Button btn = new Button();
btn.FontWeight = FontWeights.Bold;
WrapPanel pnl = new WrapPanel();
TextBlock txt = new TextBlock();
txt.Text = "Multi";
txt.Foreground = Brushes.Blue;
pnl.Children.Add(txt);
txt = new TextBlock();
txt.Text = "Color";
txt.Foreground = Brushes.Red;
pnl.Children.Add(txt);
txt = new TextBlock();
txt.Text = "Button";
pnl.Children.Add(txt);
btn.Content = pnl;
pnlMain.Children.Add(btn);
从上述内容中我们需要注意以下两个鲜为人知的事实:
WPF 不需要 XAML
XAML 不需要 WPF
它们是可分离的技术,很明显在 XAML 中创建、初始化和设置对象的属性的任务也可以使用代码完成。
XAML 只是另一种设计 UI 元素的简单方法。
使用 XAML,这并不意味着您可以设计 UI 元素是唯一的方法。您可以在 XAML 中声明对象或使用代码定义它们。
XAML 是可选的,但尽管如此,它是 WPF 设计的核心。
XAML 的目标是使可视化设计人员能够直接创建用户界面元素。
WPF 旨在使从标记控制用户界面的所有视觉方面成为可能。
什么是元素树
元素树是一种树形数据结构,通常用于表示文档对象模型(DOM)中的 HTML 或 XML 文档。在元素树中,每个 HTML 或 XML 元素都表示为一个节点,而元素之间的嵌套关系表示为树的父子关系。
在元素树中,根节点代表整个文档,而子节点代表文档中的每个元素。每个节点都包含了元素的标签名称、属性和文本内容等信息。通过遍历元素树,可以轻松地访问和修改文档的内容和结构。
WPF – 元素树
在 WPF 中,元素树是一种关键的概念,它代表了界面中所有元素的层次结构。
在 WPF 中,所有的可视元素(如控件、面板、布局等)都继承自FrameworkElement
或FrameworkContentElement
类,这些类都是 WPF 中元素树的一部分。元素树中的每个元素都可以包含子元素,这些子元素可以是其他可视元素,也可以是非可视元素(如绑定对象、转换器等)。
WPF 中的元素树与 HTML 或 XML 文档的元素树类似,都是一种树形数据结构,用于表示一个文档或应用程序中的元素之间的层次结构关系。通过遍历 WPF 中的元素树,我们可以轻松地访问和修改界面中的元素,实现复杂的布局、数据绑定和事件处理等功能,为开发人员提供了一种方便、灵活的方式来构建和管理复杂的用户界面。
在 WPF 中,有两种方法可以概念化完整的对象树 –
逻辑树结构 ( XAML 部分)
可视化树结构 ( 渲染到输出屏幕的所有 UI 元素 )
程序运行时,可以在 Live Visual Tree 窗口中看到正在运行的应用程序的可视化树,该窗口显示了该应用程序的完整层次结构。
WPF – 依赖属性
在WPF框架中,依赖属性是一种特殊类型的属性,是扩展 CLR 属性的特定类型的属性,它允许元素继承和共享属性值,并支持数据绑定、样式、动画和值转换等高级功能。
依赖属性的一个重要特点是它们具有优先级顺序,可以根据优先级顺序从多个来源(例如本地值、样式、模板、继承值、动画等)中获取最终值。这使得元素可以根据自己的需求来重写或继承属性的值,从而实现更灵活、可扩展的界面设计。
另一个重要的特点是依赖属性支持数据绑定,这意味着它们可以与其他元素或数据源进行双向或单向绑定,使得界面与数据之间的交互更加自然和动态。
在 WPF 中,许多常见的属性( 如Width、Height、Background等 )都是依赖属性。开发人员还可以定义自己的依赖属性,以扩展WPF中的元素和功能。
定义依赖属性的类必须继承自 DependencyObject 类。 XAML 中使用的许多 UI 控件类都派生自 DependencyObject 类,它们支持依赖属性。
例如 Button 类支持 IsMouseOver 依赖属性:
<Window x:Class="HelloWPF.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:HelloWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<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
:当鼠标悬停在按钮上时,按钮的前景色会变为红色。当鼠标离开按钮时,它会变回原来的颜色。XAML
中的x:Type
标记扩展具有与 C#
中的 typeof()
类似的功能。它用于指定采用对象类型的属性。
为什么需要依赖属性
当在应用程序中使用依赖属性时,它会带来各种好处。在以下情况下,依赖属性可以在 CLR 属性上使用 –
如果要设置样式
如果你想要数据绑定
如果要设置资源(静态或动态资源)
如果你想支持动画
基本上,依赖属性提供了许多使用 CLR 属性无法获得的功能。
依赖属性 和其他 CLR属性 的主要区别如下-
CLR 属性可以通过使用 getter 和 setter 直接从类的私有成员中读取/写入。相反,依赖属性不存储在本地对象中。
依赖属性存储在DependencyObject
类提供的键/值对字典中。它还节省了大量内存,因为它在更改时存储了属性。也可以绑定在 XAML 中。
XAML 中的事件
现在多数的 UI 框架都是由事件驱动的,WPF 也是。所有的控件都提供了大量的事件可以订阅,这意味着,你的程序将在事件发生时接受到通知并且你可以对这些事件做出相应。事件有很多不同的类型,大部分会发生在用户鼠标键盘和程序互动的时候,例如KeyDown, KeyUp, MouseDown, MouseEnter, MouseLeave, MouseUp
等类的事件,事件的工作原理描述起来很复杂,我们现在只需要知道怎样连接XAML
和代码程序就可以了:
<Window x:Class="HelloWPF.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:HelloWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Name="pnlMainGrid" MouseUp="pnlMainGrid_MouseUp" Background="LightBlue">
</Grid>
</Window>
我们在 Grid 中订阅了MouseUp
事件, 指向方法pnlMainGrid_MouseUp
,我们在方法中添加一个MessageBox
来查看效果:
private void pnlMainGrid_MouseUp(object sender, MouseButtonEventArgs e)
MessageBox.Show("You clicked me at " + e.GetPosition(this).ToString());
MouseUp
事件使用的是一个名为MouseButtonEventHandler
的委托,它有两个参数, sender
(发生事件的控件), MouseButtonEventArgs
(一些有用的信息, 我们在上面的例子中通过它获取了鼠标的位置)。
有些事件会使用同一个委托,比如MouseUp
和MouseDown
共用MouseButtonEventHandler
, 注意MouseMove
用的是MouseEventHandler
。 当你定义事件处理方法的时候, 你需要知道事件使用的委托, 文档里可以找到。
小tip:Visual Studio 可以帮助我们生成正确的事件处理方法。 在XAML中输入事件的名字, VS 的 IntelliSense 就会提供生成新事件处理方法。
当然,也可以通过程序订阅事件:在 C#
中这会使用到 +=
语法,做过 winform
开发的应该很熟悉了。
WPF – 路由事件
XAML 中的事件和 WPF 中的路由事件是不同的,尽管它们有一些相似之处。XAML 中的事件是传统的事件模型,即一个元素上发生的事件会通知该元素的事件处理程序。事件处理程序可以是直接在 XAML 中声明的,也可以是在代码中编写的。
WPF 中的路由事件是一种更复杂的事件模型,它可以在元素树中的多个侦听器上调用处理程序,而不仅仅是引发事件的对象。它基本上是一个由 Routed Event 类的实例支持的 CLR 事件。它在 WPF 事件系统中注册。RoutedEvents 具有三种主要的路由策略,如下所示 –
可以通过整个元素树进行事件冒泡或隧道传递。这意味着当元素上发生路由事件时,事件会向上传递到其父元素,直到到达顶层元素,然后再向下传递到具有匹配事件处理程序的元素。路由事件使得可以更灵活地处理事件,并且可以减少事件处理程序的数量。
虽然路由事件是 WPF 中的一个特性,但它们并不是 XAML 的一部分。XAML 中可以使用传统的事件模型或路由事件模型,具体取决于所使用的技术和需要。
WPF 应用程序
创建 WPF 应用程序时,你首先会遇到Window类。它作为窗体的根节点,提供了标准的边框,标题栏和最大化,最小化和关闭按钮。WPF 窗体是 XAML(.xaml)文件(其中 元素是根)和后台代码(.cs)文件的组合。如果正在使用 Visual Studio 并创建一个新的WPF应用程序,它将创建一个默认窗体,如下所示:
<Window x:Class="WpfApplication1.Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
</Grid>
</Window>
x:Class
属性告诉 XAML 文件使用哪个类,实例中的是Windows1
,它是由Visual Studio 默认创建的,我们可以在MainWindow.xaml.cs
中找到他:
namespace HelloWPF
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
public MainWindow()
InitializeComponent();
正如我们所看到的,Class: Window
被partial
修饰,因为它在运行时与XAML文件相结合,为您提供完整的窗口。这实际上就是对InitializeComponent()
的调用所做的,这就是为什么需要它来启动并运行一个完整的窗口。如果我们返回到XAML文件,你会注意到Window元素上有一些其他有趣的属性,例如Title,它定义了窗口的标题(显示在标题栏中)还有起始宽度和高度。还有一些命名空间定义,我们将在XAML章节中讨论。
我们还会注意到 Visual Studio 已在 Window
中为我们创建了一个 Grid 控件。Grid 是 WPF 面板之一。虽然这个被包含在 Window
中的控件也可以是任何面板或控件,但 Window 只能拥有一个子控件 ,因此,使用一个可以包含多个子控件的面板通常是一个不错的选择。
重要的Window属性
WPF 的 Window
类有许多有趣的属性,您可以设置这些属性来控制应用程序窗口的外观和行为。
Icon
– 允许你定义窗口的图标,该图标通常显示在窗口标题之前的左上角。
ResizeMode
– 这可以控制最终用户是否以及如何调整窗口大小。默认是CanResize,允许用户像任何其他窗口一样调整窗口大小,使用最大化/最小化按钮或拖动其中一个边缘。CanMinimize将允许用户最小化窗口,但不能最大化它或拖动它更大或更小。NoResize是最严格的,最大化和最小化按钮被移除,窗口不能被拖得更大或更小。
ShowInTaskbar
– 默认值为true,但如果将其设置为false,则窗口将不会在Windows任务栏中显示。适用于非主窗口或应尽量减少托盘的应用程序。
Make correction
SizeToContent
– 决定Window是否应调整自身大小以自动适应其内容。默认是Manual, 这意味着窗口不会自动调整大小。其他选项有Width,Height和WidthAndHeight,分别对应自动调整宽度,高度或同时调整两者。
Topmost
– 默认是false, 但如果设置为true,除非最小化,否则您的窗口将保持在其他窗口之上。仅适用于特殊情况。
WindowStartupLocation
– 控制窗口的初始位置。默认是Manual, 表示窗口最初将根据窗口的Top和Left属性进行定位。其他选项是CenterOwner,它将窗口定位在其所有者窗口的中心,以及CenterScreen,它将窗口定位在屏幕的中心。
WindowState
– 控制初始窗口状态。它可以是Normal,Maximized或Minimized。默认值为Normal,除非您希望窗口最大化或最小化,否则应该使用它。
当然还有很多其他属性,请自行查阅。
使用 App.xaml
App.xaml 是你的应用程序定义的起点,当你创建一个新的 WPF 应用时,Visual Stuido 将自动为你创建它,同时还包括一个名为 App.xaml.cs 的后置代码文件。跟 Window 类似,这两个文件里面定义的是部分类,它们允许你同时在XAML标记和后置代码中完成工作。
App.xaml.cs 继承自 Application
类,在 WPF 程序中是一个中心类。.NET 会进入这个类,从这里启动需要的窗口或页面。这也是一个订阅一些重要应用程序事件的地方,例如,应用程序启动事件,未处理的异常事件等。
App.xaml 文件最常用的功能之一是定义全局资源,这些资源(例如全局样式)将可以从整个应用程序中使用和访问。
App.xaml 结构
默认情况下,新创建的 WPF 程序的 App.xaml 的内容可能会是这样:
<Application x:Class="HelloWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HelloWPF"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
这里要注意的主要是StartupUri
属性,它实际上指定了当应用程序启动时应该被加载的 Window (窗体)
或Page
。在这个例子中,MainWindow.xaml
会被启动,但是如果你想使用另外一个window
作为启动入口点,你只需要修改一下这个属性即可。
与上面 App.xaml 内容相匹配的 App.xaml.cs 大概是这样的:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace HelloWPF
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
你会看到这个类继承自Application
类,它允许我们在应用级别去做一些事情。举个例子,你可以订阅Startup
事件,然后手动创建你的启动窗口。
屏幕看着眼疼,写不下去了:
(WPF Tutorial)[https://wpf-tutorial.com/zh/12/wpf應用程式/資源resources/]
(WPF – 蝴蝶教程)[https://www.jc2182.com/wpf/wpf-layout.html]
深入浅出WPF
博客园 – 痕迹