- WPF教程
- WPF-主页
- WPF - 概述
- WPF - 环境设置
- WPF - 你好世界
- WPF - XAML 概述
- WPF - 元素树
- WPF - 依赖属性
- WPF - 路由事件
- WPF - 控件
- WPF - 布局
- WPF - 布局嵌套
- WPF - 输入
- WPF-命令行
- WPF - 数据绑定
- WPF - 资源
- WPF - 模板
- WPF - 样式
- WPF - 触发器
- WPF-调试
- WPF - 自定义控件
- WPF - 异常处理
- WPF - 本地化
- WPF-交互
- WPF - 2D 图形
- WPF - 3D 图形
- WPF-多媒体
- WPF 有用资源
- WPF - 快速指南
- WPF - 有用的资源
- WPF - 讨论
WPF - 快速指南
WPF - 概述
WPF 代表 Windows 演示基础。它是用于构建 Windows 应用程序的强大框架。本教程介绍了构建 WPF 应用程序所需了解的功能以及它如何为 Windows 应用程序带来根本性的变化。
WPF 首次在 .NET Framework 3.0 版本中引入,然后在后续的 .NET Framework 版本中添加了许多其他功能。
WPF架构
在 WPF 之前,Microsoft 提供的其他用户界面框架(例如 MFC 和 Windows 窗体)只是 User32 和 GDI32 DLL 的包装器,但 WPF 仅很少使用 User32。所以,
- WPF 不仅仅是一个包装器。
- 它是.NET 框架的一部分。
- 它包含托管和非托管代码的混合。
WPF架构的主要组件如下图所示。WPF 最重要的代码部分是 -
- 演示框架
- 演示核心
- 米尔科
表示框架和表示核心是用托管代码编写的。Milcore是非托管代码的一部分,允许与 DirectX(负责显示和渲染)紧密集成。CLR通过提供内存管理、错误处理等许多功能使开发过程更加高效。
WPF – 优点
在早期的 GUI 框架中,应用程序的外观和Behave之间没有真正的分离。GUI 和Behave都是用相同的语言创建的,例如C# 或VB.Net,这需要开发人员付出更多的努力来实现与之相关的UI 和Behave。
在 WPF 中,UI 元素是用 XAML 设计的,而Behave可以用过程语言(例如 C# 和 VB.Net)实现。因此很容易将Behave与设计器代码分开。
通过 XAML,程序员可以与设计人员并行工作。GUI 及其Behave之间的分离使我们能够通过使用样式和模板轻松更改控件的外观。
WPF – 功能
WPF 是一个用于创建 Windows 应用程序的强大框架。它支持许多出色的功能,其中一些功能如下 -
特征 | 描述 |
---|---|
控件内的控件 | 允许将另一个控件内部的控件定义为内容。 |
数据绑定 | 在用户界面上的 UI 元素和数据对象之间显示数据并与之交互的机制。 |
媒体服务 | 提供一个集成系统,用于使用图像、音频和视频等常见媒体元素构建用户界面。 |
模板 | 在 WPF 中,您可以直接使用模板定义元素的外观 |
动画 | 在用户界面上构建交互性和移动性 |
替代输入 | 支持Windows 7及以上版本的多点触控输入。 |
直接3D | 允许显示更复杂的图形和自定义主题 |
WPF - 环境设置
Microsoft 为 WPF 应用程序开发提供了两个重要的工具。
- 视觉工作室
- 表达混合
这两个工具都可以创建WPF项目,但事实是Visual Studio更多地被开发人员使用,而Blend更多地被设计人员使用。在本教程中,我们将主要使用 Visual Studio。
安装
Microsoft 提供了 Visual Studio 的免费版本,可以从VisualStudio下载。
下载文件并按照以下步骤在您的系统上设置 WPF 应用程序开发环境。
下载完成后,运行安装程序。将显示以下对话框。
单击“安装”按钮,它将开始安装过程。
安装过程成功完成后,您将看到以下对话框。
如果需要,关闭此对话框并重新启动计算机。
现在从“开始”菜单打开 Visual Studio,这将打开以下对话框。
- 一切完成后,您将看到 Visual Studio 的主窗口。
您现在已准备好构建您的第一个 WPF 应用程序。
WPF - 你好世界
在本章中,我们将开发一个简单的 Hello World WPF 应用程序。因此,让我们按照下面给出的步骤开始简单的实现。
- 单击“文件”>“新建”>“项目”菜单选项。
- 将显示以下对话框。
在“模板”下,选择“Visual C#”,然后在中间面板中选择“WPF 应用程序”。
为项目命名。在名称字段中输入HelloWorld,然后单击“确定”按钮。
默认情况下会创建两个文件,一个是XAML文件(mainwindow.xaml),另一个是CS文件(mainwindow.cs)
在 mainwindow.xaml 上,您将看到两个子窗口,一个是设计窗口,另一个是源(XAML)窗口。
在 WPF 应用程序中,有两种方法可以为应用程序设计 UI。一种方法是简单地将 UI 元素从工具箱拖放到设计窗口。第二种方法是通过为 UI 元素编写 XAML 标记来设计 UI。当使用拖放功能进行 UI 设计时,Visual Studio 会处理 XAML 标记。
在 mainwindow.xaml 文件中,默认写入以下 XAML 标记。
<Window x:Class = "HelloWorld.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title = "MainWindow" Height = "350" Width = "604"> <Grid> </Grid> </Window>
- 默认情况下,网格被设置为页面后的第一个元素。
- 让我们转到工具箱并将 TextBlock 拖到设计窗口中。
- 您将在设计窗口中看到 TextBlock。
当您查看源窗口时,您将看到 Visual Studio 已为您生成了 TextBlock 的 XAML 代码。
让我们将 XAML 代码中 TextBlock 的 Text 属性从 TextBlock 更改为 Hello World。
<Window x:Class = "HelloWorld.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title = "MainWindow" Height = "350" Width = "604"> <Grid> <TextBlock x:Name = "textBlock" HorizontalAlignment = "Left" Margin = "235,143,0,0" TextWrapping = "Wrap" Text = "Hello World!" VerticalAlignment = "Top" Height = "44" Width = "102" /> </Grid> </Window>
- 现在,您还将在设计窗口上看到更改。
编译并执行上述代码后,您将看到以下窗口。
恭喜!您已经设计并创建了您的第一个 WPF 应用程序。
WPF - XAML 概述
使用 WPF 时首先遇到的事情之一就是 XAML。XAML 代表可扩展应用程序标记语言。它是一种基于 XML 的简单声明性语言。
在XAML中,很容易创建、初始化和设置具有层次关系的对象的属性。
它主要用于设计 GUI,但也可用于其他目的,例如在 Workflow Foundation 中声明工作流。
基本语法
当您创建新的 WPF 项目时,您将在 MainWindow.xaml 中默认遇到一些 XAML 代码,如下所示。
<Window x:Class = "Resources.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title = "MainWindow" Height = "350" Width = "525"> <Grid> </Grid> </Window>
上面的 XAML 文件包含不同类型的信息。下表简要说明了各个信息的作用。
信息 | 描述 |
---|---|
<窗口 | 它是根的开放对象元素或容器。 |
x:Class = "资源.MainWindow" | 它是一个分部类声明,它将标记连接到后面定义的分部类代码。 |
xmlns = “http://schemas.microsoft.com/win fx/2006/xaml/presentation” | 映射 WPF 客户端/框架的默认 XAML 命名空间 |
xmlns:x = “http://schemas.microsoft.com/w infx/2006/xaml” | XAML 语言的 XAML 命名空间,将其映射到 x: 前缀 |
> | 根的对象元素结束 |
<网格> </网格> |
它是空网格对象的开始和结束标签。 |
</窗口> | 关闭对象元素 |
XAML 的语法规则几乎与 XML 类似。如果您查看 XAML 文档,您会发现它实际上是一个有效的 XML 文件,但 XML 文件不一定是 XAML 文件。这是因为在 XML 中,属性的值必须是字符串,而在 XAML 中,它可以是不同的对象,这称为属性元素语法。
Object 元素的语法以左尖括号(<) 开头,后跟对象的名称,例如Button。
定义该对象元素的一些属性和属性。
对象元素必须以正斜杠 (/) 结束,紧接着是直尖括号 (>)。
没有子元素的简单对象示例
<Button/>
具有某些属性的对象元素示例
<Button Content = "Click Me" Height = "30" Width = "60" />
定义属性的替代语法示例(属性元素语法)
<Button> <Button.Content>Click Me</Button.Content> <Button.Height>30</Button.Height> <Button.Width>60</Button.Width> </Button>
具有子元素的对象示例:StackPanel 包含 Textblock 作为子元素
<StackPanel Orientation = "Horizontal"> <TextBlock Text = "Hello"/> </StackPanel>
为什么在 WPF 中使用 XAML
XAML 不仅是 WPF 最广为人知的功能,而且也是最容易被误解的功能之一。如果你接触过WPF,那么你一定听说过XAML;但请注意以下关于 XAML 的两个鲜为人知的事实 -
- WPF 不需要 XAML
- XAML 不需要 WPF
它们实际上是可分离的技术。为了了解这是怎么回事,让我们看一个简单的示例,其中使用 XAML 中的一些属性创建按钮。
<Window x:Class = "WPFXAMLOverview.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title = "MainWindow" Height = "350" Width = "604"> <StackPanel> <Button x:Name = "button" Content = "Click Me" HorizontalAlignment = "Left" Margin = "150" VerticalAlignment = "Top" Width = "75" /> </StackPanel> </Window>
如果您选择不在 WPF 中使用 XAML,那么您也可以使用过程语言实现相同的 GUI 结果。让我们看一下同一个示例,但这一次,我们将用 C# 创建一个按钮。
using System.Windows; using System.Windows.Controls; namespace WPFXAMLOverview { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // Create the StackPanel StackPanel stackPanel = new StackPanel(); this.Content = stackPanel; // Create the Button Button button = new Button(); button.Content = "Click Me"; button.HorizontalAlignment = HorizontalAlignment.Left; button.Margin = new Thickness(150); button.VerticalAlignment = VerticalAlignment.Top; button.Width = 75; stackPanel.Children.Add(button); } } }
当您编译并执行 XAML 代码或 C# 代码时,您将看到如下所示的相同输出。
从上面的示例可以清楚地看出,您可以在 XAML 中创建、初始化和设置对象属性,也可以使用代码完成相同的任务。
XAML 只是另一种简单易用的 UI 元素设计方法。
使用 XAML,并不意味着设计 UI 元素是唯一的方法。您可以在 XAML 中声明对象或使用代码定义它们。
XAML 是可选的,但尽管如此,它仍然是 WPF 设计的核心。
XAML 的目标是使视觉设计人员能够直接创建用户界面元素。
WPF 旨在使通过标记控制用户界面的所有视觉方面成为可能。
WPF - 元素树
有许多技术将元素和组件按树结构排序,以便程序员可以轻松处理对象并更改应用程序的Behave。Windows Presentation Foundation (WPF) 具有对象形式的综合树结构。在 WPF 中,有两种方法可以概念化完整的对象树 -
- 逻辑树结构
- 视觉树结构
借助这些树结构,您可以轻松创建和识别 UI 元素之间的关系。大多数情况下,WPF 开发人员和设计人员要么使用过程语言创建应用程序,要么在 XAML 中设计应用程序的 UI 部分,同时牢记对象树结构。
逻辑树结构
在 WPF 应用程序中,XAML 中 UI 元素的结构表示逻辑树结构。在XAML中,UI的基本元素由开发人员声明。WPF 中的逻辑树定义如下 -
- 依赖属性
- 静态和动态资源
- 将元素绑定到其名称等上。
让我们看一下下面的示例,其中创建了一个按钮和一个列表框。
<Window x:Class = "WPFElementsTree.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title = "MainWindow" Height = "350" Width = "604"> <StackPanel> <Button x:Name = "button" Height = "30" Width = "70" Content = "OK" Margin = "20" /> <ListBox x:Name = "listBox" Height = "100" Width = "100" Margin = "20"> <ListBoxItem Content = "Item 1" /> <ListBoxItem Content = "Item 2" /> <ListBoxItem Content = "Item 3" /> </ListBox> </StackPanel> </Window>
如果你看一下XAML代码,你会观察到一种树形结构,即根节点是Window,根节点内部只有一个子节点,那就是StackPanel。但 StackPanel 包含两个子元素:按钮和列表框。列表框还有三个子列表框项目。
视觉树结构
在 WPF 中,视觉树的概念描述了视觉对象的结构,由视觉基类表示。它表示渲染到输出屏幕的所有 UI 元素。
当程序员想要为特定控件创建模板时,他实际上是在渲染该控件的可视化树。对于那些出于性能和优化原因想要绘制较低级别控件的人来说,可视化树也非常有用。
在 WPF 应用程序中,可视化树用于 -
- 渲染视觉对象。
- 渲染布局。
- 路由事件主要沿着视觉树而不是逻辑树传播。
要查看上面包含按钮和列表框的简单应用程序的可视化树,让我们编译并执行 XAML 代码,您将看到以下窗口。
当应用程序运行时,您可以在Live Visual Tree窗口中看到正在运行的应用程序的可视化树,其中显示了该应用程序的完整层次结构,如下所示。
视觉树通常是逻辑树的超集。您可以在此处看到所有逻辑元素也出现在可视化树中。因此,这两棵树实际上只是构成 UI 的同一组对象的两个不同视图。
逻辑树遗漏了很多细节,使您能够专注于用户界面的核心结构,而忽略其具体呈现方式的细节。
逻辑树用于创建用户界面的基本结构。
如果您专注于演示,视觉树将会很有趣。例如,如果您希望自定义任何 UI 元素的外观,则需要使用可视化树。
WPF - 依赖属性
在 WPF 应用程序中,依赖属性是扩展 CLR 属性的特定类型的属性。它利用了 WPF 属性系统中可用的特定功能。
定义依赖属性的类必须继承自DependencyObject类。XAML 中使用的许多 UI 控件类都派生自 DependencyObject类,并且它们支持依赖属性,例如 Button 类支持IsMouseOver依赖属性。
以下 XAML 代码创建一个具有一些属性的按钮。
<Window x:Class = "WPFDependencyProperty.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local = "clr-namespace:WPFDependencyProperty" Title = "MainWindow" Height = "350" Width = "604"> <Grid> <Button Height = "40" Width = "175" Margin = "10" Content = "Dependency Property"> <Button.Style> <Style TargetType = "{x:Type Button}"> <Style.Triggers> <Trigger Property = "IsMouseOver" Value = "True"> <Setter Property = "Foreground" Value = "Red" /> </Trigger> </Style.Triggers> </Style> </Button.Style> </Button> </Grid> </Window>
XAML 中的 x:Type 标记扩展具有与 C# 中的 typeof() 类似的功能。当指定采用对象类型的属性时使用它,例如 <Style TargetType = "{x:Type Button}">
当上面的代码被编译并执行时,你将得到以下MainWindow。当鼠标悬停在按钮上时,它将改变按钮的前景色。当鼠标离开按钮时,它会变回原来的颜色。
为什么我们需要依赖属性
当您在应用程序中使用依赖属性时,它会给您带来各种好处。在以下情况下,依赖属性可以在 CLR 属性上使用 -
- 如果你想设置样式
- 如果你想要数据绑定
- 如果要设置资源(静态或动态资源)
- 如果你想支持动画
基本上,依赖属性提供了许多使用 CLR 属性无法获得的功能。
依赖属性和其他CLR 属性之间的主要区别如下:
CLR 属性可以使用getter和setter直接从类的私有成员中读取/写入。相反,依赖属性不存储在本地对象中。
依赖属性存储在 DependencyObject 类提供的键/值对字典中。它还可以节省大量内存,因为它会在更改时存储属性。它也可以绑定在 XAML 中。
自定义依赖属性
在.NET框架中,还可以定义自定义依赖属性。按照下面给出的步骤在 C# 中定义自定义依赖属性。
使用系统调用寄存器声明并注册您的依赖属性。
提供属性的setter和getter 。
定义一个静态处理程序,它将处理全局发生的任何更改
定义一个实例处理程序,它将处理该特定实例发生的任何更改。
以下 C# 代码定义了一个依赖属性来设置用户控件的SetText属性。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; 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 WpfApplication3 { /// <summary> /// Interaction logic for UserControl1.xaml /// </summary> public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } public static readonly DependencyProperty SetTextProperty = DependencyProperty.Register("SetText", typeof(string), typeof(UserControl1), new PropertyMetadata("", new PropertyChangedCallback(OnSetTextChanged))); public string SetText { get { return (string)GetValue(SetTextProperty); } set { SetValue(SetTextProperty, value); } } private static void OnSetTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { UserControl1 UserControl1Control = d as UserControl1; UserControl1Control.OnSetTextChanged(e); } private void OnSetTextChanged(DependencyPropertyChangedEventArgs e) { tbTest.Text = e.NewValue.ToString(); } } }
下面是 XAML 文件,其中 TextBlock 被定义为用户控件,并且 Text 属性将由 SetText 依赖属性分配给它。
以下 XAML 代码创建一个用户控件并初始化其SetText依赖属性。
<Window x:Class = "WpfApplication3.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views = "clr-namespace:WpfApplication3" Title = "MainWindow" Height = "350" Width = "604"> <Grid> <views:UserControl1 SetText = "Hellow World"/> </Grid> </Window>
让我们运行这个应用程序。您可以立即观察到,在我们的主窗口中,用户控件的依赖属性已成功用作文本。
WPF - 路由事件
路由事件是一种事件类型,它可以调用元素树中多个侦听器上的处理程序,而不仅仅是引发事件的对象。它基本上是一个由路由事件类的实例支持的 CLR 事件。它已在 WPF 事件系统中注册。RoutedEvents 具有三种主要路由策略,如下所示 -
- 直接事件
- 冒泡事件
- 隧道事件
直接事件
直接事件类似于 Windows 窗体中的事件,由事件发起的元素引发。
与标准 CLR 事件不同,直接路由事件支持类处理,并且可以在您的自定义控件样式中的事件设置器和事件触发器中使用它们。
直接事件的一个很好的例子是 MouseEnter 事件。
冒泡事件
冒泡事件从事件发起的元素开始。然后,它沿着视觉树向上移动到视觉树中的最顶层元素。因此,在 WPF 中,最顶层的元素很可能是一个窗口。
隧道事件
调用元素树根上的事件处理程序,然后事件沿着可视树向下传播到所有子节点,直到到达事件起源的元素。
冒泡事件和隧道事件之间的区别在于隧道事件始终以预览开始。
在 WPF 应用程序中,事件通常作为隧道/冒泡对实现。因此,您将有一个 MouseDown 预览,然后是一个 MouseDown 事件。
下面给出了一个路由事件的简单示例,其中使用一些属性和事件创建了一个按钮和三个文本块。
<Window x:Class = "WPFRoutedEvents.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" Title = "MainWindow" Height = "450" Width = "604" ButtonBase.Click = "Window_Click" > <Grid> <StackPanel Margin = "20" ButtonBase.Click = "StackPanel_Click"> <StackPanel Margin = "10"> <TextBlock Name = "txt1" FontSize = "18" Margin = "5" Text = "This is a TextBlock 1" /> <TextBlock Name = "txt2" FontSize = "18" Margin = "5" Text = "This is a TextBlock 2" /> <TextBlock Name = "txt3" FontSize = "18" Margin = "5" Text = "This is a TextBlock 3" /> </StackPanel> <Button Margin = "10" Content = "Click me" Click = "Button_Click" Width = "80"/> </StackPanel> </Grid> </Window>
以下是 Button、StackPanel 和 Window 的 Click 事件实现的 C# 代码。
using System.Windows; namespace WPFRoutedEvents { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { txt1.Text = "Button is Clicked"; } private void StackPanel_Click(object sender, RoutedEventArgs e) { txt2.Text = "Click event is bubbled to Stack Panel"; } private void Window_Click(object sender, RoutedEventArgs e) { txt3.Text = "Click event is bubbled to Window"; } } }
当您编译并执行上述代码时,它将产生以下窗口 -
当您单击该按钮时,文本块将更新,如下所示。
如果您想在任何特定级别停止路由事件,则需要设置 e.Handled = true;
让我们更改StackPanel_Click事件,如下所示 -
private void StackPanel_Click(object sender, RoutedEventArgs e) { txt2.Text = "Click event is bubbled to Stack Panel"; e.Handled = true; }
当您单击按钮时,您将观察到单击事件不会被路由到窗口,而是会停在堆栈面板上,并且第三个文本块不会更新。
自定义路由事件
在.NET框架中,还可以定义自定义路由事件。您需要按照下面给出的步骤在 C# 中定义自定义路由事件。
使用系统调用 RegisterRoatedEvent 声明并注册您的路由事件。
指定路由策略,即 Bubble、Tunnel 或 Direct。
提供事件处理程序。
让我们通过一个示例来了解有关自定义路由事件的更多信息。请按照以下步骤操作 -
使用 WPFCustomRoatedEvent 创建新的 WPF 项目
右键单击您的解决方案并选择“添加”>“新项目...”
将打开以下对话框,现在选择自定义控件 (WPF)并将其命名为MyCustomControl。
单击“添加”按钮,您将看到两个新文件(Themes/Generic.xaml 和 MyCustomControl.cs)将添加到您的解决方案中。
以下 XAML 代码在 Generic.xaml 文件中设置自定义控件的样式。
<ResourceDictionary xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local = "clr-namespace:WPFCustomRoutedEvent"> <Style TargetType = "{x:Type local:MyCustomControl}"> <Setter Property = "Margin" Value = "50"/> <Setter Property = "Template"> <Setter.Value> <ControlTemplate TargetType = "{x:Type local:MyCustomControl}"> <Border Background = "{TemplateBinding Background}" BorderBrush = "{TemplateBinding BorderBrush}" BorderThickness = "{TemplateBinding BorderThickness}"> <Button x:Name = "PART_Button" Content = "Click Me" /> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
下面给出的是MyCustomControl 类的 C# 代码,该类继承自Control 类,在该类中为自定义控件创建了自定义路由事件 Click。
using System.Windows; using System.Windows.Controls; namespace WPFCustomRoutedEvent { public class MyCustomControl : Control { static MyCustomControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); //demo purpose only, check for previous instances and remove the handler first var button = GetTemplateChild("PART_Button") as Button; if (button ! = null) button.Click + = Button_Click; } void Button_Click(object sender, RoutedEventArgs e) { RaiseClickEvent(); } public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyCustomControl)); public event RoutedEventHandler Click { add { AddHandler(ClickEvent, value); } remove { RemoveHandler(ClickEvent, value); } } protected virtual void RaiseClickEvent() { RoutedEventArgs args = new RoutedEventArgs(MyCustomControl.ClickEvent); RaiseEvent(args); } } }
下面是 C# 中的自定义路由事件实现,当用户单击它时,它将显示一个消息框。
using System.Windows; namespace WPFCustomRoutedEvent { // <summary> // Interaction logic for MainWindow.xaml // </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void MyCustomControl_Click(object sender, RoutedEventArgs e) { MessageBox.Show("It is the custom routed event of your custom control"); } } }
以下是 MainWindow.xaml 中的实现,用于添加带有路由事件 Click 的自定义控件。
<Window x:Class = "WPFCustomRoutedEvent.MainWindow" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local = "clr-namespace:WPFCustomRoutedEvent" Title = "MainWindow" Height = "350" Width = "604"> <Grid> <local:MyCustomControl Click = "MyCustomControl_Click" /> </Grid> </Window>
当上面的代码被编译并执行时,它将产生以下包含自定义控件的窗口。
当您单击自定义控件时,它将产生以下消息。
WPF - 控件
Windows Presentation Foundation (WPF) 允许开发人员轻松构建和创建基于视觉效果丰富的 UI 的应用程序。
其他 UI 框架中的经典 UI 元素或控件也在 WPF 应用程序中得到增强。
所有标准 WPF 控件都可以在工具箱中找到,它是 System.Windows.Controls 的一部分。
这些控件也可以用 XAML 标记语言创建。
WPF 控件的完整继承层次结构如下 -
下表包含我们将在后续章节中讨论的控件列表。
先生。没有。 | 控制和说明 |
---|---|
1 | 按钮
响应用户输入的控件 |
2 | 日历
表示一个控件,使用户能够使用可视日历显示来选择日期。 |
3 | 复选框
用户可以选择或清除的控件。 |
4 | 组合框
用户可以从中选择项目的下拉列表。 |
5 | 上下文菜单
获取或设置每当通过用户界面 (UI) 从此元素内请求上下文菜单时应显示的上下文菜单元素。 |
6 | 数据网格
表示在可自定义网格中显示数据的控件。 |
7 | 日期选择器
允许用户选择日期的控件。 |
8 | 对话框
应用程序还可以显示附加窗口来帮助用户收集或显示重要信息。 |
9 | 网格视图
一种按行和列显示项目集合的控件,可以水平滚动。 |
10 | 图像
呈现图像的控件。 |
11 | 标签
在表单上显示文本。提供对访问键的支持。 |
12 | 列表框
一个控件,显示用户可以从中选择的内联项目列表。 |
13 | 菜单
表示 Windows 菜单控件,使您能够分层组织与命令和事件处理程序关联的元素。 |
14 | 密码盒
用于输入密码的控件。 |
15 | 弹出窗口
在应用程序窗口范围内的现有内容之上显示内容。 |
16 | 进度条
通过显示条来指示进度的控件。 |
17 号 | 单选按钮
允许用户从一组选项中选择单个选项的控件。 |
18 | 滚动查看器
允许用户平移和缩放其内容的容器控件。 |
19 | 滑块
一种控件,允许用户通过沿轨道移动 Thumb 控件来从一系列值中进行选择。 |
20 | 文本块
显示文本的控件。 |
21 | 切换按钮
可以在两种状态之间切换的按钮。 |
22 | 工具提示
显示元素信息的弹出窗口。 |
23 | 窗户
根窗口提供最小化/最大化选项、标题栏、边框和关闭按钮 |
24 | 第三方控制
在 WPF 应用程序中使用第三方控件。 |
我们将一一讨论所有这些控制及其实施。
WPF - 布局
控件的布局对于应用程序的可用性非常重要且至关重要。它用于在应用程序中排列一组 GUI 元素。选择布局面板时需要考虑一些重要事项 -
- 子元素的位置
- 子元素的大小
- 将重叠的子元素分层
当应用程序在不同的屏幕分辨率上运行时,控件的固定像素排列不起作用。XAML 提供了一组丰富的内置布局面板,可以以适当的方式排列 GUI 元素。一些最常用和流行的布局面板如下 -
先生。没有。 | 面板和说明 |
---|---|
1 | 堆栈面板
Stack面板是XAML中的一个简单且有用的布局面板。在堆栈面板中,子元素可以根据方向属性水平或垂直排列在一行中。 |
2 | 包裹面板
在WrapPanel 中,子元素根据orientation 属性按从左到右或从上到下的顺序排列。 |
3 | 坞站面板
DockPanel 定义一个区域来相对于彼此排列子元素,无论是水平还是垂直。通过 DockPanel,您可以使用Dock属性轻松地将子元素停靠到顶部、底部、右侧、左侧和中心。 |
4 | 画布面板
Canvas 面板是基本布局面板,其中可以使用相对于 Canvas任何一侧(例如左、右、上、下)的坐标显式定位子元素。 |
5 | 网格面板
网格面板提供了由行和列组成的灵活区域。在网格中,子元素可以以表格形式排列。 |
WPF - 布局嵌套
布局嵌套意味着在另一个布局内使用布局面板,例如在网格内定义堆栈面板。这个概念被广泛用于利用应用程序中的多种布局的优势。在下面的示例中,我们将在网格内使用堆栈面板。
让我们看一下下面的 XAML 代码。
<Window x:Class = "WPFNestingLayouts.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:WPFNestingLayouts" mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "604"> <Grid Background = "AntiqueWhite"> <Grid.RowDefinitions> <RowDefinition Height = "*" /> <RowDefinition Height = "*" /> <RowDefinition Height = "*" /> <RowDefinition Height = "*" /> <RowDefinition Height = "*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width = "*" /> </Grid.ColumnDefinitions> <Label Content = "Employee Info" FontSize = "15" FontWeight = "Bold" Grid.Column = "0" Grid.Row = "0"/> <StackPanel Grid.Column = "0" Grid.Row = "1" Orientation = "Horizontal"> <Label Content = "Name" VerticalAlignment = "Center" Width = "70"/> <TextBox Name = "txtName" Text = "Muhammad Ali" VerticalAlignment = "Center" Width = "200"> </TextBox> </StackPanel> <StackPanel Grid.Column = "0" Grid.Row = "2" Orientation = "Horizontal"> <Label Content = "ID" VerticalAlignment = "Center" Width = "70"/> <TextBox Name = "txtCity" Text = "421" VerticalAlignment = "Center" Width = "50"> </TextBox> </StackPanel> <StackPanel Grid.Column = "0" Grid.Row = "3" Orientation = "Horizontal"> <Label Content = "Age" VerticalAlignment = "Center" Width = "70"/> <TextBox Name = "txtState" Text = "32" VerticalAlignment = "Center" Width = "50"></TextBox> </StackPanel> <StackPanel Grid.Column = "0" Grid.Row = "4" Orientation = "Horizontal"> <Label Content = "Title" VerticalAlignment = "Center" Width = "70"/> <TextBox Name = "txtCountry" Text = "Programmer" VerticalAlignment = "Center" Width = "200"></TextBox> </StackPanel> </Grid> </Window>
当您编译并执行上述代码时,将产生以下窗口。
我们建议您执行上面的示例代码并尝试其他嵌套布局。
WPF - 输入
Windows Presentation Foundation (WPF) 提供了强大的 API,应用程序可以借助该 API 从各种设备(例如鼠标、键盘和触摸板)获取输入。在本章中,我们将讨论可以在 WPF 应用程序中处理的以下输入类型 -
先生。没有。 | 输入和描述 |
---|---|
1 | 老鼠
鼠标输入有不同类型,例如 MouseDown、MouseEnter、MouseLeave 等。 |
2 | 键盘
键盘输入有多种类型,例如 KeyDown、KeyUp、TextInput 等。 |
3 | 上下文菜单或路由命令
RoutedCommands 支持在更语义级别的输入处理。这些实际上是简单的指令,如新建、打开、复制、剪切和保存。 |
4 | 多点触摸
Windows 7 及其更高版本能够接收来自多个触摸感应设备的输入。WPF 应用程序还可以通过在发生触摸时引发事件来将触摸输入作为其他输入(例如鼠标或键盘)进行处理。 |
WPF-命令行
命令行参数是一种机制,用户可以在执行 WPF 应用程序时将一组参数或值传递给该应用程序。这些参数对于从外部控制应用程序非常重要,例如,如果您想从命令提示符打开Word文档,那么您可以使用此命令“ C:\> start winword word1.docx ”,它将打开word1 .docx文档。
命令行参数在启动函数中处理。以下是一个简单的示例,演示如何将命令行参数传递给 WPF 应用程序。让我们创建一个名为WPFCommandLine的新 WPF 应用程序。
将一个文本框从工具箱拖到设计窗口。
在此示例中,我们将 txt 文件路径作为命令行参数传递给我们的应用程序。
程序将读取txt文件,然后将所有文本写入文本框。
以下 XAML 代码创建一个文本框并使用一些属性对其进行初始化。
<Window x:Class = "WPFCommandLine.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:WPFCommandLine" mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "525"> <Grid> <TextBox x:Name = "textBox" HorizontalAlignment = "Left" Height = "180" Margin = "100" TextWrapping = "Wrap" VerticalAlignment = "Top" Width = "300"/> </Grid> </Window>
- 现在订阅 App.xaml 文件中的 Startup 事件,如下所示。
<Application x:Class = "WPFCommandLine.App" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local = "clr-namespace:WPFCommandLine" StartupUri = "MainWindow.xaml" Startup = "app_Startup"> <Application.Resources> </Application.Resources> </Application>
下面给出了 App.xaml.cs 中 app_Startup 事件的实现,它将获取命令行参数。
using System.Windows; namespace WPFCommandLine { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { public static string[] Args; void app_Startup(object sender, StartupEventArgs e) { // If no command line arguments were provided, don't process them if (e.Args.Length == 0) return; if (e.Args.Length > 0) { Args = e.Args; } } } }
现在,在 MainWindow 类中,程序将打开 txt 文件并将所有文本写入文本框。
如果发现错误,程序将在文本框中显示错误消息。
using System; using System.IO; using System.Windows; namespace WPFCommandLine { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); String[] args = App.Args; try { // Open the text file using a stream reader. using (StreamReader sr = new StreamReader(args[0])) { // Read the stream to a string, and write // the string to the text box String line = sr.ReadToEnd(); textBox.AppendText(line.ToString()); textBox.AppendText("\n"); } } catch (Exception e) { textBox.AppendText("The file could not be read:"); textBox.AppendText("\n"); textBox.AppendText(e.Message); } } } }
当上面的代码被编译并执行时,它将产生一个带有文本框的空白窗口,因为该程序需要命令行参数。因此,Visual Studio 提供了一种使用命令行参数执行应用程序的简单方法。
在解决方案资源管理器中右键单击您的 WPF 项目并选择属性,它将显示以下窗口。
选择“调试”选项并在命令行参数中写入文件路径。
使用 Test.txt 创建一个 txt 文件,并在该文件中写入一些文本并将其保存在任意位置。在本例中,txt 文件保存在“ D:\ ”硬盘上。
保存项目中的更改并立即编译并执行您的应用程序。您将在 TextBox 中看到程序从 Text.txt 文件中读取的文本。
现在让我们尝试将计算机上的文件名从Test.txt更改为Test1.txt并再次执行您的程序,然后您将在文本框中看到该错误消息。
我们建议您执行上述代码并按照所有步骤成功执行您的应用程序。
WPF - 数据绑定
数据绑定是 WPF 应用程序中的一种机制,它为 Windows 运行时应用程序提供一种简单易用的方法来显示数据并与数据交互。在这种机制下,数据的管理与数据的传输方式完全分离。
数据绑定允许用户界面上的 UI 元素和数据对象之间的数据流。当建立绑定并且数据或业务模型发生变化时,它会自动将更新反映到 UI 元素,反之亦然。也可以不绑定到标准数据源,而是绑定到页面上的另一个元素。
数据绑定有两种类型:单向数据绑定和双向数据绑定。
单向数据绑定
在单向绑定中,数据从其源(即保存数据的对象)绑定到其目标(即显示数据的对象)
让我们通过一个简单的例子来详细了解单向数据绑定。首先,创建一个名为WPFDataBinding的新 WPF 项目。
以下 XAML 代码创建两个标签、两个文本框和一个按钮,并使用一些属性对它们进行初始化。
<Window x:Class = "WPFDataBinding.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:WPFDataBinding" mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "604"> <Grid> <Grid.RowDefinitions> <RowDefinition Height = "Auto" /> <RowDefinition Height = "Auto" /> <RowDefinition Height = "*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width = "Auto" /> <ColumnDefinition Width = "200" /> </Grid.ColumnDefinitions> <Label Name = "nameLabel" Margin = "2">_Name:</Label> <TextBox Name = "nameText" Grid.Column = "1" Margin = "2" Text = "{Binding Name, Mode = OneWay}"/> <Label Name = "ageLabel" Margin = "2" Grid.Row = "1">_Age:</Label> <TextBox Name = "ageText" Grid.Column = "1" Grid.Row = "1" Margin = "2" Text = "{Binding Age, Mode = OneWay}"/> <StackPanel Grid.Row = "2" Grid.ColumnSpan = "2"> <Button Content = "_Show..." Click="Button_Click" /> </StackPanel> </Grid> </Window>
两个文本框的文本属性都绑定到“Name”和“Age”,它们是 Person 类的类变量,如下所示。
在 Person 类中,我们只有两个变量Name和Age ,其对象在MainWindow类中初始化。
在 XAML 代码中,我们绑定到属性 Name 和 Age,但我们尚未选择该属性属于哪个对象。
更简单的方法是将一个对象分配给DataContext,我们在MainWindowconstructor 的以下 C# 代码中绑定该对象的属性。
using System.Windows; namespace WPFDataBinding { public partial class MainWindow : Window { Person person = new Person { Name = "Salman", Age = 26 }; public MainWindow() { InitializeComponent(); this.DataContext = person; } private void Button_Click(object sender, RoutedEventArgs e) { string message = person.Name + " is " + person.Age; MessageBox.Show(message); } } public class Person { private string nameValue; public string Name { get { return nameValue; } set { nameValue = value; } } private double ageValue; public double Age { get { return ageValue; } set { if (value != ageValue) { ageValue = value; } } } } }
让我们运行这个应用程序,您可以立即在主窗口中看到我们已成功绑定到该 Person 对象的名称和年龄。
当您按下“显示”按钮时,它将在消息框中显示姓名和年龄。
让我们在对话框中更改名称和年龄。
如果您现在单击“显示”按钮,它将再次显示相同的消息。
这是因为数据绑定模式在 XAML 代码中设置为单向。要显示更新的数据,您需要了解双向数据绑定。
双向数据绑定
在双向绑定中,用户可以通过用户界面修改数据并在源中更新该数据。如果在用户查看视图时源发生变化,您希望更新视图。
让我们采用相同的示例,但在这里,我们将在 XAML 代码中将绑定模式从“单向”更改为“双向”。
<Window x:Class = "WPFDataBinding.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:WPFDataBinding" mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "604"> <Grid> <Grid.RowDefinitions> <RowDefinition Height = "Auto" /> <RowDefinition Height = "Auto" /> <RowDefinition Height = "*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width = "Auto" /> <ColumnDefinition Width = "200" /> </Grid.ColumnDefinitions> <Label Name = "nameLabel" Margin = "2">_Name:</Label> <TextBox Name = "nameText" Grid.Column = "1" Margin = "2" Text = "{Binding Name, Mode = TwoWay}"/> <Label Name = "ageLabel" Margin = "2" Grid.Row = "1">_Age:</Label> <TextBox Name = "ageText" Grid.Column = "1" Grid.Row = "1" Margin = "2" Text = "{Binding Age, Mode = TwoWay}"/> <StackPanel Grid.Row = "2" Grid.ColumnSpan = "2"> <Button Content = "_Show..." Click = "Button_Click" /> </StackPanel> </Grid> </Window>
让我们再次运行这个应用程序。
它将产生相同的输出 -
现在让我们更改名称和年龄值 -
如果您现在单击“显示”按钮,它将显示更新的消息。
我们建议您在这两种情况下执行上述代码,以便更好地理解这个概念。
WPF - 资源
资源通常是与某些您希望多次使用的对象相关的定义。它能够在本地存储控件或当前窗口的数据,或者为整个应用程序全局存储数据。
将对象定义为资源允许我们从另一个地方访问它。这意味着该对象可以重复使用。资源在资源字典中定义,任何对象都可以定义为资源,有效地使其成为可共享的资产。为 XAML 资源指定唯一键,并且可以使用该键通过使用 StaticResource 标记扩展来引用该资源。
资源可以有两种类型 -
- 静态资源
- 动态资源
StaticResource 是一次性查找,而 DynamicResource 的工作方式更像是数据绑定。它会记住一个属性与特定的资源键相关联。如果与该键关联的对象发生更改,动态资源将更新目标属性。
例子
这是 SolidColorBrush 资源的简单应用程序。
让我们创建一个名为WPFResouces的新 WPF 项目。
拖动两个矩形并设置其属性,如以下 XAML 代码所示。
<Window x:Class = "WPFResources.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:WPFResources" mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "525"> <Window.Resources> <SolidColorBrush x:Key = "brushResource" Color = "Blue" /> </Window.Resources> <StackPanel> <Rectangle Height = "50" Margin = "20" Fill = "{StaticResource brushResource}" /> <Rectangle Height = "50" Margin = "20" Fill = "{DynamicResource brushResource}" /> <Button x:Name = "changeResourceButton" Content = "_Change Resource" Click = "changeResourceButton_Click" /> </StackPanel> </Window>
在上面的XAML代码中,您可以看到一个矩形具有StaticResource,另一个矩形具有DynamicResource,并且brushResource的颜色为Bisque。
当您编译并执行代码时,它将产生以下主窗口。
当您单击“更改资源”按钮时,您将看到带有 DynamicResource 的矩形将其颜色更改为红色。
资源范围
资源是在资源字典中定义的,但是有很多地方可以定义资源字典。在上面的示例中,资源字典是在窗口/页面级别定义的。在哪个字典中定义资源会立即限制该资源的范围。因此,范围(即您可以在哪里使用资源)取决于您定义它的位置。
在网格的资源字典中定义资源,并且该资源可由该网格及其子元素访问