与君共勉
使用到的工具
C#
Visual Studio 2019
生成WPF应用程序
1. 创建项目
启动 VS 2019 :创建新项目 —> 语言选择C#,平台选择 windows,项目类型选择 桌面—>WPF应用(.NET Framework) —>项目名称输入 BellRingers —>安装位置随便选一个—>点击确定 随后会创建新项目,其中包括一个空白窗体,名为MainWindow
如果没有WPF应用(.NET Framework) 此项,可以选择vs界面的 工具–获取工具和功能–.NET桌面开发下载安装就行了
检查窗体和网格布局
- 检查“设计”视图下方的XAML窗格。其中包含了窗体XAML的定义:
<Window x:Class="BellRingers.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BellRingers"
mc:Ignorable="d"
Title="MainWindow" Height="470" Width="600">
<Grid>
</Grid>
</Window>
Class属性指定了负责实现窗体的那个类的完全限定名称。在本例中为MainWindow,位于BellRingers命名空间中。其它的暂时不管。
除了设计窗口,XAML窗口 还有 解决方案资源管理器和属性窗口(都可以在视图中找到)。将这四个窗口全部打开。属性窗口顾名思义可以更改选中控件的属性。
- 更改窗体标题栏中显示的文本
可以直接在XAML代码 Title=“MainWindow” Height=“470” Width=“600”> 中更改Title的值
或者在设计视图中单击MainWindow窗体,在属性窗口找到Title属性,输入Middleshire Bell Ringers Association Members,从而改变窗体标题栏中显示的文本。 - Grid 面板非常灵活,但也能变得很复杂,咱们把它想象成一个单元格,可以把控件放入这个单元格中,在本例中只会使用一个单元格。
- 先拖一个按钮到窗体中
单击VS左边工具箱 找到button控件 点击拖到窗体中
检查XMAL 窗格中的代码发现多了一行
<Button x:Name="clear" Content="Clear" Height="23" HorizontalAlignment="Left" Margin="311,388,0,0" VerticalAlignment="Top" Width="75" />
HorizontalAlignment和VerticalAlignment为对齐属性 可以指定边和窗体某边对齐,删除的话默认与窗体中心对齐。 Margin="311,388,0,0"指定和窗口边的距离 Height和Width指定控件的长宽。
可以自己更改值运行(调试–>开始执行(不调试))拖动窗体边线 试试效果。
为窗体添加背景图片
注:添加背景图有很多方式本例只是相对简单的一种方式
- 在工具箱中找到 Image 控件,拖到窗体中
- 在XAML窗格中设置 Image 的属性,改成这样
<Image x:Name="image" Margin="0,0,0,0" />
图像控件将占据整个网格
- 在解决方案资源管理器中右击BellRingers项目–>添加–>现有项 随便找副图添加都行
- 在Image控件中添加图片来源
<Image x:Name="image" Margin="0,0,0,0" Source="bell.gif" /> //我的图片名称:bell.gif
这时图片应该已经在窗体上显示了,但是把其它控件(原先的按钮)遮住了
之所以会这样是因为除非特意指定,否则放在一个布局面板(例如 Grid)上的所有控件默认“Z序”(z-order)是后添加的组件位于先添加的组件的顶部。
“Z序”就是三维空间中的 XYZ 轴的Z轴 Z值高的压着Z值小的
有两个方法改变改变Z值 一是在XAML窗格中把 Image的定义放在其它控件的前面,二是显式地为控件的ZIndex属性指定一个值在同一个布局中ZIndex值小的在ZIndex值大的下面
- 指定 ZIndex属性值
<Image x:Name="image" Margin="0,0,0,0" Panel.ZIndex="0" Source="bell.gif" />
其它控件也添加Panel.ZIndex属性并把值写成1就行
创建样式来管理控件在窗体上的“外观与感觉”
- 在XAML 窗格中更改按钮的定义Button.Resources是一个复合属性,必须修改Button的定义来包装这个属性。
假如一个控件的XAML定义包含了复合的子属性值,一个良好的编程习惯是将其划分成多行来写,使代码更容易维护和阅读。
<Button Panel.ZIndex="1" Content="Button" Margin="379,84,49,0" Name="button" Height="23" verticalAlignment="Top" >
<Button.Resources>
<style x:Key= "buttonstyle">
<Setter Property="Button.Background" Value="Gray"/>
<Setter Property="Button.Foreground" Value="White"/>
<Setter Property="Button.FontFamily" Value="Comic Sans MS"/>
</Style>
</Button.Resources>
</Button>
这个例子指定按钮的背景色和前景色,以及按钮所使用的字体。样式是一种资源 所以要添加到控件的Resources元素中。使用Key属性,可以为样式指定唯一的名称。不过虽然定义了一个样式,但按钮样式还是没有改变。要使用Style属性将样式用于一个控件。
- 修改按钮定义,让它应用button样式,
<Button Panel.ZIndex="1" Style="{DynamicResource buttonStyle}" Content="Button" Margin="379,84,49,0" Name="button" Height="23" verticalAlignment="Top" >
<Button.Resources>
<style x:Key= "buttonstyle">
<Setter Property="Button.Background" Value="Gray"/>
<Setter Property="Button.Foreground" Value="White"/>
<Setter Property="Button.FontFamily" Value="Comic Sans MS"/>
</Style>
</Button.Resources>
</Button>
现在按钮在窗体上的外观应该发生变化,样式有自己的作用域,如果你想第二个按钮也使用这种样式只能在第二个按钮中同样的写<Button.Resources>。很明显 这个方法很笨,有个更好的方法是将样式定义成静态资源,这样就可以在窗口的所有控件中应用它。
- 在XAML窗格中,在网格(Grid)上方添加一个<Window.Rexources>元素,将buttonStyle样式的定义转移至这个新元素,删掉<Button.Resources>。
<Window x:Class="BellRingers.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BellRingers"
mc:Ignorable="d"
Title="MainWindow" Height="470" Width="600">
<Window.Rexources>
<style x:Key= "buttonstyle">
<Setter Property="Button.Background" Value="Gray"/>
<Setter Property="Button.Foreground" Value="White"/>
<Setter Property="Button.FontFamily" Value="Comic Sans MS"/>
</Style>
</Window.Rexources>
<Grid>
<Button Panel.ZIndex="1" Style="{DynamicResource buttonStyle}" Content="Button" Margin="379,84,49,0" Name="button" Height="23" verticalAlignment="Top" >
</Button>
</Grid>
</Window>
现在 这个静态buttonstyle样式就可以给多个按钮控件使用了。
可能有人注意到了我说的是静态buttonstyle样式 但是我使用的时候是 DynamicResource 动态资源引用,其实这里你也可以使用StaticResource 静态资源引用 但是我在属性窗口找到 style 选择本地资源 再选择buttonstyle 自动生成的代码是Style="{DynamicResource buttonStyle}" 所以我也就跟着这样使用了
- 但是这样代码还是不够简洁,于是可以修改样式定义来指定TargetType属性
<style x:Key= "buttonstyle" TargetType="Button">
<Setter Property="Background" Value="Gray"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontFamily" Value="Comic Sans MS"/>
</Style>
但是仅仅这样,如果我想把这个样式用于其他控件怎么办呢?
- 咱们在工具箱中找到TextBox 文本框控件,拉它到窗体中,如果想之前一样使用 Style="{DynamicResource buttonStyle}" 不用想肯定是会失败。
- 把TargetType值改为Control ,Key值改为 bellRingersStyle(换一个名字)然后修改按钮和文本框对样式的应用
<Window x:Class="BellRingers.MainWindow"
...
<Window.Rexources>
<style x:Key= "bellRingersStyle" TargetType="Control">
<Setter Property="Background" Value="Gray"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontFamily" Value="Comic Sans MS"/>
</Style>
</Window.Rexources>
<Grid>
<Button ... Style="{DynamicResource bellRingersStyle}" ...>
</Button>
<TextBox ... Style="{DynamicResource bellRingersStyle}" .../>
</Grid>
</Window>
样式的TargetType值为Control意味着该样式可以用于从Control类继承的所有控件,在WPF模型中许多控件都是继承于Control 。但是 控件即使继承至Control但还是有一些附加属性,这些属性不是Control的一部分,如果指定了任何控件的专有属性,就不能将TargetType值设为Control。
这个时候可以选择运行程序试试结果是否正确。
- 编辑bellRingersStyle样式,在其中添加<Style.Triggers>触发器元素
<style x:Key= "bellRingersStyle" TargetType="Control">
<Setter Property="Background" Value="Gray"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
触发器指定了一个属性改变时要采取的行动,bellRingersStyle样式检测到IsMouseOver属性中的一个变化,从而临时修改鼠标所在的控件背景色。
使用WPF控件
示例程序需求
我们需要TextBox输入会员名以及姓氏,每个会员都应有一个特定的钟楼(Tower)钟楼是有限且固定的,可以使用ComboBox组合框。窗体还应记录会员是不是钟楼的主管,可以使用CheckBox要么是要么不是。应用还应收集会员入会时间,以及会员资历(即工作了多少年)。DateTimePicker控件可以选择和显示时间。最后,应用程序应该记录会员敲钟时允许的音调(每个会员敲不同的音调就可以组成一首曲子),为了显示这种信息并且指出会员会不会一种敲钟方法,可以使用包含一组CheckBox控件的一个ListBox。最后加两个按钮用来保存和清空输入。
将控件添加到窗口
- 先添加一个style只改变字体
<Style x:Key="bellRingersFontStyle" TargetType="Control">
<Setter Property="FontFamily" Value="Comic Sans Ms"/>
</Style>
- 添加控件以及应用style后代码如下,读者可自行添加并更改相应的属性,这里就不再过多声明
<Window x:Class="BellRingers.MainWindow"
...
<Window.Resources>
<Style x:Key="bellRingersFontStyle" TargetType="Control">
<Setter Property="FontFamily" Value="Comic Sans Ms"/>
</Style>
<Style x:Key="bellRingersStyle" TargetType="Control">
<Setter Property="Background" Value="Gray"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Image x:Name="image" Margin="0,0,0,0" Panel.ZIndex="0" Source="bell.gif" />
<Label x:Name="label" Content="First Name" HorizontalAlignment="Left" Margin="29,25,0,0" VerticalAlignment="Top" Height="28" Width="75" Style="{DynamicResource bellRingersFontStyle}"/>
<TextBox x:Name="firstName" HorizontalAlignment="Left" Height="23" Margin="121,25,0,0" TextWrapping="Wrap" Text="first name" VerticalAlignment="Top" Width="175" Style="{DynamicResource bellRingersStyle}"/>
<Label x:Name="label1" Content="Last Name" HorizontalAlignment="Left" Margin="305,25,0,0" VerticalAlignment="Top" Height="28" Width="75" Style="{DynamicResource bellRingersFontStyle}"/>
<TextBox x:Name="lastName" HorizontalAlignment="Left" Height="23" Margin="380,25,0,0" TextWrapping="Wrap" Text="last name" VerticalAlignment="Top" Width="175" Style="{DynamicResource bellRingersStyle}"/>
<Label x:Name="label2" Content="Tower" HorizontalAlignment="Left" Margin="29,72,0,0" VerticalAlignment="Top" Height="28" Width="75" Style="{DynamicResource bellRingersFontStyle}"/>
<ComboBox x:Name="towerNames" HorizontalAlignment="Left" Margin="121,72,0,0" VerticalAlignment="Top" Width="275" Height="23" Style="{DynamicResource bellRingersFontStyle}"/>
<CheckBox x:Name="isCaptain" Content="captain" HorizontalAlignment="Left" Margin="420,72,0,0" VerticalAlignment="Top" Width="75" Height="23" Style="{DynamicResource bellRingersFontStyle}"/>
<Label x:Name="label3" Content="Member Since" HorizontalAlignment="Left" Margin="29,134,0,0" VerticalAlignment="Top" Width="90" Height="28" Style="{DynamicResource bellRingersFontStyle}"/>
<DatePicker HorizontalAlignment="Left" Margin="121,134,0,0" VerticalAlignment="Top" Width="275" AutomationProperties.Name="memberSince" Height="23"/>
<GroupBox x:Name="yearsExperience" Header="Experience" HorizontalAlignment="Left" Height="200" Margin="29,174,0,0" VerticalAlignment="Top" Width="258" Style="{DynamicResource bellRingersFontStyle}">
<StackPanel Margin="0,0,0,0" Name="stackPanel1">
<RadioButton x:Name="novice" Content="Up to 1 year" HorizontalAlignment="Left" Margin="0,10,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="intermediate" Content="1 to 4 years" HorizontalAlignment="Left" Margin="0,20,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="experienced" Content="5 to 9 years" HorizontalAlignment="Left" Margin="0,20,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="accomplished" Content="10 or more years" HorizontalAlignment="Left" Margin="0,20,0,0" VerticalAlignment="Top"/>
</StackPanel>
</GroupBox>
<ListBox x:Name="methods" HorizontalAlignment="Left" Height="200" Margin="310,174,0,0" VerticalAlignment="Top" Width="245" Style="{DynamicResource bellRingersFontStyle}"/>
<Button x:Name="add" Content="Add" Height="23" HorizontalAlignment="Left" Margin="188,388,0,0" VerticalAlignment="Top" Width="75" Style="{DynamicResource bellRingersStyle}"/>
<Button x:Name="clear" Content="Clear" Height="23" HorizontalAlignment="Left" Margin="311,388,0,0" VerticalAlignment="Top" Width="75" Style="{DynamicResource bellRingersStyle}"/>
</Grid>
</Window>
- 完成后效果如图:
动态更改属性
前面已经使用“设计”视图,“属性”窗口和XAML窗格来进行属性的静态设置。窗体运行时有必要有初始默认值,单击clear键时也应该初始化。为此需要自己写一些代码了。
创建Reset方法
- 双击Main Window.xaml.cs文件,随后会打开“代码和文本编辑器”窗口。
- 在Main Window类中添加Reset方法:
public partial class MainWindow : Window
{
...
public void Reset()
{
firstName.Text = String.Empty;
lastName.Text = String.Empty;
}
...
}
这个方法给firstName 和lastName 两个文本框Text初始值 (为空)(其实不用这个也是空的)。
- 给钟楼列表添加字符串数组towers
public partial class MainWindow : Window
{
...
private string[] towers = { "Great Shevington", "Little Mudford", "Upper Gumtree", "Downley Hatch" };
public void Reset()
{
firstName.Text = String.Empty;
lastName.Text = String.Empty;
towerNames.Items.Clear();//先清空
foreach(string towerName in towers)
{
towerNames.Items.Add(towerName);
}
towerNames.Text = towerNames.Items[0] as string;//把第一个字符串作为默认值
}
...
}
- 初始化敲钟方法
public partial class MainWindow : Window
{
...
private string[] ringingMethods = { "Plain Bob", "Reverse Canterbury", "Grandsire", "Stedman","Kent Treble Bob",
"Old Oxford Delight","Winchendon place", "Norwich Surprise", "Crayford Little Court" };
public void Reset()
{
...
methods.Items.Clear();
CheckBox method = null;
foreach(string menthodName in ringingMethods)
{
method = new CheckBox();
method.Margin = new Thickness(0, 0, 0, 10);
method.Content = menthodName;
methods.Items.Add(method);
}
}
...
}
- 初始化IsChecked复选框 应设置值为FALSE
...
public void Reset()
{
...
isCaptain.IsChecked = false;
...
}
...
- 会员资历把第一项作为初始值
...
public void Reset()
{
...
novice.IsChecked = true;
...
}
...
- 初始化入会日期(今天)
...
public void Reset()
{
...
memberSince.Text = DateTime.Today.ToString();
...
}
...
- 在窗体首次显示时调用Reset方法
public partial class MainWindow : Window
{
...
public MainWindow()
{
InitializeComponent();
this.Reset();
}
...
}
- 这个时候可以运行试试了,看看初始值是否是我们想要的结果:
处理WPF窗体中事件
处理Windows 窗体中的事件
值选定之后,我们应该可以Clear清除输入,以及ADD添加会员信息(这个要连数据库,本例不做描述,所以仅仅展示数据)
- 处理Clear按钮的Click事件
在设计视图中双击Clear按钮,会跳转到“代码和文本编辑器”窗口,并自动创建一个名为clear_Click的事件方法,会在用户单击Clear按钮时调用。可以发现clear按钮控件多了一个Click属性
<Button x:Name="clear" ... Click="Clear_Click"/>
事件方法要获取两个参数:sender参数(一个object)和一个e参数(一个RoutedEventArgs对象)。WPF“运行时”(RunTime)会使用与事件来源有关的信息填充sender,并用处理事件时可能有用的其他附加信息填充e。本例不会使用这些参数。
我们希望当用户点击Clear时窗体能重置为它的默认值。
private void Clear_Click(object sender, RoutedEventArgs e)
{
this.Reset();
}
其实Clear_Click就是一个方法名,你可以随意取,但是控件的Click属性值也应该做出相应的更改,同时也可以在其它按钮中引用这个事件方法,只需要加上Click=“方法名”
- add按钮应该提交用户数据然后处理数据(这里用一个对话框展示数据)
...
private void add_Click(object sender, RoutedEventArgs e)
{
string nameAndTower = String.Format("Member name:{0} {1} form the tower at {2} rings the following methods:",
firstName.Text, lastName.Text, towerNames.Text);
//"Member name:" + firstName.Text + lastName.Text+" form the tower at " + towerNames.Text+" rings the following methods:";
StringBuilder details = new StringBuilder();
details.AppendLine(nameAndTower);
foreach (CheckBox cb in methods.Items)
{
if (cb.IsChecked.Value)
{
details.AppendLine(cb.Content.ToString());
}
}
MessageBox.Show(details.ToString(), "Member Information");
}
...
String.Format和StringBuilder可以更高效组合字符串,不用每次新建字符串对象
处理窗体的Closing事件
就是当用户点击关闭窗体的时候,弹出一个对话框,询问是否确定关闭,或者执行其它语句
- 在XAML窗格中将Closing="Window_Closing"代码输入MainWindow窗口的描述中
<Window x:Class="BellRingers.MainWindow"
...
Title="Middleshire Bell Ringers Association Members" Height="470" Width="600" Closing="Window_Closing">
...
最好是先输入closing=然后选择 “新建事件处理程序”这样会直接生成一个Window_Closing 的事件方法,并把它同窗体的Closing事件关联起来。
这个时候应该已经在MainWIndow类中添加好了一个空白的Window_Closing事件方法。
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
}
这个方法的第二个参数具有CancelEventArgs 类型,将CancelEventArgs 类有一个名为Cancel的Boolean属性。如果Cancel为TRUE,窗体就不会关闭,否则窗体关闭。
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
MessageBoxResult key = MessageBox.Show( //弹出对话框,返回用户单击的哪个按钮
"Are you sure you want to quit", "Confirm",
MessageBoxButton.YesNo,
MessageBoxImage.Question
// MessageBoxResult.No
);
e.Cancel = (key == MessageBoxResult.No ); //单击的是No就取消事件
}
这个时候点击关闭窗口就会弹出对话框,点击确定才会关闭窗体,否则窗体继续运行
运行程序可以看看是否达到理想效果。
参考文献:visual C# 2010 从入门到精通, 参考网址:.