一个简单的WPF应用程序

与君共勉

使用到的工具
C#
Visual Studio 2019

生成WPF应用程序

1. 创建项目

启动 VS 2019 :创建新项目 —> 语言选择C#,平台选择 windows,项目类型选择 桌面—>WPF应用(.NET Framework) —>项目名称输入 BellRingers —>安装位置随便选一个—>点击确定 随后会创建新项目,其中包括一个空白窗体,名为MainWindow

如果没有WPF应用(.NET Framework) 此项,可以选择vs界面的 工具–获取工具和功能–.NET桌面开发下载安装就行了

检查窗体和网格布局

  1. 检查“设计”视图下方的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窗口 还有 解决方案资源管理器和属性窗口(都可以在视图中找到)。将这四个窗口全部打开。属性窗口顾名思义可以更改选中控件的属性。

  1. 更改窗体标题栏中显示的文本
    可以直接在XAML代码 Title=“MainWindow” Height=“470” Width=“600”> 中更改Title的值
    或者在设计视图中单击MainWindow窗体,在属性窗口找到Title属性,输入Middleshire Bell Ringers Association Members,从而改变窗体标题栏中显示的文本。
  2. Grid 面板非常灵活,但也能变得很复杂,咱们把它想象成一个单元格,可以把控件放入这个单元格中,在本例中只会使用一个单元格。
  3. 先拖一个按钮到窗体中
    单击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指定控件的长宽。
可以自己更改值运行(调试–>开始执行(不调试))拖动窗体边线 试试效果。

为窗体添加背景图片

注:添加背景图有很多方式本例只是相对简单的一种方式

  1. 在工具箱中找到 Image 控件,拖到窗体中
  2. 在XAML窗格中设置 Image 的属性,改成这样
<Image x:Name="image" Margin="0,0,0,0" />

图像控件将占据整个网格

  1. 在解决方案资源管理器中右击BellRingers项目–>添加–>现有项 随便找副图添加都行
  2. 在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值大的下面

  1. 指定 ZIndex属性值
 <Image x:Name="image" Margin="0,0,0,0" Panel.ZIndex="0" Source="bell.gif" />

其它控件也添加Panel.ZIndex属性并把值写成1就行

创建样式来管理控件在窗体上的“外观与感觉”

  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属性将样式用于一个控件。

  1. 修改按钮定义,让它应用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>。很明显 这个方法很笨,有个更好的方法是将样式定义成静态资源,这样就可以在窗口的所有控件中应用它。

  1. 在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}" 所以我也就跟着这样使用了

  1. 但是这样代码还是不够简洁,于是可以修改样式定义来指定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>

但是仅仅这样,如果我想把这个样式用于其他控件怎么办呢?

  1. 咱们在工具箱中找到TextBox 文本框控件,拉它到窗体中,如果想之前一样使用 Style="{DynamicResource buttonStyle}" 不用想肯定是会失败。
  2. 把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。
这个时候可以选择运行程序试试结果是否正确。

  1. 编辑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。最后加两个按钮用来保存和清空输入。

将控件添加到窗口

  1. 先添加一个style只改变字体
  <Style x:Key="bellRingersFontStyle" TargetType="Control">
     <Setter Property="FontFamily" Value="Comic Sans Ms"/>
  </Style>
  1. 添加控件以及应用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>

  1. 完成后效果如图:
    效果图

动态更改属性

前面已经使用“设计”视图,“属性”窗口和XAML窗格来进行属性的静态设置。窗体运行时有必要有初始默认值,单击clear键时也应该初始化。为此需要自己写一些代码了。

创建Reset方法

  1. 双击Main Window.xaml.cs文件,随后会打开“代码和文本编辑器”窗口。
  2. 在Main Window类中添加Reset方法:
public partial class MainWindow : Window
{
    ...
    public void Reset()
    {
        firstName.Text = String.Empty;
        lastName.Text = String.Empty;
	}
	...
}

这个方法给firstName 和lastName 两个文本框Text初始值 (为空)(其实不用这个也是空的)。

  1. 给钟楼列表添加字符串数组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;//把第一个字符串作为默认值	 
    }
    ...
}
  1. 初始化敲钟方法
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);
		}
	}
	...
}
  1. 初始化IsChecked复选框 应设置值为FALSE
...
    public void Reset()
    {
       ...
       isCaptain.IsChecked = false;
       ...
	}
...
  1. 会员资历把第一项作为初始值
...
    public void Reset()
    {
       ...
       novice.IsChecked = true;
       ...
	}
...
  1. 初始化入会日期(今天)
...
    public void Reset()
    {
       ...
       memberSince.Text = DateTime.Today.ToString();
       ...
	}
...
  1. 在窗体首次显示时调用Reset方法
public partial class MainWindow : Window
{
...
    public MainWindow()
    {
       InitializeComponent();
       this.Reset();
    }
	...
}
  1. 这个时候可以运行试试了,看看初始值是否是我们想要的结果:
    运行结果

处理WPF窗体中事件

处理Windows 窗体中的事件

值选定之后,我们应该可以Clear清除输入,以及ADD添加会员信息(这个要连数据库,本例不做描述,所以仅仅展示数据)

  1. 处理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=“方法名”

  1. 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事件

就是当用户点击关闭窗体的时候,弹出一个对话框,询问是否确定关闭,或者执行其它语句

  1. 在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 从入门到精通, 参考网址:.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值