(九)WPF入门
1.window对象
在window元素中,
-
xmlns是 xml-namespace 的缩写,是用来定义名称空间的。xaml的命名空间与c#代码的命名空间的作用类似。只有声明了恰当的命名空间,指定了ui控件的来源,才能在wpf中使用控件。比如说现在我们看到的就是wpf系统默认的命名空间,我们可以使用这个命名空间下的所有ui控件,button和text block。另外,如果当来源不同的类重名时,我们也可以通过使用空间的名称来加以区分。
-
xmlns后可以跟一个可选的映射前缀,之间用冒号分割。
- xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation” 是默认名称空间。这种不带映射前缀的名称空间只能有一个,一般选用元素最频繁使用的名称空间。< Grid >等常见元素都来自于这个名称空间。
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"这个名称空间与解析XAML语言相关。x:Class 向为 XAML 页提供代码隐藏的类指定 CLR 命名空间和类名。必须具有这样一个类才能支持每个 WPF 编程模型的代码隐藏, 本项目此特性将映射到代码定义的类型HelloWord.MainWindow类。
- xmlns:d=“http://schemas.microsoft.com/expression/blend/2008”
- xmlns:mc=“http://schemas.openxmlformats.org/markup-compatibility/2006”
-
xmlns:local=“clr-namespace:HelloWord” 表示当前应用的命名空间
• Title=“MainWindow” Height=“450” Width=“800” 就比较容易理解了,他们分别定义了视窗的标题、高度、宽度。 -
最后,这个 mc:Ignorable=“d” 是做什么的呢?这个命名空间有点费解,他的名称叫Ignorable,中文意思就是“可忽略的”。表示的是运行过程中可以被忽略的控件,而这些空间都将会以 “d:”d+冒号的形式出现在设计界面中。
2.WPF内置UI控件分类
2.1 cs代码
MainWindow.xaml 对应的MainWindow.xaml.cs 的文件,这是c#文件,实际上就是ui设计文档背后的页面逻辑代码。
cs文件打开以后,我们可以看到这个class 的名称就是MainWindow,对应的就是我们的主界面。这是一个partial class,而这个MainWindow继承与Window基类。
而这个类的构造方法怎会在当前页面被打开的时候或者程序启动的时候调用,而方法InitializeComponent会帮助我们完成页面的初始化工作,比如初始化页面的标题、大小等相关属性。
2.1 通过c#添加UI控件
三种添加控件方式
- 直接拖拽
- xaml编写
- c#代码编写
首先,在添加按钮之前,我们需要首先获得ui的添加位置,也就是页面的第二个元素,grid。其实这个grid就是window页面的内容,所以,当我们访问this.Content的时候,访问的就正是grid元素。不过,为了能够正常使用grid元素的功能,我们需要对content做一下类型转换。
var grid = (Grid)this.Content;
Button button = new Button();
button.Height = 50;
button.Width = 100;
button.Margin = new Thickness(0, 0, 700, 385);
button.Content = "HELLO alex";
grid.Children.Add(button);
3.逻辑树与视觉树
3.1 逻辑树
拓展资料:
http://www.wpftutorial.net/logicalandvisualtree.html
实际上,WPF 的 UI 元素是有一种从上到下、逐层递进的关系的,而这种UI元素之间的关系是一个倒字塔型,从数据结构上来说属于树形结构,所以,我们把WPF的ui元素之间的关系称为逻辑树。
而一个ui元素是由多个视觉元素组成,比如说我们现在看到的这个hello world TextBlock,他四周有四条看不到的边界线、而他内部有内容展示、显示文字的地方,而这些能被用户看到的所有的元素也同样是以树的结构呈现出来的,这就是视觉树 VisualTree。

3.2 WPF为什么需要两种树?
每个 WPF UI 控件都是由多个更加基础的可视化控件组成的。 比如说,按钮由边框、矩形和内容展示器组成。当 WPF 呈现按钮时,元素本身没有外观,但它会遍历可视化树并呈现它的可视化子项。 这种层次关系还可以用于进行命中测试、布局等。
逻辑树描述了用户界面元素之间的关系。 逻辑树负责:
- 继承 DependencyProperty 值
- 解析 DynamicResources 引用
- 查找绑定的元素名称
- 转发路由事件
视觉树包含所有逻辑元素,包括每个元素的模板的所有可视化元素。 视觉树负责:
- 渲染视觉元素
- 渲染控件布局
- 进行命中测试
- 寻找父级组件
逻辑树始终存在于WPF的UI中,不管UI是用XAML编写还是用代码编写。WPF的每个方面(属性、事件、资源等等)都是依赖于逻辑树的。
而视觉树则是逻辑树的一种扩展。逻辑树的每个结点都可以分解为它们的核心视觉组件。逻辑树的结点对我们而言基本是一个黑盒。而视觉树不同,它暴露了视觉的实现细节。
4. 依赖属性与数据处理
在button元素中,通过元素嵌套的方式,我们来定义按钮的样式,同时设置按钮样式的触发事件,我们的计划是当用户的鼠标进入按钮区域的时候,按钮就会触发改变文字的大小和颜色。
<Button
Width="50"
Height="50"
Margin="10"
Content="clickme">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="FontSize" Value="25" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
我们需要直接使用Trigger元素来触发事件,并且通过属性 Property等于"IsMouseOver",来监听按钮区域的鼠标动作,最后给trigger加上Value等于true,表示启用这个trigger。
在xmal代码中,到处都是属性,宽度width是属性,content是属性,所有这些属性都依赖于我们在button styles中所定义的property,也就是说这些ui上的属性与事件触发的属性是绑定在一起的,从数据流动的角度来说,这就是数据绑定的最基本逻辑。
button继承ButtonBase,在这里,我们可以看到他有三个的基础属性,readonly DependcyProperty,分别是判断默认属性、判断属性取消、以及判断判是否回到默认属性。同时,对应着这三个依赖属性,他们还有三个正常属性。

在程序运行的时候这三个正常属性与依赖属性相连接,而这三组属性则可以控制ui控件的动态变化,比如样式数据的绑定、动画效果、甚至是样式的继承。
而ButtonBase则继承于ContentControl, 在它内部,除了有一个可以处理事件的routedEvent属性以外,其他属性都是依赖属性。
让我们在深入一点,进入ContentControl,再进入control,我们同样也能看到这里定义有类似的依赖属性。

在control中,我们可以看到刚刚我们使用过的字体大小FontSizeProperty,背景BackgroundProperty,前景ForegroundProperty,就定义在这里。
所以,对于WPF 的ui控件来说,依赖属性是最基础的数据和事件传递机制。
5.INotifyPropertyChanged
public class Sum : INotifyPropertyChanged
{
//PropertyChangedEventHandler方法就是一个非常典型的事件处理委托。
public event PropertyChangedEventHandler? PropertyChanged;
//事件触发处理
private void OnPropertyChanged(string property)
{
if (PropertyChanged == null)
{
return;
}
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
//声明三个私有变量
private string _num1;
private string _num2;
private string _result;
// 然后,给这三个私有变量添加属性。
public string Num1
{
get { return _num1; }
set
{
int number;
bool result = int.TryParse(value, out number);
if (result)
{
_num1 = value;
OnPropertyChanged("Num1");
OnPropertyChanged("Result");
}
}
}
public string Num2
{
get { return _num2; }
set
{
int number;
bool result = int.TryParse(value, out number);
if (result)
{
_num2 = value;
OnPropertyChanged("Num2");
OnPropertyChanged("Result");
}
}
}
public string Result
{
get
{
int result = int.Parse(_num1) + int.Parse(_num2);
return result.ToString();
}
set
{
int result = int.Parse(_num1) + int.Parse(_num2);
_result = result.ToString();
OnPropertyChanged("Result");
}
}
}
前台页面
<Label Content="number 1" />
<TextBox
Width="200"
Margin="30"
Text="{Binding Path=Num1, Mode=TwoWay}" />
<Label Content="number 2" />
<TextBox
Width="200"
Margin="30"
Text="{Binding Path=Num2, Mode=TwoWay}" />
<Label Content="number 3" />
<TextBox
Width="200"
Margin="30"
Text="{Binding Path=Result, Mode=TwoWay}" />
入口
- 定义加法计算器
- 在构建方法中出初始化这个加法计算器,同时,我们也定义number1和number2两个参数。
- 向MainWindow 的数据上下文DataContext 绑定加法计算器。
this.DataContext = Sum;
public partial class MainWindow : Window
{
public Sum Sum { get; set; }
public MainWindow()
{
InitializeComponent();
Sum = new Sum() { Num1 = "1", Num2 = "2" };
this.DataContext = Sum;
}
}
程序运行起来,我们会惊奇的发现,数据和ui绑定起来了,1+2等于3。而当我们修改number 1 或 number 2 的数据的以后,按下tab键,计算结果,result 中的数据也会发生相应的变化。


浙公网安备 33010602011771号