开发准备
WPF编程是微软推出的打破GUI编程旧模式的创新技术。全称为WINDOWS PRESENTATION FOUNDATION。开发工具建议使用Visual Studio 2008 Team System。语言建议使用C#。WPF可以更方便的开发更漂亮的界面,并且可以比以前更好地将GUI设计和程序逻辑分离开来,使得有条件的公司可以专门培养平面设计人员进行GUI设计(有点类似于与网页美工),而程序员更加关注业务逻辑。
一个简单的示例开始
创建一个WPF工程,工程名为Demo。
请注意图中灰色区域,那是一个XAML文件,如果不用VS2008图形化设计工具,光写XAML文件,我们也可以设计出GUI界面来。理解XAML的一些基本知识很重要,但是还是推荐使用图形化设计工具来工作,毕竟谁都想生活轻松点。
这个工程新建好了以后,就创建了一个窗口应用程序。现在只有一个窗口而已,窗口标题为默认的Window1。下面是运行的结果:
理解XAML
XAML全称为Extensible Application Markup Language 。XAML可以用来编写WPF,但是也可以做很多其他的事情。每个WPF程序都有一个Application对象,该对象代表了整个应用程序,它总是有一个static Main方法。如何编写一个Application类有多种方法,可以参考 http://www.cnblogs.com/kuku/archive/2007/02/09/645623.html 。但是这篇文章写得较早,最新的VS2008有些变化。所以下面我说明一下,App.xaml内容如下:xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
Application元素的x:Class属性的值为Demo名字空间里面的类App。该类的定义如下:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace Demo
{
///
/// Interaction logic for App.xaml
///
public partial class App : Application
{
}
}
我们并不需要写static Main方法,估计编译器替我们生成了。 App.xaml中的Application元素还有一个属性StartupUri指定了应用程序开始运行时显示的第一个窗口Window1。Window1.xaml文件内容如下:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
这个应该比较容易懂,除了指定Demo.Window1为窗口类以外,还指定了Title,Height,Width属性的值。Window1类的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Demo
{
///
/// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
Window1继承了System.Windows.Window类。InitializeComponent()方法的主要作用是读取Window1.xaml文件中指定的Window元素的属性值。代码如下:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/Demo;component/window1.xaml", System.UriKind.Relative);
#line 1 "....Window1.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
添加两个控件,并理解事件处理机制
现在我打算添加一个文本控件,然后再添加一个按钮,当按钮点击后,文本控件的内容将被修改。通过Toolbox拖拽TextBox和Button到Window1窗口上。现在的Window1.xaml文件内容变成:xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
Grid控件用来精确的定位控件的位置。现在Grid包含了两个子控件。Button控件显示的文本Press被
现在来为Press按钮增加一个单击事件,在xaml文件中增加一个事件。这里已经不再采用传统的事件选择的方式,而是在xaml文件中直接通过提示选择。VS2008同时将会创建一个成员函数button1_Click,代码如下:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
}
}
熟悉.Net WinForm开发的人应该很能猜到这里仍然使用委托机制来实现事件回调。sender就代表事件源,这里其实就是Window1对象自己。RoutedEvnetArgs e可以让我们获得事件的相关信息。我们添加一句
System.Windows.MessageBox.Show("You click me"),这样点击按钮后就会弹出一个消息框。
到这里,一个麻雀虽小,但是五脏俱全的例子程序就结束了。使用xml编写界面的方式的确比以前用代码来编写直观,并且降低了门槛,让具备美术功底的设计人员可以参与进来。但是由于界面直接和软件功能有关,所以要做到界面设计和代码编写完全分离还不可能。
后面我会陆续总结一些编写优美界面的知识。
BitmapEffect特效
这篇文章介绍得很详细,请参考:http://www.cnblogs.com/kuku/archive/2007/02/11/647533.html
无标题栏的圆角窗口
通常特殊形状的窗口都要去掉标题栏,所以Window对象的属性WindowStyle要设置为"None",将背景设为透明,并且允许透明。代码如下:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" WindowStyle="None" Background="Transparent" AllowsTransparency="True">
我们可以通过增加一个Border元素,设置四个转角的角度,代码如下:
没有了标题栏,我们还要允许拖拽窗口,因此需要处理鼠标左键的单击事件。代码如下:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" WindowStyle="None" Background="Transparent" AllowsTransparency="True" MouseLeftButtonDown="Window_MouseLeftButtonDown">
Background="AliceBlue" >
注意,Border的背景色不能为透明,否则无法拖拽。
Window_MouseLeftButtonDown函数的实现非常简单。请看:xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" WindowStyle="None" Background="Transparent" AllowsTransparency="True">
我们可以通过增加一个Border元素,设置四个转角的角度,代码如下:
没有了标题栏,我们还要允许拖拽窗口,因此需要处理鼠标左键的单击事件。代码如下:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" WindowStyle="None" Background="Transparent" AllowsTransparency="True" MouseLeftButtonDown="Window_MouseLeftButtonDown">
namespace Demo
{
///
/// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
System.Windows.MessageBox.Show("You click me");
}
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
}
}
所以整个效果看上去应该这样:
线性递增画刷
该画刷可以绘制渐变的效果,真得很方便。比如下面的代码:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" WindowStyle="None" Background="Transparent" AllowsTransparency="True" MouseLeftButtonDown="Window_MouseLeftButtonDown" Name="Resources">
这种xml嵌套使用的方式似乎通过vs2008的可视化编辑工具做不出来,还是需要手动编辑xaml文件。
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" WindowStyle="None" Background="Transparent" AllowsTransparency="True" MouseLeftButtonDown="Window_MouseLeftButtonDown" Name="Resources">
这种xml嵌套使用的方式似乎通过vs2008的可视化编辑工具做不出来,还是需要手动编辑xaml文件。
Content 模式
凡是从ContentControl类派生的控件都能够容纳别的子控件。比如下面的例子来自于<>。
界面如下:
界面如下:
你也可以这么使用,这种情况更适合嵌入对象。
Grid布局控件
有些控件,提供了很方便的布局功能,比如Grid,它使用表格的方式放置它的子控件。下面的示例,同样来自于<>::
使用Grid.RowDefinitions子元素定义了三行,使用Grid.ColumnDefinitions定义了三列。
Grid.ColumnSpan="2 "指定了A按钮占用两列空间。
ListView控件
下面的代码定义了三个Header,名称分别为Name,Email和Address。
DisplayMemberBinding="{Binding Path=FirstName}" 指的是该列的要显示的内容和后面ItemSource所指的对象的属性FirstName绑定。
效果如下:
DisplayMemberBinding="{Binding Path=FirstName}" 指的是该列的要显示的内容和后面ItemSource所指的对象的属性FirstName绑定。
效果如下:
通常数据都是从数据源(比如数据库)获得,然后通过代码插入到ListView控件中,因此下面演示如何通过代码插入数据:
ListView有一个属性IEnumerable ItemsSource。ItemsSource代表ListView里面的Item的集合。我们应该为ItemsSource赋予一个数据集合对象,该集合对象的类型是继承自ObservableCollection<>,ObservableCollection最终也是继承自IEnumerable接口。如果ListView的每个Item(代表一行纪录)对应一个Person类。Person代码如下:(注意这里的属性名称和前面xaml文件里面的绑定名称相同)
public class Person
{
public Person(String name, String email, String address)
{
this.name = name;
this.email = email;
this.address = address;
}
private String name;
private String email;
private String address;
public String Name
{
get
{
return name;
}
set
{
name = value;
}
}
public String Email
{
get
{
return email;
}
set
{
email = value;
}
}
public String Address
{
get
{
return address;
}
set
{
address = value;
}
}
}
Item的集合可以容纳多个Item。我设计了Persons类代表Item的集合,代码如下:
public class Persons : ObservableCollection
{
public Persons()
{
Add(new Person("chenshu","[email protected]","101"));
Add(new Person("lijing", "[email protected]", "102"));
}
}
Persons的构造函数将创建两个Person对象作为两个Item。然后往ListView里面填写数据的代码就很简单了:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
list.ItemsSource = new Persons();
}
..........
}
还有一种更加简单的做法。利用ListView.Items属性的Add方法将Person对象逐个加入。比如下面的代码:
list.Items.Add(new Person("chenshu", "[email protected]", "d"));
ListView有一个属性IEnumerable ItemsSource。ItemsSource代表ListView里面的Item的集合。我们应该为ItemsSource赋予一个数据集合对象,该集合对象的类型是继承自ObservableCollection<>,ObservableCollection最终也是继承自IEnumerable接口。如果ListView的每个Item(代表一行纪录)对应一个Person类。Person代码如下:(注意这里的属性名称和前面xaml文件里面的绑定名称相同)
public class Person
{
public Person(String name, String email, String address)
{
this.name = name;
this.email = email;
this.address = address;
}
private String name;
private String email;
private String address;
public String Name
{
get
{
return name;
}
set
{
name = value;
}
}
public String Email
{
get
{
return email;
}
set
{
email = value;
}
}
public String Address
{
get
{
return address;
}
set
{
address = value;
}
}
}
Item的集合可以容纳多个Item。我设计了Persons类代表Item的集合,代码如下:
public class Persons : ObservableCollection
{
public Persons()
{
Add(new Person("chenshu","[email protected]","101"));
Add(new Person("lijing", "[email protected]", "102"));
}
}
Persons的构造函数将创建两个Person对象作为两个Item。然后往ListView里面填写数据的代码就很简单了:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
list.ItemsSource = new Persons();
}
..........
}
还有一种更加简单的做法。利用ListView.Items属性的Add方法将Person对象逐个加入。比如下面的代码:
list.Items.Add(new Person("chenshu", "[email protected]", "d"));