如何学习WPF技术?

本文由长沙DotNET技术社区【邹溪源】整理,共有7544字,阅读需耗时15分钟。

引言

在桌面开发领域,虽然在某些领域,基于electron的跨平台方案能够为我们带来某些便利,但是由于WPF技术能够更好的运用Direct3D带来的性能提升、以及海量Windows操作系统和硬件资源的支持,所以他依然有着得天独厚的优势。

当然,选用一门技术,依然看公司的基因土壤和综合因素或者老板的心血来潮,例如QT也同样是一门非常不错的跨平台图形界面解决方案。

如何学习WPF技术?

在Quote上有人提出了这样一个相同的问题,查看问题,开发者Srikanth Pagadala如是回答:

1、以了解基础控件作为学习的起步过程:这些控件包括TextBox,Button,TextBlock及其他的,理解这些控件对外提供的属性,以及如何使用。
2、了解和使用布局空间:例如Grid、StackPanel、DockerPanel和其他控件,在这一点上,你需要花费大量的时间。同时你需要学会创建复杂的UI设计。
3、了解循环类型的空间,例如ItemControl控件。
4、了解关于模板的概念。包括如何定义包含CheckBox的Combox,同时这个控件还包含了一张图片的按钮,以及如何在ItemsControl中使用不同的模板。
5、理解数据绑定的运行机制。尝试创建一个MVVM或类似类型的应用程序。
6、创建一个典型的控件,探索DependencyProperties(依赖属性)和AttachedProperties(附加属性)。
7、创建一个样式资源,理解如何给控件设计样式。

除此之外,还有其他开发者给出了补充回答:

1、学习控件的数据绑定过程,在DataGrid上实现数据绑定。
2、学习和实现INotifyPropertyChanged类。查看如何实现
3、学习Observable Collection。该类型的集合广泛使用于数据集合绑定方面,同时也提供了数据改变通知的机制。
4、使网格上的列可编辑。用文本控件(用户项目模板)替换列。为每个捕获文本更改事件的列创建一个属性。在文本控件上使用绑定类型。尝试捕获您在后端在网格上所做的更改。
5、成功将数据控件中的文本控件与后端属性绑定后,请在同一页面上创建网格的副本。尝试同步这两个网格。例如,您在第一个网格中所做的每个更改都必须在第二个网格中自动更新。

网站“https://www.wpf-tutorial.com/”是一个专门用于学习WPF的网站,通过这个网站,可以快速的入门WPF。

由于WPF技术已经比较熟悉,所以书籍也比较多,网友推荐来自刘铁猛老师的《深入浅出WPF》这本书,而我通过Kindle则看到了一本比较有意思的书《葵花宝典-WPF自学手册》,这本书写得比较生动,通过故事的形式讲了WPF的许多技术原理,无形中让我对WPF的概念有了许多新的认识。当然,这本书已经有点年头了。

WPF的常用控件

| 控件类型 | 控件名称 | 控件说明 | 链接地址 |
|:—-:|:—-:|:—-:|:—-:|:—-:|:—-:|:—-:|:—-:|
| 组件 | Window | 窗口 | 查看示例 |
| | Page | 页面 | 查看示例 |
| | NavigationWindow | 导航窗口 | 查看示例 |
| | Frame | | 查看示例 |
| 常规控件 | Button | 按钮控件,提供Content作为内容 | 查看示例 |
| | TextBox | 文本框控件,用以输入文本 | 查看示例 |
| | TextBlock | 文本块,用以显示文本 | 查看示例 |
| | Label | 标签,用以显示文本 | 查看示例 |
| | ProgressBar | 进度条 | 查看示例 |
| | ToggleButton | 一种可以设置开关三态的按钮 | 查看示例 |
| | Image | 图像控件,通过Source设置资源路径 | 查看示例 |
| | CheckBox | 勾选框,可以设置是否勾选的三种状态 | 查看示例 |
| | RichTextBox | 富文本框,可以多种格式显示和输入文本 | 查看示例 |
| | TreeView | 树视图,以树状图的形式显示绑定内容,可以显示是否勾选三态。| 查看示例 |
| | WebBrowser | 浏览器,基于IE内核的浏览器控件 | 查看示例 |
| | Calendar | 日历控件 | 查看示例 |
| | ComboBox | 下拉列表 | 查看示例 |
| | ContentControl | 内容控件 | 查看示例 |
| | Expander | 扩展器,可以显示和折叠面板内的元素 | 查看示例 |
| | GroupBox | 分组框 | 查看示例 |
| | StatusBar | 状态栏,用于在页面下方显示状态信息。| 查看示例 |
| | DateTimePicker | 时间控件,可以设置时间状态。| 查看示例 |
| | DocumentViewer | 文档查看器 | 查看示例 |
| | RadioButton | 单选按钮 | 查看示例 |
| | ScollViewer | 滚动视图 | 查看示例 |
| | ScollBar | 滚动条 | 查看示例 |
| | Separator | 分隔器 | 查看示例 |
| | ToolBar | 工具条 | 查看示例 |
| | Slider | | 查看示例 |
| | Menu | 菜单 | 查看示例 |
| | MediaElement | 多媒体控件 | 查看示例 |
| | PasswordBox | 密码输入框 | 查看示例 |
| | TabControl | 选项卡 | 查看示例 |
| | ToolBarTray | 工具条 | 查看示例 |
| | WindowsFormsHost | 用以承载WinForm | 查看示例 |
| | Border | 边框 | 查看示例 |
| 数据控件 | ListView | 列表视图 | 查看示例 |
| | DataGrid | 数据表 | 查看示例 |
| | ListBox | 列表框 | 查看示例 |
| 布局 | WrapPanel | 可变面板 | 查看示例 |
| | StackPanel | 固定面板 | 查看示例 |
| | DockerPanel | 停靠面板 | 查看示例 |
| | Grid | 表格布局 | 查看示例 |
| | UniformGrid | 统一分布表格布局 | 查看示例 |
| 查看示例 | Canvas | 画布 | 查看示例 |
| 图形 | Point | 点 | 查看示例 |
| | Line | 线 | 查看示例 |
| | Path | 路径 | 查看示例 |
| | Polygon | 多边形 | 查看示例 |
| | Polyline | 多段线 | 查看示例 |
| | Rectangle | 矩形 | 查看示例 |
| | Shape | 画笔 | 查看示例 |
| | Rectangle | 矩形 | 查看示例 |
| | Ellipse | 椭圆 | 查看示例 |

WPF的XAML语法

概述

在WPF技术中引入的XAML语法算是该技术的一大特色,也是被学习者视同为学习路径陡峭的“罪魁祸首”。原因是在前端技术飞速发展的今天,HTML的语法体系由于更早的被开发者接受,所以也自然而然更容易成为开发者的首选。

而XAML是一种脱胎于XML,并吸收了HTML的精华的语法体系,是一种界面描述语言,XML语法本身相对而言较为臃肿的体系,看似成为了他的历史负担,但是其实倒也没那么复杂,通过几个简单的示例,其实就足够掌握这门新的语法体系了。例如,使用这样的语法,完全可以平滑过渡到这样的语法体系。(部分标签其实只是大小写不同)。当然,在XAML中熟练编写样式,确实需要花一点点时间。

在WPF中,通过XAML定义面向用户交互层的界面,然后编译成baml运行,后端则使用C#或VB.NET这样的CLR语法来实现逻辑交互。

XAML的语法定义

XAML的根元素定义

根元素定义是定义XAML的命名空间。

1
2
3
<Pagexmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

XAML的属性语法

通过xaml定义按钮,并设置文本为 helloworld 。这种写法在官方文档中称为“属性语法”,即直接在XAML中对属性进行设置。

1
<Button Background="Blue" Foreground="Red" Content="hello world"/>

XAML的属性元素语法

通过xaml定义按钮,并设置其背景为蓝色画笔,字体颜色为红色画笔,内容 为helloworld。这种写法在官方文档中称为“属性元素语法”。

1
2
3
4
5
6
7
8
9
10
11
<Button><Button.Background><SolidColorBrush Color="Blue"/></Button.Background><Button.Foreground><SolidColorBrush Color="Red"/></Button.Foreground><Button.Content>hello world</Button.Content>
</Button>

XAML的集合语法

定义按钮的颜色为红色和蓝色渐变色,内容为helloworld。这种称为“集合语法”。

1
2
3
4
5
6
7
<LinearGradientBrush><LinearGradientBrush.GradientStops><!-- no explicit new GradientStopCollection, parser knows how to find or create --><GradientStop Offset="0.0" Color="Red" /><GradientStop Offset="1.0" Color="Blue" /></LinearGradientBrush.GradientStops>
</LinearGradientBrush>

XAML的样式定义

通过属性语法来定义按钮的外观

样式定义使用 标签,然后在中间对样式的内容进行定义。

例如,以下表示通过XAML语法对 ToggleButton 按钮定义了一个命名为 ToggleLikeButtonStyle 的样式。

1
2
3
4
5
6
7
8
9
10
11
12
 <Style TargetType="ToggleButton" x:Key="ToggleLikeButtonStyle"><Setter Property="Margin"   Value="4" /><Setter Property="FontWeight" Value="Black"/><Setter Property="Foreground"  Value="Black" /><Setter Property="BorderThickness" Value="0"/><Setter Property="IsThreeState" Value="False"></Setter></Style>

WPF中的模板Template

WPF中的控件可以通过模板 Template 的形式来定义其内容,使得开发者能够通过 XAML 灵活的对控件的外观进行扩展。例如,如下定义了一个 Template,这个控件模板将会对控件(Button)定义填充制定颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 <Setter Property="Template"><Setter.Value><ControlTemplate><Border BorderThickness="0" CornerRadius="3"><Border.Background><LinearGradientBrush EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#4696F2" Offset="0.5"/></LinearGradientBrush></Border.Background><ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" /></Border></ControlTemplate></Setter.Value></Setter>

XAML中的触发器Triggers

传统的WinForm开发者习惯于通过事件的机制对按钮的外观进行定义,而在WPF中,则可以通过属性的形式对外观进行设置,这使得开发者更能够写出高质量的代码。

例如,如下代码通过定义触发器,设置控件(控件为 ToggleButton),当控件的勾选状态属性为“IsChecked” 时,其边框填充色为#4696F2颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter Property="Border.Background" TargetName="PART_Background"
<Setter.Value><LinearGradientBrush EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#4696F2" Offset="0.5"/></LinearGradientBrush></Setter.Value></Setter><Setter Property="Width" TargetName="PART_Background" Value="60"></Setter><Setter Property="Content"  TargetName="contextPresenter" Value="已点赞"></Setter><Setter Property="Visibility"  TargetName="contextPresenter" Value="Visible"></Setter><Setter Property="Visibility" TargetName="contextImage" Value="Hidden"/></Trigger>
</ControlTemplate.Triggers>

部分完整代码

在上述事例中,共定义了两个按钮的样式,分别是:

  • FlatButtonStyle,这是个圆角按钮。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    <Style TargetType="Button" x:Key="FlatButtonStyle"><Setter Property="Margin"   Value="4" /><Setter Property="FontWeight" Value="Black"/><Setter Property="Foreground"  Value="Black" /><Setter Property="BorderThickness" Value="0"/><Setter Property="Template"><Setter.Value><ControlTemplate><Border BorderThickness="0" CornerRadius="3"><Border.Background><LinearGradientBrush EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#4696F2" Offset="0.5"/></LinearGradientBrush></Border.Background><ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" /></Border></ControlTemplate></Setter.Value></Setter></Style>
    
  • ToggleLikeButtonStyle,这是一个点赞按钮。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    
    <Style TargetType="ToggleButton" x:Key="ToggleLikeButtonStyle"><Setter Property="Margin"   Value="4" /><Setter Property="FontWeight" Value="Black"/><Setter Property="Foreground"  Value="Black" /><Setter Property="BorderThickness" Value="0"/><Setter Property="IsThreeState" Value="False"></Setter><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type ToggleButton}"><Border BorderThickness="0" CornerRadius="3" Name="PART_Background"><Border.Background><LinearGradientBrush EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#525252" Offset="0.5"/></LinearGradientBrush></Border.Background><Grid><ContentPresenter x:Name="contextPresenter" Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" /><Image x:Name="contextImage" Width="24" Height="24" Source="assests/thumbs-up-outline.png" HorizontalAlignment="Center" VerticalAlignment="Center"/></Grid></Border><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter Property="Border.Background" TargetName="PART_Background"><Setter.Value><LinearGradientBrush EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#4696F2" Offset="0.5"/></LinearGradientBrush></Setter.Value></Setter><Setter Property="Width" TargetName="PART_Background" Value="60"></Setter><Setter Property="Content"  TargetName="contextPresenter" Value="已点赞"></Setter><Setter Property="Visibility"  TargetName="contextPresenter" Value="Visible"></Setter><Setter Property="Visibility" TargetName="contextImage" Value="Hidden"/></Trigger><Trigger Property="IsChecked" Value="False"><Setter Property="Border.Background" TargetName="PART_Background"><Setter.Value><LinearGradientBrush EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#525252" Offset="0.5"/></LinearGradientBrush></Setter.Value></Setter><Setter Property="Width" TargetName="PART_Background" Value="40"></Setter><Setter Property="Visibility"  TargetName="contextPresenter" Value="Hidden"></Setter><Setter Property="Visibility" TargetName="contextImage" Value="Visible"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
    </Style>
    

XAML的标记扩展

通过了解WPF的常用控件,我们可以知道自己需要使用的控件有哪些属性,并能使用 XAML 语法对相应的属性进行设置,这种设置方法有别于通过C#代码的形式进行定义的方法,在 XAML中的属性称为 “标记”。标记使用 “{}” 花括号,编译器通过该花括号将语法和XAML语法进行区分。

例如:

1
 HeaderTemplate="{DynamicResource StretchedHeaderTemplate}"

标记值的转换与TypeConverters

在进行标记值转换时,有时候需要使用TypeConverters实现类型转换。例如,在上述示例代码中,可以看到使用了字符串“#525252”来定义颜色,在内部就是实现了从字符串到 Color 类的转换过程。限于篇幅有限,此处就暂时略过。

XAML中内置特殊标记扩展

  • x:Type:特定类型

    1
    
    <object property="{x:Type prefix:typeNameValue}" .../>
    
  • x:Static:使用静态值。

    1
    
    <object property="{x:Static prefix:typeName.staticMemberName}" .../>
    
  • x:Null:使用空对象定义为属性值。

    1
    
    <object property="{x:Null}" .../>
    
  • x:Array:使用数组对象。

    1
    2
    3
    
    <x:Array Type="typeName">arrayContents
    </x:Array>
    

常见的标记扩展

  1. StaticResource:通过替换已定义资源的值来为属性提供内容,该资源标记在XAML加载时自动执行。静态资源无法通过在XAML语法体系中对其引用关系进行前向引用,意味着无法通过多层级关系定义可复用的样式资源,如果需要这样做,则需要使用DynamicResource。

    1
    
    <object property="{StaticResource key}" .../>
    
  2. DynamicResource:在运行时为资源提供内容。

    1
    
    <object property="{DynamicResource key}" .../>
    
  3. Binding:在运行时为使用数据上下文为数据提供内容。

    1
    2
    3
    4
    5
    6
    7
    8
    
    <object property="{Binding}" .../>
    -or-
    <object property="{Binding  bindProp1=value1[, bindPropN=valueN]*}" ...
    />
    -or-
    <object property="{Binding path}" .../>
    -or
    <object property="{Binding path[, bindPropN=valueN]*}" .../>
    
  4. RelativeSource:提供了可在运行时对象树中导航几个可能的关系的 Binding 的源信息。

    1
    
    <Binding RelativeSource="{RelativeSource modeEnumValue}" .../>
    
  5. TemplateBinding:使控件模板能够使用模板化属性的值,这些属性来自于将使用该模板的类的对象模型定义属性。

    1
    
    <object property="{TemplateBinding sourceProperty}" .../>
    
  6. ColorConvertedBitmap:提供一种方法,用于指定没有嵌入的配置文件的位图源。颜色上下文/配置文件由 URI 指定,与映像源 URI 相同。

    1
    
    <object property="{ColorConvertedBitmap imageSource sourceIIC destinationIIC}" .../>
    
  7. ComponentResourceKey和TemplateResourceKey:

    1
    
    <object x:Key="{ComponentResourceKey {x:Type targetTypeName}, targetID}" .../>
    

XAML资源复用

在开发过程中,我们可以直接在按钮上进行按钮模板的定义,例如下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<Button Width="40" Height="40" Style="{DynamicResource CubeImageButtonStyle}" Click="Button_Click" Content="点赞"><Button.Background><ImageBrush ImageSource="/assests/favicon.png" Stretch="Fill"/></Button.Background></Button><Setter Property="Template"<Setter.Value><ControlTemplate><Border BorderThickness="0" CornerRadius="3"><Border.Background><LinearGradientBrush EndPoint="0,1" StartPoint="0,0"><GradientStop Color="#4696F2" Offset="0.5"/></LinearGradientBrush></Border.Background><ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center" /></Border></ControlTemplate></Setter.Value></Setter></Style></Window.Resources><Grid>
<Button Grid.Column="0" Grid.Row="1" Style="{StaticResource FlatButtonStyle}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="48" Height="16" FontSize="10" Background="#4696F2" Content="获取"></Button></Grid>

这样的代码在界面比较简单时,还无所谓,但是随着控件的样式越来越复杂,可能会成为一团乱麻,这对于追求优雅代码的我们来说,可能是难以忍受的,所以往往会使用资源引用来完成。

StaticResource

例如,我们可以在当前页面代码中定义对应的样式,这种样式可以使用 StaticResource 的形式引入。但是这样的引用形式,没有对象图的访问权限,意味着无法访问资源依赖的其他资源。

1
2
3
4
5
6
7
 <Window.Resources>
<Style TargetType="Button" x:Key="FlatButtonStyle"> <Setter Property="Margin"   Value="4" /><Setter Property="FontWeight" Value="Black"/><Setter Property="Foreground"  Value="Black" />        <Setter Property="BorderThickness" Value="0"/>
</Window.Resources>

DynamicResource

将上述代码中的{StaticResource FlatButtonStyle} 改成{StaticResource FlatButtonStyle}则会在运行时加载样式,并可以访问相应的对象图。

当然,这样的更改意义不大,如果该FlatButtonStyle引用了其他样式或元素,会发生作用。

1
2
3
<Grid
<Button Grid.Column="0" Grid.Row="1" Style="{StaticResource FlatButtonStyle}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="48" Height="16" FontSize="10" Background="#4696F2" Content="获取"></Button></Grid>

注意事项

1、由于XAML语法脱胎于XML语法,而XML语法中本身对某些输入字符,如“<>”存在限制,所以在XAML中也会出现这类问题,并会被Visual Studio检测出错误而无法编译,需要使用UTF-8编码进行转换。

用户控件和自定义控件

用户控件

而用户控件,使用于控件组合的场景。

自定义控件

在笔者进行开发时,总是思考究竟是使用用户控件,还是自定义控件,后来在阅读《葵花宝典-WPF自学手册》这本书中,终于得以大彻大悟。

作者指出:“不要被控件的外观所欺骗,要考虑其内在本质”。即思考控件的基本特征,首先想到该控件的行为与原有控件的行为是否相似,如果能够找到,则修改原有控件,而不是定义一个控件。尤其是在XAML语法中,能够通过Content 模型和模板、附加属性的运用,使得自定义控件的用途得到了进一步缩减,只有当实在万不得已时,在定义自定义控件。

作者给出了使用自定义控件的分析思路:

例如,在示例代码*ToggleLikeButtonStyle *中,我实现了一个点赞和取消点赞的状态,则使用了ToggleButton来完成,就没必要使用 Button 扩展出一个是否点赞的状态了。

而如果我们需要实现的功能有这么复杂,那大概使用传统的控件就无法实现,就得使用自定义控件了。(点击查看示例代码)

作者定义了自定义控件 ButtonEx,并实现了依赖属性 ButtonType,见【依赖属性】,并定义了不同类型的样式特征。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 <Trigger Property="ButtonType" Value="Icon"><Setter Property="Cursor" Value="Hand"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type controls:ButtonEx}"><Border Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"><Image x:Name="Img" VerticalAlignment="Center" HorizontalAlignment="Center" Source="{TemplateBinding Icon}" Stretch="None"/></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter Property="Opacity" Value="0.8"/></Trigger><Trigger Property="IsPressed" Value="True"><Setter Property="Opacity" Value="0.9"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Trigger>

使用时,只需这样设置,即可实现不同类型的按钮外观。

1
 <controls:ButtonEx Icon="/Images/search.png"  Margin="10" ButtonType="Icon"/>

属性和事件

依赖属性

依赖属性是为既有WPF控件对象定义自定义属性,以便支持其扩展,例如在上述自定义控件的示例中,就定义了依赖属性 ButtonType,实现了不同类型的按钮外观。

1
2
3
4
5
6
7
8
public ButtonType ButtonType{get { return (ButtonType)GetValue(ButtonTypeProperty); }set { SetValue(ButtonTypeProperty, value); }}public static readonly DependencyProperty ButtonTypeProperty =
DependencyProperty.Register(“ButtonType”, typeof(ButtonType), typeof(ButtonEx), new PropertyMetadata(ButtonType.Normal));


附加属性
按照官方的说法就是“附加属性旨在用作可在任何对象上设置的一类全局属性”,例如,DockPanel面板中的子对象,继承了来自于容器对象的附加属性,使得其能够在父对象中实现停靠的功能。

1
2
3
<DockPanel><CheckBox DockPanel.Dock="Top">Hello</CheckBox>
</DockPanel>

路由事件

基本定义

假设我们定义了几个这样的控件。

1
2
3
4
5
6
7
<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1"><StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler"><Button Name="YesButton" Width="Auto" >Yes</Button><Button Name="NoButton" Width="Auto" >No</Button><Button Name="CancelButton" Width="Auto" >Cancel</Button></StackPanel>
</Border>

实现了这样的界面

路由事件就是针对这组元素树中多个元素调用处理程序的事件。当我们点击了按钮Button时,将会触发 Button=>StackPanel=>Border的事件路由,而不是像WinForm应用一样,只能触发最上层的Button的按钮点击事件。

路由策略

  • 冒泡事件(官方称为浮升,这个翻译有点。。):调用事件源上的事件处理程序。路由事件随后会路由到后续的父级元素,直到到达元素树的根。大多数路由事件都使用浮升路由策略。浮升路由事件通常用于报告来自不同控件或其他 UI 元素的输入或状态变化。

  • 直接:只有源元素本身才有机会调用处理程序以进行响应。通过使用 EventSetter 和 EventTrigger使用来设置处理程序。例如,可以使用RoutedEventArgs的Handled,设置为 true 将事件标记为已处理,将 “停止” 路由用于隧道路由或冒泡路由。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    void MakeButton2()
    {Button b2 = new Button();b2.Click += new RoutedEventHandler(Onb2Click2);
    }
    void Onb2Click2(object sender, RoutedEventArgs e)
    {//logic to handle the Click event
    }
    
  • 隧道:最初将调用元素树的根处的事件处理程序。随后,路由事件将朝着路由事件的源节点元素(即引发路由事件的元素)方向,沿路由线路传播到后续的子元素。

  • WPF中约定,隧道路由事件的名称以单词“Preview”开头。输入事件通常成对出现,一个是浮升事件,另一个是隧道事件。例如,如下图所示,假设按钮2为触发事件的源。

1、处理Border根元素的隧道事件PreviewMouseDown

2、处理StackPanel面板的隧道事件PreviewMouseDown.

3、处理Button按钮的隧道事件的PreMouseDown。

4、处理Button按钮的MouseDown事件。

5、处理StackPanel的MouseDown事件。

6、处理Border的MouseDown事件。

总结

WPF是一个非常庞大的技术体系,以上学习路径仅供开发者进行简单的入门,由于篇幅有限,对于标记扩展还需要进一步理解透彻,以及格式转换、图形绘制、数据绑定、MVVM等内容未能一一描述。

如果果想要对WPF进一步了解,最好通过系统的学习相关知识,除了前面提到的网站和几本书,最好的入门网站依然是微软官方文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311563.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java爬取网页数据_如何使用爬虫工具采集数据

网络爬虫是一种按照一定规则&#xff0c;自动抓取万维网数据的脚本。按照一定规则&#xff0c;指的是爬虫程序需要解析网页的dom结构&#xff0c;针对dom结构爬取自己感兴趣的数据。(图1)这就是一个网页源码的dom结构,我们需要一级一级指定抓取的标签&#xff0c;如下图:(图2)图…

gRPC in ASP.NET Core 3.x - gRPC 简介(2)

前一篇&#xff1a;gRPC in ASP.NET Core 3.x - gRPC 简介&#xff08;1&#xff09;身份认证这里指的不是用户的身份认证&#xff0c;而是指多个server和client之间&#xff0c;它们如何识别出来谁是谁&#xff0c;并且能安全的进行消息传输。在身份认证这方面&#xff0c;gRP…

.NET Core开发实战(第8课:配置框架:让服务无缝适应各种环境)--学习笔记

08 | 配置框架&#xff1a;让服务无缝适应各种环境配置是应用程序发布到各种环境的必备能力&#xff0c;这一节开始详细讲解 ASP.NET Core 的配置框架配置框架的核心包有两个&#xff0c;一个抽象包&#xff0c;一个实现包Microsoft.Extensions.Configuration.AbstractionsMicr…

【译】探索更轻量的Electron替代品来托管Blazor桌面应用程序

本文翻译自 ASP.NET 项目组的 Steve Sanderson 的博客&#xff0c;发表于 2019 年 11 月 1 日。Steve Sanderson 是 Blazor 最早的创造者。这篇文章发布后还有一篇后续&#xff0c;是介绍一个在本文提到的跨平台 webview 概念的落地项目 WebWindow &#xff0c;我也会接着翻译过…

mysql 剔除不可见字符_不可见字符,Excel里最隐蔽的坑

小伙伴们好啊&#xff0c;我是流浪铁匠&#xff0c;今天为大家介绍的是excel数据整理时一类最常见的坑——不可见字符。从unichar函数对应的uincode字符集结果来说&#xff0c;excel大约有111万个字符&#xff0c;其中有不少字符的性质千奇百怪&#xff0c;由于unichar/unicode…

同为Chromium浏览器,Edge却被“特别关照”

谷歌会在 Microsoft Edge 用户访问 Chrome Web Store&#xff08;Chrome 网上应用商店&#xff09; 时发去一条通知提醒&#xff0c;建议用户切换到 Google Chrome 以安全地使用扩展程序&#xff0c;通知还提供了下载 Google Chrome 的链接。不过有趣的是&#xff0c;其他 Chro…

噪声与振动控制工程手册_声学分享客噪声与振动控制篇大型隔振工程案例介绍...

第一期《声学分享客--噪声与振动控制篇》将于今日上午10&#xff1a;00~11&#xff1a;00&#xff0c;在线直播&#xff0c;欢迎各位上线交流。主讲人&#xff1a;苏宏兵报告内容&#xff1a;大型隔振工程案例介绍报告人介绍&#xff1a;苏宏兵&#xff0c;工学硕士&#xff0c…

微软推出全新的Windows 10系统图标

微软已开始通过 Fast Ring 向 Insider 用户推出新的 Windows 10 系统图标&#xff0c;这些新图标基于 Fluent 设计。更改从邮件和日历等程序开始&#xff0c;微软将陆续推出更多彩色图标。这些图标将在接下来的几个月中通过 Microsoft Store 的应用更新和 Windows 10 的发布预览…

.NET Core开发实战(第9课:命令行配置提供程序)--学习笔记

09 | 命令行配置提供程序&#xff1a;最简单快捷的配置注入方法这一节讲解如何使用命令行参数来作为配置数据源命令行配置&#xff08;提供程序的&#xff09;支持三种格式的命令1、无前缀的 keyvalue 模式2、双中横线模式 --keyvalue 或 --key value3、正横杠模式 /keyvalue 或…

gRPC in ASP.NET Core 3.x - gRPC 消息定义

之前写了几篇关于 Protoco Buffer 的文章。gRPC in ASP.NET Core 3.x - gRPC 简介&#xff08;1&#xff09;gRPC in ASP.NET Core 3.x - gRPC 简介&#xff08;2&#xff09;当gRPC使用Protocol Buffer作为传输协议的时候&#xff0c;Protocol Buffer里所有的规则仍然都适用。…

10个用于C#.NET开发的基本调试工具

在调试软件时&#xff0c;工具非常重要。获取正确的工具&#xff0c;然后再调试时提取正确的信息。根据获取的正确的错误信息&#xff0c;可以找到问题的根源所在。找到问题根源所在&#xff0c;你就能够解决该错误了。你将看到我认为最基本的解决在C# .NET 中错误问题的工具的…

[蓝桥杯2015初赛]移动距离

题目描述 X星球居民小区的楼房全是一样的&#xff0c;并且按矩阵样式排列。 其楼房的编号为1,2,3… 当排满一行时&#xff0c;从下一行相邻的楼往反方向排号。 比如&#xff1a;当小区排号宽度为6时&#xff0c;开始情形如下&#xff1a; 1 2 3 4 5 6 12 11 10 9 8 7 13 14 15 …