外观
WPF基础
约 9132 字大约 30 分钟
WPF
2024-03-15
Application
窗口关闭模式
WPF 应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序才停止运行。 ShutDown 是隐式或显式发生,可以通过指定 ShutdownMode 的属性值来进行设置。
的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的的 Windows 应用程序的关闭模式比较类似) - OnExplicitShutdown:只有在调用 Application 对象的 Shutdown()方法时,应用程序才会关闭。
Application 属性
| 属性 | 说明 |
|---|---|
| Windows | 获取在应用程序中实例化窗口。 |
| Current | 获取当前 AppDomain 的 Application 对象。 |
| MainWindow | 获取或设置应用程序的主窗口。 |
| Properties | 获取应用程序范围的属性集合。 |
| ShutdownMode | 获取或设置会导致 Shutdown 方法调用的情况。 |
| StartupUri | 获取或设置自动显示的 UI ,当应用程序启动时。 |
| Resources | 获取或设置应用程序范围资源的集合,例如样式和画笔。 |
| Dispatcher | 获取与此 DispatcherObject 关联的 Dispatcher。 (继承自 DispatcherObject。) |
| ResourceAssembly | 获取或设置对于 WPF 应用程序的资源提供已装箱统一资源标识符 (URI) 的 Assembly 。 |
Windows
窗口的主要用途是承载可视化数据并使用户可以与数据进行交互的内容。WPF 应用程序使用 Window 类来提供它们自己的窗口。在 WPF 中,可以使用代码或 XAML 标记来实现窗口的外观和行为。
窗口生命周期
实例>显示>激活>停用>关闭
显示
Show()//显示窗口;ShowDialog()//显示模态窗口激活 当窗口变为活动窗口时,它会引发
Activated事件。活动窗口是正在捕获用户输入(例如,键击和鼠标单击)的窗口。- 当第一次打开窗口时,只有在引发了 Activated 事件之后,才会引发
Loaded和ContentRendered事件。 记住这一点,在引发 ContentRendered 时,便可认为窗口已打开。 - 窗口变为活动窗口之后,用户可以在同一个应用程序中激活其他窗口,还可以激活其他应用程序。 当这种情况出现时,当前的活动窗口将停用,并引发
Deactivated事件。 同样,当用户选择当前停用的窗口时,该窗口会再次变成活动窗口并引发Activated。
- 当第一次打开窗口时,只有在引发了 Activated 事件之后,才会引发
关闭窗口
- Close()方法:关闭窗体,并释放窗体的资源
- Closing 事件、Closed 事件:关闭时、关闭后引发的事件,通常在
Closing事件中提示用户是否退出等信息。
view
viewmodel
code-behind
控件父类
DispatcherObject
在 WPF 中绝大部分控件都继承自 DispatcherObject,甚至包括 Application。这些继承自 DispatcherObject 的对象具有线程关联特征,也就意味着只有创建这些对象实例,且包含了 Dispatcher 的线程(通常指默认 UI 线程)才能直接对其进行更新操作。
所有 WPF 应用程序启动时都会加载两个重要的线程:一个用于呈现用户界面,另一个用于管理用户界面。呈现线程是一个在后台运行的隐藏线程,因此您通常面对的唯一线程 就是 UI 线程。WPF 要求将其大多数对象与 UI 线程进行关联。这称之为线程关联,意味着要使用一个 WPF 对象,只能在创建它的线程上使用。在其他线程上使用它会导致引发运行时异常。 UI 线程的作用是用于接收输入、处理事件、绘制屏幕以及运行应用程序代码。
在 WPF 中,DispatcherObject 只能通过与它关联的 Dispatcher 进行访问。 例如,后台线程不能更新由 UI 线程创建的 Label 的内容。
Dispatcher提供了两个方法解决跨线程调用的问题,`Invoke` **和 `BeginInvoke`** 。其中 `Invoke`内部还是调用了 `BeginInvoke`
`Invoke` 是同步操作,而 `BeginInvoke` 是异步操作。 该这两个操作将按指定的 DispatcherPriority 添加到 Dispatcher 的队列中。
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
{
lblHello.Content = "Dispatche 同步方法 !!";
});
//或者
new Thread(() =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(2));
this.lblHello.Content = "Dispatche 异步方法!!"+ DateTime.Now.ToString();
}));
}).Start();DependencyObject
管理所有的依赖 依赖属性 是事件驱动变为数据驱动的产物,比如由wonform的属性赋值,变为mvvm的绑定概念
DependencyObject 类表示参与依赖属性系统的对象。属性系统的主要功能是计算属性的值,并提供有关已更改的值的系统通知。 参与属性系统的另一个类 DependencyProperty。 DependencyProperty 允许将依赖属性注册到属性系统,并提供有关每个依赖属性的标识和信息,而 DependencyObject 为基类,使对象能够使用此依赖属性。
INotifyPropertyChanged 类用于通知UI刷新,注重的仅仅是数据更新后的通知。DependencyObject 类用于给UI添加依赖和附加属性,注重数据与UI的关联。如果简单的数据通知,两者都可以实现的。
Visual
为呈现提供支持 透明度 边框 圆角阴影等等属性;比如button,textbox派生自visual类
输出显示:呈现视觉对象的持久、序列化的绘图内容。
- 转换:针对视觉对象执行转换。
- 剪裁:为视觉对象提供剪裁区域支持。
- 命中测试:确定坐标或几何形状是否包含在视觉对象的边界内。
- 边框计算:确定视觉对象的边框。
UIElement (很重要)
继承自Visual类,几乎所有控件都继承了该类,包含了所有的事件及属性
eg: Visibility属性:获取或设置控件的可见性。默认是Visible。
ClipToBounds属性:如果该值为true,表示进行裁剪,以适配它的父控件。比如有时候我们外面有一个Panel,里面的控件尺寸太大,势必会“撑破”外面的父控件,为了布局美观,只好削足适履。
Clip属性:用于剪裁区域大小的几何图形。需要注意的是,这个属性和上面的ClipToBounds属性是有区别的。ClipToBounds是裁剪控件自身,Clip是裁剪控件里面的内容。比如Image图像控件,我们在显示一张图时,就可以运用Clip进行裁剪后显示,通常在显示用户头像时裁剪成圆形时使用。
<Image
Source="sampleImages\Waterlilies.jpg"
Width="200" Height="150" HorizontalAlignment="Left">
<Image.Clip>
<EllipseGeometry
RadiusX="100"
RadiusY="75"
Center="100,75"/>
</Image.Clip>
</Image>SnapsToDevicePixels属性:如果该值为true,表示控件的呈现是否应使用特定于设备的像素设置。意思是开启后可以最大限度的防锯齿效果,默认为false。
IsFocused属性:这是一个只读属性,表示当前控件是否有焦点。
IsEnabled属性:如果该值为true,表示禁用控件,反之启用控件。
IsHitTestVisible属性:获取或设置一个值,该值声明是否可以返回此元素作为其呈现内容的某些部分的点击测试结果。
IsVisible属性:这是一个只读属性,表示当前控件是否显示。
Focusable属性:如果该值为true,表示控件可以得到焦点,大部份内容控件都默认可以获得焦点。
IsKeyboardFocused属性:表示该控件是否具有键盘焦点。
IsMouseOver属性:表示鼠标是否在控件上面。通常在设计控件的样式(Style)时会用到。
IsStylusOver属性:表示触笔指针是否在控件的上方。
IsSealed属性:表示当前类型是否为只读类。
Opacity属性:设置控件的透明度,取值范围是0-1之间的double值。
AllowDrop属性:表示控件是否允许拖拽操作。
RenderTransform属性:(非常重要)如果要设置控件的移动、缩放、旋转,需要这此属性进行设置。
FrameworkElement
继承自UIElement。是众多控件中最核心的基类 Object->DispatcherObject->DependencyObject->Visual->UIElement->FrameworkElement
LayoutTransform:获取或设置在执行布局时应应用于此元素的图形转换。Width:这是表示控件的宽度。与之相关的还有以下几个属性。- ActualWidth:获取此元素的呈现宽度。只读属性。
- MaxWidth:获取或设置一个控件的最大宽度。
- MinWidth:获取或设置一个控件的最小宽度。
Height:这是表示控件的高度,与之相关的还有以下几个属性。- ActualHeight:获取此元素的呈现高度。只读属性。
- MaxHeight:获取或设置一个控件的最大高度。
- MinHeight:获取或设置一个控件的最小高度。
Tag:这个属性非常重要,它是object类型,意味着可以保存任意类型的对象值。它就像FrameworkElement类身上的一个小口袋,但确能容纳万物。我们通常会将一些与控件相关的数据临时存放在Tag属性中,当把控件作为参数传递时,小口袋里面的对象也随之传递过去了。Name:获取或设置控件的标识名称。在同一个窗体、页、用户控件中,Name标识是唯一的。设置了控件的名称后,我们就可以在后端代码直接以这个标识去引用控件。Margin:获取或设置控件的外边距。如下所示,我们定义了一个button的margin,距离左边、上边、右边和下边的像素分别是20、40、60、80。
相关信息
Padding属性 与Margin相对应的是Padding,表示设置控件的内边距。但是这个属性并不在FrameworkElement中,而在Control类中,从本节第一张图所示,说明只有内容控件才有Padding,而Shape和Panel是没有Padding属性的。
HorizontalAlignment:设置控件的水平对齐方式。这个对齐方式是相对于父元素而言的,比如我们有一个Button控件,在外面还包裹了一层Grid控件,那么,设置Button控件的HorizontalAlignment属性,可以将Button控件分别显示在Grid控件的左边、中间、右边三个位置。VerticalAlignment:设置控件的垂直对齐方式。与HorizontalAlignment属性类似,只是对方的方向不同,可以设置控件在垂直方向上是居于顶部、中间、还是底部三个位置。 他们都有stretch表示填充式ToolTip:获取或设置用户界面 (UI) 中为此元素显示的工具提示对象。指鼠标移到控件上方时显示的提示内容,它是一个object类型,意味着可以显示任意布局外观。Parent:获取此元素的逻辑父元素。它是一个只读属性。Style样式FocusVisualStyle获得焦点时的样式ResourceDictionary 资源
- 资源字典,它提供一个哈希表/字典实现,其中包含组件所使用的 WPF 资源以及 WPF 应用程序的其他元素
Resources获取或设置本地定义的资源字典DataContext数据上下文
在前面的《DependencyObject类》一文中提到过数据驱动模式,控件的值绑定某个“变量”,当“变量”的值发生改变,控件的值也跟着改变,反过来说,当控件的值发生改变,“变量”的值也跟着改变。那么这个特指的“变量”是什么?它和我们今天要介绍的数据上下文有什么关系? 答案是,这个“变量”其实也是一个属性,且必须是一个属性(重点),它是谁的属性?WPF说,它是某个ViewModel类的属性。 假定我们有一个View窗体,窗体有一个TextBox控件;又假如我们还有一个ViewModel实体,这个实体中有一个叫Name的属性。如果我们要将TextBox控件的Text属性和ViewModel实体的Name属性成功的建立绑定关系,必备的条件是什么? 首先,由于View窗体继承于FrameworkElement类,所以每个窗体(或控件)都有一个叫DataContext的数据上下文属性。所以必备的条件是:ViewModel实体必须先赋值给View窗体的DataContext,ViewModel的Name属性才能绑定到TextBox控件的Text属性。换言之,领导之间要先搭好桥,下属和下属才好配合工作。这就是DataContext的概念和用途。
DataContext:获取或设置元素参与数据绑定时的数据上下文。ContextMenu:设置与获取控件的上下文菜单 ,就是鼠标在控件上右键时弹出来的菜单。Cursor:获取或设置在鼠标指针位于此元素上时显示的光标。
布局
WPF 的布局控件都在 System.Windows.Controls.Panel 基类下
Panel基类
注意
Background 切记!切记! 如果Background没有值,鼠标事件不会生效
Canvas(绝对布局)
Canvas 是最基本的面板,只是一个存储控件的容器,它不会自动调整内部元素的排列及大小,它仅支持用显式坐标定位控件,使用 left top 等布局。
- Canvas 的主要用途是用来画图。Canvas 默认不会自动裁减超过自身范围的内容,即溢出的内容会显示在 Canvas 外面,这是因为默认
ClipToBounds="False"可以通过设置ClipToBounds="True"来裁剪多出的内容。- Canvas 内的子控件不能使用两个以上的 Canvas 附加属性,如果同时设置
Canvas.Left和Canvas.Right属性,那么后者将会被忽略。
- Canvas 内的子控件不能使用两个以上的 Canvas 附加属性,如果同时设置
WrapPanel(瀑布流)
换行面板。将各个控件从左至右按照行或列的顺序罗列,当长度或高度不够时就会自动调整进行换行,后续排序按照从上至下或从右至左的顺序进行
StackPanel(栈)
将控件按照行或列来顺序排列,但不会换行。
通过设置面板的
Orientation属性设置了两种排列方式:横排(Horizontal 默认的)和竖排(Vertical)。纵向的 StackPanel 默 认每个元素宽度与面板一样宽,反之横向亦然。如果包含的元素超过了面板空间,它只会截断多出的内容。 元素的 Margin 属性用于使元素之间产生一定得间隔,当元素空间大于其内容的空间时,剩余空间将由HorizontalAlignment和VerticalAlignment属性来决定如何分配。当把 StackPanel 的 FlowDirection 属性设置为 RightToLeft,Orientation 属性设置为 Horizontal,StackPanel 将从右向左排列元素。
DockPanel(停靠布局)
类似 winform 的 Dock 属性,将面板内的控件填充到对应的位置
Grid(网格)
网格 子控件被放在一个一个实现定义好的小格子里面,整齐配列。 Grid 和其他各个 Panel 比较起来,功能最多也最为复杂。
要使用 Grid,首先要向
RowDefinitions和ColumnDefinitions属性中添加一定数量的RowDefinitions和ColumnDefinitions元素,从而定义行数和列数。而放置在 Grid 面板中的控件元素都必须显示采用附加属性语法定义其 放置所在的行和列,都是以 0 为基准的整型值,如果没有显式设置任何行或列,Grid 将会隐式地将控件加入在第 0 行第 0 列。Grid 的单元格可以是空的,一个单元格中可以有多个元素,而在单元格中元素是根据它们的 Z 顺序一个接着一个呈现的。与 Canvas 一样,同一个单元格中 的子元素不会与其他元素交互布局,信息——它们仅仅是重叠而已
- Grid.IsSharedSizeScope:共享尺寸
- ShowGridLines:显示边框线
- UseLayoutRounding:抗锯齿
- SharedSizeGroup:设置一个组名,共组共享尺寸
UniformGrid(均分布局)
<UniformGrid Columns="4" FirstColumn="3" Rows="4">
<Button Content="1" />
<Button Content="2" />
<Button Content="3" />
<Button Content="4" />
<Button Content="5" />
<Button Content="6" />
<Button Content="7" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="1" />
</UniformGrid>ViewBox
这个控件通常和其他控件结合起来使用,是 WPF 中非常有用的控件。定义一个内容容器。拉伸或延展位于其中的组件,以填满可用空间,使之有更好的布局及视觉效果。
一个 Viewbox 中只能放一个控件。如果多添加了一个控件就会报错.
- Child:获取或设置一个 ViewBox 元素的单一子元素。
- Stretch:获取或设置拉伸模式以决定该组件中的内容以怎样的形式填充该组件的已有空间。
- None:内容保持其原始大小。
- Fill:调整内容的大小以填充目标尺寸。 不保留纵横比。
- Uniform:在保留内容原有纵横比的同时调整内容的大小,以适合目标尺寸。
- UniformToFill:在保留内容原有纵横比的同时调整内容的大小,以填充目标尺寸。 如果目标矩形的纵横比不同于源矩形的纵横比,则对源内容进行剪裁以适合目标尺寸。
Border
用于绘制边框与背景。只能有一个子控件,若要显示多个子控件,可以将子控件放置在 Panel 控件中。
- Background:用一个 Brush 对象来绘制背景
- BorderBrush:用一个 Brush 对象来绘制边框
- BorderThickness:此属性设置 Border 边框的大小
- CornerRadius:此属性设置 Border 的每一个角圆的半径
- Padding:此 r 属性设置 Border 里的内容与边框的之间的间隔
ScrollViewer
滚动面板
ContentPresenter
ContentPresenter 用于显示内容,默认绑定到 ContentControl 的 Content 属性。基本上所有 ContentControl 中都包含一个 ContentPresenter。ContentPresenter 直接从 FrameworkElement 派生。
TemplateBinding
用于单向绑定 ControlTemplate 所在控件的功能属性,例如 Margin="{TemplateBinding Padding}"几乎等效于 Margin="{Binding Margin,RelativeSource={RelativeSource Mode=TemplatedParent},Mode=OneWay}",相当于一种简化的写法。但它们之间有如下不同:
- TemplateBinding 只能用在 ControlTemplate 中。
- TemplateBinding 的源和目标属性都必须是依赖属性。
- TemplateBinding 不能使用 TypeConverter,所以源属性和目标属性必须为相同的数据类型。
通常在 ContentPresenter 上使用 TemplateBinding 的属性不会太多,因为很大一部分 Control 的属性的值都可继承,即默认使用 VisualTree 上父节点所设置的属性值,譬如字体属性(如 FontSize、FontFamily)、DataContext 等。
除了可继承值的属性,需要适当地将 ControlTemplate 中的元素属性绑定到所属控件的属性,例如Margin="{TemplateBinding Padding}",这样可以方便控件的使用者通过属性调整 UI。
FontWeight 字重
| 名称(字符串) | 数值(整型) | 说明 |
|---|---|---|
Thin | 100 | 极细 |
ExtraLight | 200 | 特细 |
UltraLight | 200 | 同 ExtraLight |
Light | 300 | 细体 |
Normal | 400 | 正常(默认) |
Regular | 400 | 同 Normal |
Medium | 500 | 中等 |
DemiBold | 600 | 半粗 |
SemiBold | 600 | 同 DemiBold |
Bold | 700 | 粗体(最常用) |
ExtraBold | 800 | 特粗 |
UltraBold | 800 | 同 ExtraBold |
Black | 900 | 极粗 |
Heavy | 900 | 同 Black |
ExtraBlack | 950 | 超极粗(部分字体支持) |
UltraBlack | 950 | 同 ExtraBlack |
图形 Shape
Shape是一个抽象基类,它不能被实例化,所以我们在使用时只能实例化它的子类。而Shape的父类是FrameworkElement,所以,所有的Shape子类都是一个UIElement 类,因此形状对象可以用在面板和大多数控件中。 由于 Canvas 面板支持其子对象的绝对位置,因此特别适合创建复杂的图形。
| 控件 | 说明 | 属性 |
|---|---|---|
| Ellipse | 椭圆形 | Fill:填充内部BrushStroke:边框BrushStrokeThickness:边框粗细 |
| Line | 在两个点之间绘制直线。 | X1,Y1:第一个点坐标X2,Y2:第二个点坐标StrokeStartLineCap:线头是否圆角等Stroke:线条颜色。StrokeThickness:线条宽度。StrokeDashArray:设置虚线。StrokeDashOffset:虚线位置偏移量。 |
| Rectangle | 绘制矩形。 | RadiusX、RadiusY:表示设置矩形的圆角,可以画圆 |
| Polyline | 折线 绘制一系列相互连接的直线。 | Points:线段相连的点集合 |
| Polygon | 绘制多边形,它是由一系列相互连接的线条构成的闭合形状。 | Points:线段相连的点,只是首位点会相连闭合 |
| Path | 绘制一系列相互连接的直线和曲线。 | Geometry: LineGeometry:直线几何 RectangleGeometry:矩形几何 EllipseGeometry:椭圆几何 PathGeometry:路径几何 StreamGeometry:PathGeometry的轻量级替代品,不支持 Bidning、动画等功能 CombinedGeometry:多图形组合,形成单一几何几何图形 GeometryGroup:多图形组合,形成几何图形组 |
效果/阴影
BlurEffect 模糊
| 属性名称 | 说明 | 例子 |
|---|---|---|
| Radius | 获取或设置模糊效果的曲线的半径。默认值为5 | Radius="5" |
| KernelType | 获取或设置计算变得模糊的曲线 | |
| RenderingBias | 获取或设置是否呈现效果时注重速度还是质量 |
DropShadowEffect 阴影
| 属性名 | 功能说明 | 例子 |
|---|---|---|
| Color | 阴影效果背景色 | Color="Red" |
| ShadowDepth | 阴影的偏移度 | ShadowDepth="5" |
| Direction | 阴影的角度 | Direction="-45" |
| BlurRadius | 阴影模糊程度 | BlurRadius="20" |
| Opacity | 阴影透明度 | Opacity="1" |
ShadeEffect
<DropShadowEffect BlurRadius="11" ShadowDepth="3" Opacity="0.9" Color="Black" />
BlurRadius="11":这个属性定义了阴影的模糊半径。数值越大,阴影的边缘就越模糊,产生一种更柔和的阴影效果。BlurRadius的值是一个非负浮点数,通常范围在 0 到几十之间。BlurRadius设置为 11,表示阴影的边缘会有一定的模糊效果,但不会很突出。ShadowDepth="3":这个属性定义了阴影的深度,即阴影与原始对象之间的距离。数值越大,阴影看起来就越远离原始对象,产生一种更立体的效果。ShadowDepth的值也是一个非负浮点数。ShadowDepth设置为 3,这意味着阴影会稍微偏离原始对象,给人一种轻微的立体效果。
变换
抽象类Transform,定义了实现二维平面中的转换的功能。它包括旋转 (RotateTransform)、缩放 (ScaleTransform)、倾斜 (SkewTransform) 和平移 (TranslateTransform)4个子类
TranslateTransform
水平或垂直平移
RotateTransform
旋转,按照选择的点旋转; 对应有LayoutTransform填充式,被旋转的元素会挤开空间
- Angle:旋转角度
- CenterX,CenterY:旋转中心点
- RenderTransformOrigin:旋转相对点位0.5,0.5,可替代CenterXY;
ScaleTransform
放大或缩小
SkewTransform
按照点倾斜角度,可以把正方形变成平行四边形
透明
Opacity
0-1,0完全透明
- 画刷透明 背景透明
父容器中使用画刷来定义透明度不会影响子元素透明度的显示
<StackPanel>
<StackPanel.Background>
<SolidColorBrush Opacity=".2" Color="AliceBlue" />
</StackPanel.Background>
<Button Width="100"
Height="35"
Margin="22"
Content="11111" />
</StackPanel>OpacityMask
掩码透明
<Grid Margin= 10,50 >
<Button Background="Purple" FontSize="14" FontWeight="Bold”>
<Button.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1”> <!--线性渐变画刷 从上到下完全透明-->
<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Button.Opacitylask>
<Button.Content>A PartK</Button. Content>
</Button>
</Grid>透明度结合变换例:透明度结合变换
动画
线性插值动画
平滑的动画 从开始到结束点
- DoubleAnimation
数据绑定
数据绑定必须具有绑定目标与绑定源。 绑定目标是任意继承自 DependencyProperty 的属性或控件。
注
控件的属性一定是依赖属性。
ViewModel的属性一定要实现属性通知。
控件自带媒婆——Binding。
媒婆只能在控件的DataContext(数据上下文)中找数据源。
Binding Model
- 使用
{Binding ...}实现界面控件属性与后台数据的绑定- 源对象与目标对象
- 数据流向
Model - 值转换器
Converter
- Model 具体属性
| 成员名称 | 说明 |
|---|---|
| Default | 使用绑定目标的默认 Mode 值。 每个依赖项属性的默认值都不同。用户可编辑控件属性(例如文本框和复选框的属性)默认为双向绑定,而多数其他属性默认为单向绑定。 |
| OneTime | 当应用程序启动或数据上下文更改时,更新绑定目标。 此绑定类型适用于以下情况:使用当前状态的快照适合使用的或数据状态实际为静态的数据。 如果要从源属性初始化具有某个值的目标属性,并且事先不知道数据上下文,则也可以使用此绑定类型。 此绑定类型实质上是 OneWay 绑定的简化形式,在源值不更改的情况下可以提供更好的性能。 |
| OneWay | 单向,当绑定源(源)更改时,更新绑定目标(目标)属性。 |
| OneWayToSource | 当目标属性更改时更新源属性。 |
| TwoWay | 导致对源属性或目标属性的更改可自动更新对方。 此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案。 |
也可通过配置器配置触发,通过 UpdateSourceTrigger 属性实现,具体有如下几种值
| 成员名称 | 说明 |
|---|---|
| Default | 绑定目标属性的默认 UpdateSourceTrigger 值。 大多数依赖项属性的默认值都为 PropertyChanged,而Text 属性的默认值为 LostFocus。 确定依赖项属性的默认 UpdateSourceTrigger 值的编程方法是使用 GetMetadata 来获取属性的属性元数据,然后检查 DefaultUpdateSourceTrigger 属性的值。 |
| Explicit | 仅在调用 UpdateSource 方法时更新绑定源。 |
| LostFocus | 当绑定目标元素失去焦点时,更新绑定源。 |
| PropertyChanged | 当绑定目标属性更改时,立即更新绑定源。 |
eg:<TextBox Name="itemNameTextBox" Text="{Binding Path=ItemName, UpdateSourceTrigger=Explicit}" />
简单绑定:
<ListBox x:Name="listStockName" Width="248" Height="56">
<ListBoxItem Content="11"/>
<ListBoxItem Content="22"/>
<ListBoxItem Content="33"/>
<ListBoxItem Content="44"/>
<ListBoxItem Content="55"/>
<ListBoxItem Content="66"/>
<ListBoxItem Content="77"/>
</ListBox>
<TextBlock Width="248" Height="24" Text="{Binding ElementName=listStockName, Path=SelectedItem.Content}">//txt值绑定,ElementName指向另一个控件的名称,Binding的值需要是另一个控件的属性,比如textbox的text属性
<TextBox hc:TitleElement.Title="Count" hc:TitleElement.TitlePlacement="Left" Style="{StaticResource TextBoxExtend}" Text="{Binding Text,ElementName=countt}" />
<TextBox hc:TitleElement.Title="数量:" x:Name="countt" hc:TitleElement.TitlePlacement="Left" Style="{StaticResource TextBoxExtend}" Text="{Binding MyTableInputs.Count}" Margin="10"/>RelativeSource
- Mode
- FindAncestor 寻找对于父级
- PreviousData:绑定到列表的前一个项
- Self 自身的其他属性
- TemplateParent:控件或数据模板内
内容模型
ContentControl
可包含任意类型的内容,但只能包含一个子元素做其内容,但是可以放容器。 Button、ButtonBase、CheckBox、ComboBoxItem、ContentControl、Frame、GridViewColumnHeader、GroupItem、Label、ListBoxItem、ListViewItem、NavigationWindow、RadioButton、RepeatButton、ScrollViewer、StatusBarItem、ToggleButton、ToolTip、UserControl、Window 都继承与 ContentControl
<!--按钮有Button.Content,里面放置内容-->
<Button Grid.Column="0" Grid.Row="0">
<Button.Content>
<TextBlock FontSize="32">我是一个有Content按钮</TextBlock>
</Button.Content>
</Button>
<!--省略Button.Content-->
<Button Grid.Column="0" Grid.Row="1">
<TextBlock FontSize="32">我是一个省略Content按钮</TextBlock>
</Button>
<!--放置一个人容器放置多个内容-->
<Button Grid.Column="0" Grid.Row="2">
<!--<Button.Content>-->
<StackPanel>
<TextBlock FontSize="32">我是一个文本</TextBlock>
<Image Source="1.png" Height="80"></Image>
<Label FontSize="32">我是一个Label</Label>
</StackPanel>
<!--</Button.Content>-->
</Button>HeaderedContentControl
继承自 ContentControll 类,表示带有 Header 的 ContentControl。 继承 HeaderedContentControl 的有:Expander、GroupBox、TabItem。
<!--TabControl 的TabItem里设置标头-->
<TabControl>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Ellipse Width="10" Height="10" Fill="Red"/>
<TextBlock>我是一个有标头的tab</TextBlock>
</StackPanel>
</TabItem.Header>
<!--TabItem.Content 可以省略-->
<!--<TabItem.Content>-->
<StackPanel>
<TextBlock>这里是tab 的内容</TextBlock>
<Button Content="这里放置了一个按钮"></Button>
</StackPanel>
<!--</TabItem.Content>-->
</TabItem>
</TabControl>
<!--GroupBox 设置标头-->
<GroupBox Grid.Row="1" Grid.Column="0">
<GroupBox.Header>
<TextBlock>这里是GroupBox 的标头</TextBlock>
<!--多个控件,外面需要包裹一个容器-->
<!--<StackPanel Orientation="Horizontal">
<Ellipse Width="10" Height="10" Fill="Red"/>
<TextBlock>这里是GroupBox 的标头</TextBlock>
</StackPanel>-->
</GroupBox.Header>
<!--GroupBox.Content 可以省略-->
<!--<GroupBox.Content>-->
<StackPanel>
<TextBlock>这里是GroupBox的内容</TextBlock>
</StackPanel>
<!--</TabItem.Content>-->
</GroupBox>ItemsControl
继承自 Control,可以包含多个项。 内容属性为 Items 和 ItemsSource。 继承 ItemsControl 的有:Menu、MenuBase、ContextMenu、ComboBox、ItemsControl、ListBox、ListView、TabControl、TreeView、Selector、StatusBar
<!--使用ItemsSource填充-->
<ListBox x:Name="listBoxs" ItemsSource=".."></ListBox>
<!--使用Items填充-->
<ListBox Grid.Row="0" Grid.Column="0">
<ListBox.Items>
<ListBoxItem>
<TextBlock>我是ListBoxItem第1行</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock>我是ListBoxItem第2行</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock>我是ListBoxItem第3行</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock>我是ListBoxItem第4行</TextBlock>
</ListBoxItem>
</ListBox.Items>
</ListBox>HeaderedItemsControl
继承自 ItemsControl,多一个标题,一半树形使用 继承的控件:MenuItem、ToolBar、TreeViewItem
<!--每个TreeViewItem都有一个头和一个Items集合-->
<TreeView>
<TreeView.Items>
<TreeViewItem IsExpanded="True">
<TreeViewItem.Header>
<TextBlock Text="树的根节点1" />
</TreeViewItem.Header>
<TreeViewItem.Items>
<TextBlock Text="树的节点1- 1" />
<TreeViewItem IsExpanded="True">
<TreeViewItem.Header>
<TextBlock Text="树的节点1- 2" />
</TreeViewItem.Header>
<TreeViewItem.Items>
<TextBlock Text="树的节点1- 2 - 1" />
<TextBlock Text="树的节点1- 2 - 2" />
<TextBlock Text="树的节点1- 2 - 3" />
</TreeViewItem.Items>
</TreeViewItem>
</TreeViewItem.Items>
</TreeViewItem>
<TreeViewItem IsExpanded="True">
<TreeViewItem.Header>
<TextBlock Text="树的节点2" />
</TreeViewItem.Header>
<TreeViewItem.Items>
<TreeViewItem IsExpanded="True">
<TreeViewItem.Header>
<TextBlock Text="树的节点2- 1" />
</TreeViewItem.Header>
<TreeViewItem.Items>
<TextBlock Text="树的节点21 - 1" />
<TextBlock Text="树的节点2 1 - 2" />
<TextBlock Text="树的节点2 - 3" />
</TreeViewItem.Items>
</TreeViewItem>
</TreeViewItem.Items>
</TreeViewItem>
</TreeView.Items>
</TreeView>模板
ControlTemplate(控件模板)
最常见的模板,用于自定义控件的外观
<Button Content="Click me!">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="Red" BorderBrush="Black" BorderThickness="2">
<TextBlock Text="{TemplateBinding Content}" Foreground="White" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>DataTemplate(数据模板)
用于定义数据对象的展示
<ListBox ItemsSource="{Binding Customers}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>Hierarchic(层次结构数据模板)
显示具有层级结构的数据控件,tree 等
<TreeView ItemsSource="{Binding Departments}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Employees}">
<TextBlock Text="{Binding Name}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding EmployeeName}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>模型与模板的结合
| 属性 | 控制的对象层级 | 主要作用 | 典型设置类型 | 最常见的修改内容 | 是否必须配合 ItemsSource 使用 | 备注 / 容易混淆点 |
|---|---|---|---|---|---|---|
| ItemsSource | 数据源 | 提供要显示的数据集合 | IEnumerable / ICollectionView | ObservableCollection、List、数组等 | — | 这是“内容从哪里来”的起点。没有它,后面三个属性基本没意义(除非手动加 Items) |
| ItemsPanel | 所有 Item 的布局容器 | 决定所有数据项如何排列(横向?换行?网格? Canvas 自由定位?) | ItemsPanelTemplate | StackPanel、WrapPanel、UniformGrid、Canvas、Grid、VirtualizingStackPanel 等 | 通常需要 | 它只管摆放位置和排列方式,不管每个 Item 长什么样 |
| ItemTemplate | 每个数据项的内容呈现 | 决定数据对象如何被渲染成 UI 元素 | DataTemplate | Border + StackPanel + TextBlock/Image/Button 等组合 | 通常需要 | 这是“数据 → 视觉”的核心转换器。最常绑 |
| ItemContainerStyle | 每个数据项的容器 | 控制包裹每个 Item 的外层容器的样式和行为 | Style(TargetType 通常是 ContentPresenter 或 XXXItem) | Margin、Padding、Background、Width/Height、Template、触发器(如 IsSelected、IsMouseOver) | 通常需要 | ItemsControl 默认容器是 ContentPresenter;ListBox 是 ListBoxItem;ListView 是 ListViewItem |
“源 → 板 → 模板 → 容器”
- ItemsSource → 数据源头
- ItemsPanel → 摆放的板子(Panel)
- ItemTemplate → 每个数据的模样(内容模板)
- ItemContainerStyle → 每个数据外面包的容器样式
<ItemsControl ItemsSource="{Binding Students}">
<!-- 1. 最外层:整个控件的模板(极少改) -->
<ItemsControl.Template>
<ControlTemplate>
<Border BorderBrush="Gray" BorderThickness="1">
<ScrollViewer>
<ItemsPresenter /> <!-- 这里是关键:ItemsPanel 会出现在这里 -->
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<!-- 2. 决定所有项怎么排列 -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
<!-- 或: <UniformGrid Columns="4"/> -->
<!-- 或: <Canvas /> -->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- 3. 每个数据的“长相” -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#F0F8FF" CornerRadius="6" Padding="8" Margin="4">
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Age, StringFormat='{}年龄:{0}岁'}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- 4. 每个项外层容器的样式(最常用来调间距、选中效果) -->
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Margin" Value="4" />
<!-- 或者给 ListBox/ListView 用 -->
<!-- <Style TargetType="ListBoxItem"> ... -->
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>触发器
作为定义和管理 XAML 资源的触发器,根据不同的条件或事件来改变控件属性的行为。
- 类型
- 基本触发器(
Trigger):根据控件本身的依赖属性值来触发,例如鼠标移到按钮改变背景颜色 - 数据触发器(
DataTrigger):根据绑定的数据值来触发,例如绑定的数据等于 1 时显示图片 - 事件触发器(EventTrigger):根据控件路由事件触发,例如按钮点击播放动画
- 多条件触发(MultiTrigger、MultiDataTrigger):根据多个组合条件触发,例如控件属性和绑定的值同时满足时改变控件样式
- 基本触发器(
Converter 引入
可以使用
IValueConverter接口来实现自定义的值转换器,用于将一个值转换为另一个值。
//1. 第一种方式 需要声明资源
//xaml
xmlns:converters="clr-namespace:AutoImageAnalysis.WPF.UI.Converter"
//UserControl与window 使用对应的
<UserControl.Resources>
<converters:DateTimeFormatConverter x:Key="DateTimeFormatConverter"/>
</UserControl.Resources>
<DataGridTextColumn Binding="{Binding CreateTime,Converter={StaticResource DateTimeFormatConverter}}" Width="180" Header="创建时间"/>
//cs
public class DateTimeFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DateTime dateTime) return dateTime.ToString("yyyy-MM-dd HH:mm");
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
//2.第二种 不需要声明,直接引用静态对象
//.xaml
xmlns:converters="clr-namespace:AutoImageAnalysis.WPF.UI.Converter"
<DataGridTextColumn Binding="{Binding CreateTime,Converter={x:Static converters:DateTimeFormatConverter.Instance}}" Width="180" Header="创建时间"/>
//cs
public class DateTimeFormatConverter : IValueConverter
{
//重点是这个静态对象
public static DateTimeFormatConverter Instance = new DateTimeFormatConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DateTime dateTime) return dateTime.ToString("yyyy-MM-dd HH:mm");
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}在 DataGridTextColumn 的 Foreground 属性绑定中,无法找到相应的 FrameworkElement 或 FrameworkContentElement 作为绑定的目标元素。这是因为 DataGridTextColumn 是一个列定义对象,而不是一个可视化元素(Visual Element)。绑定的目标属性必须是可视化元素的属性才能正确工作。可以尝试使用 DataGridTemplateColumn 代替 DataGridTextColumn,并在其中定义一个具有可视化元素的 CellTemplate。然后,在 CellTemplate 中,将要绑定的属性设置为可视化元素的属性,以便使用转换器设置前景颜色。
StaticResource DynamicResource 的区别
StaticResource在编译时解析和获取资源,是一种静态的引用方式。DynamicResource在运行时解析和获取资源,是一种动态的引用方式,可以响应运行时的更改。通常情况下,当你需要引用不会在运行时更改的资源时,可以使用
StaticResource。而当你需要引用可能会在运行时更改的资源时,例如主题更改或动态资源的修改,可以使用DynamicResource。 请注意,DynamicResource的使用会带来一些性能开销,因为它需要在运行时进行资源解析和更新。因此,如果你知道资源在运行时不会更改,请使用StaticResource来获得更好的性能。
Auto 与 * 的区别
Height="Auto"表示该行的高度将根据其内容自动调整。也就是说,行的高度将根据所包含的元素的大小来确定,以确保内容适合该行。如果行中有多个元素,它们将按照它们的大小进行布局,并且行的高度将根据最大元素的高度进行调整。Height="*"表示该行的高度将根据剩余可用空间进行分配。它告诉Grid将剩余空间平均分配给使用*作为高度的行。如果有多个*行,它们将按比例平均分配剩余的空间。
Marin 与 Alignment 的冲突
在设置了Alignment后,marin的某些值会失效
| 控件所处容器 | Alignment 属性 | Margin 中哪个值彻底失效(被忽略) | 实际生效的 Margin 值 | 典型错误写法 |
|---|---|---|---|---|
| Grid / StackPanel / DockPanel 等 | HorizontalAlignment="Left" | Right | Left / Top / Bottom | Margin="0,0,50,0" → Right 的 50 完全没用 |
HorizontalAlignment="Right" | Left | Right / Top / Bottom | Margin="-80,0,0,0" → Left 的 -80 被扔掉(你最常犯的) | |
HorizontalAlignment="Center" | Left 和 Right 都失效 | 只有 Top / Bottom | Margin="30,0,50,0" → 左右全变 0 | |
VerticalAlignment="Top" | Bottom | Top / Left / Right | Margin="0,0,0,50" → Bottom 50 无效 | |
VerticalAlignment="Bottom" | Top | Bottom / Left / Right | Margin="0,50,0,0" → Top 50 无效 | |
VerticalAlignment="Center" | Top 和 Bottom 都失效 | 只有 Left / Right | Margin="0,30,0,50" → 上下全变 0 | |
| Canvas | 任何 Alignment 都不起作用 | 无(全都不失效) | Left/Top/Right/Bottom 全生效 | Canvas 里永远不用管 Alignment |
| StackPanel(水平) | VerticalAlignment | Top 和 Bottom 都可能失效(取决于 Orientation) | 看具体情况 | 水平 StackPanel 里 VerticalAlignment=Center 时上下 Margin 无效 |
| TextBlock / Button 等 Inline 控件 | TextAlignment | 不影响 Margin | Margin 永远全生效 | 注意:TextAlignment 只影响文字,不影响外边距 |
依赖属性
PropertyChangedCallback 是一个委托,表示在依赖属性的有效属性值更改时调用的回调。也就是说,当我们修改了某个依赖属性的值后,还希望立即做一些事情,那就在注册(定义)一个依赖属性时,将一个方法名通过 PropertyMetadata 构造函数注入,一并注册到依赖属性系统当中。
- DefaultValue 属性:表示依赖属性的默认值。
- PropertyChangedCallback 属性:一个回调委托对象。当依赖属性值发现改变时触发执行。
- CoerceValueCallback 属性:一个回调委托对象。表示强制转换时执行的业务逻辑,它会先于- PropertyChangedCallback 委托触发。
