CH05 内容控件 (Content Control)
内容控件用于展示用户需要看到的内容, 是一般不会被修改的内容.
作者描述了控件的分类, 可以分为内容控件, 布局控件, 用户交互控件. 并对其进行了说明. 然后作者将
ListView
和TreeView
定位为内容控件.
每一个控件都含有大量的事件与属性, 这里主要介绍常用的.
然后作者简要说明了一下属性, 方法, 与事件, 为没基础的读者进行说明. 例如:
<Button Name="btnClear" Content="Button" Click="btnClear_Click" />
WPF 包含大量对象, 但本书不会介绍所有, 有些控件对象并非直接使用. 例如下面, 通过 ToolTip
属性来设置 ToolTip
对象:
<TextBox ToolTip="自定义 ZIP 编码" />
如果要使用独立的 ToolTip
对象, 代码应该是:
<TextBox>
<TextBox.ToolTip>
<ToolTip>
自定义 ZIP 编码
</ToolTip>
</TextBox.ToolTip>
</TextBox>
如深入浅出里面介绍的, 简介利于阅读的 XAML 代码更合适. 比较推崇第一种语法.
这里的 ZIP 是邮政编码的含义
控件概览
下面表格列举的控件的作用:
控件 | 作用 |
---|---|
Border | 为单个子控件绘制一个边框. |
BulletDecorator | 在列表前显示标号. |
DocumentViewer | 用于显示 DixedDocument 的内容, 例如 XPS 文件. |
FlowDocumentPageViewer | 使用 viewing mode 在页面中展示 FlowDocument . |
FlowDocumentReader | 使用 Page, Scroll, TwoPage mode 显示 FlowDocument . |
FlowDocumentScrollViewer | 使用 Scroll modal 显示 FlowDocument . |
GroupBox | 给单个子元素添加一个标题与边框. |
Image | 像是一张图片. |
Label | 显示一段用户不能修改的文本. |
ListView | 显示一组数据, 在一组布局中. |
MediaElement | 播放音频或视频. |
Popup | 展示一个浮动区域, 例如 ContentMenu 或 ToolTip . |
ProgressBar | 展示进度信息. |
Separator | 在菜单中展示一条水平线. |
TextBlock | 展示一段只读的文本, 相比与 Label , 它可以带有简单格式. |
ToolTip | 鼠标悬浮时弹出提示. |
TreeView | 使用树结构显示组织架构. |
下面讨论这些控件. 将其分为三类: 图形, 文本, 空间控件.
图形控件
图形控件就是用来显示图形, 包括线, 形状, 图片, 以及多媒体输出等.
通常在设计时这些控件就已设置, 不需要在运行时进行修改, 也不需要用用户交互.
Image
该控件用于展示图片, 使用也很简单
- 首先打开项目, 右键添加现有项.
- 选择图片, 然后添加.
- 然后设置
Image
控件的Source
属性即可.
Image
有两个比较重要的属性: Stretch
和 StretchDirection
.
Stretch
控制是否缩放, 取值有:
None
不进行缩放, 原始尺寸输出.Fill
拉伸图片, 可能会变形.Uniform
图片宽高同时被拉伸, 并尽可能大的填满控件.UniformToFill
图片宽高被拉伸. 但宽高尺寸与控件不同, 可能会显示不完整.
StretchDirection
控制缩放方向, 取值有:
UpOnly
只允许放大DownOnly
只允许缩小Both
可同时缩小与放大
使用语法示例:
<Image
Width="Auto"
Height="Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Source="/1690436791750269.png"
Stretch="Uniform" StretchDirection="Both"
/>
MediaElement
用于播放媒体音乐与视频. 设计时, 其最重要的属性是 Source
, 用于指定需要播放的文件.
默认情况下, MediaElement
加载文件后就会立即播放. 如果要手动控制, 设置 LoadedBehavior
属性为 Manual
. 然后再给控件设置 x:Name
, 然后通过调用方法来控制播放暂停等操作.
可以使用的方法有: Play()
, Pause()
.
播放进度可以通过读写 Position
属性来实现, 例如: mm.Position = new TimeSpan(0)
.
MediaElement
没有足够的属性来通过 XAML 来控制播放, 需要使用后台代码来实现.
但 XAML 中有一个 SoundPlayerAction
的命令, 可以用来播放音频文件. 可以通过触发器来调用该命令. 例如, 点击 btnSoundPlayerAction
时触发:
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click"
SourceName="btnSoundPlayerAction">
<SoundPlayerAction Source="xxx.wav"/>
</EventTrigger>
</Window.Triggers>
文本控件
文本控件主要用于显示文本.
FlowDocument
和 FixedDocument
除了可以显示文本, 还可以显示其他内容. 但主要还是为了排版文本. 这里作者将其归类为文本控件.
DocumentViewer
FlowDocument
可以根据运行时容器的调整实时的重新排列内容.
相反 DocumentViewer
是固定的, 不会重拍内容, 就像 PDF 一样.
下面的代码创建了 FixedDocument
对象, 并包含两页的内容. 为了节省空间, 省略了不重要的代码.
jk: 代码似乎没跑起来, 不明确的错误. 代码结构是
DocumentViewer
中首先设置了背景为线性渐变. 然后DocumentViewer
中包含一个FixedDocument
, 在FixedDocument
中又包含了两个PageContent
, 即为两页内容. 在PageContent
中又包含了FixedPage
.
FlowDocument
该控件用于展示文档, 其内可以包含:
- Paragraphs 段落.
- Tables 表格.
- Lists 列表.
- Floaters 浮动模块.
- Figures 图片.
- 用户交互控件, 例如按钮, 文本框等.
- 3D 对象.
更多细节在 Ch21 会介绍. 下面介绍 FlowDocument
的不同样式. 如果将其放在 Window
中, 就好像使用了 FlowDocumentReader
一样.
FlowDocumentPageViewer (略)
FlowDocumentPageViewer
以页模式展示 FlowDocument
. 该模式下仅显示一页的内容. 控件下方可以实现切换.
页面大小由 PageHeight
和 PageWidth
决定.
FlowDocumentReader (略)
FlowDocumentReader
使用三种模式来查看页面, 分别是 Page, Scroll, 和 TwoPage.
FlowDocumentScrollViewer (略)
Label
用于显示不可变文本, 使用 Content
来设置内容. 可以设置前景背景色, 字体字号等.
Label
显示的文本不可更改也不允许获得用户焦点, 所以无法进行复制等操作, 如果需要不可改, 但要可以复制选中, 可以使用TextBox
, 然后设置其只读 (IsReadOnly
) 即可.
Pop-Up
Popup
会在窗体显示一个浮动的区域. 类似于上下文菜单或 ToolTip
, 不同的是它不会自动显示与隐藏, 需要通过编码来控制.
一般添加鼠标进入 (MouseEnter
) 与鼠标离开 (MouseLeave
) 事件, 在事件中设置 IsOpen
属性 true
或 false
来控制显示与隐藏.
下面是控制显示位置的属性
属性 | 含义 |
---|---|
PlacementTarget | 弹出窗基于该控件进行定位, 如果省略, 则基于容器定位. 其 XAML 语法类似于 PlacementTarget="{Binding ElementName=xxx}" . |
Placement | 定位方式. 例如停靠对齐等. |
PlacementRectangle | 如果有该属性, 会在定位目标中确认一个矩形, 基于该矩形进行定位. |
HorizontalOffset | 水平偏移量 |
VerticalOffset | 垂直偏移量 |
弹出位置由 Placement
属性来控制, 其取值可以是:
值 | 定位 |
---|---|
Absolute | 基于平面左上角. |
Relative | 基于 PlacementTarget 左上角. |
Bottom | |
Center | |
Right | |
Left | |
Top | |
AbsolutePoint | 基于屏幕左上角, 由 *Offset 来定位. |
Mouse | 基于鼠标位置定位. |
MousePoint | 基于鼠标位置, 由 *Offset 来定位. |
Custom | 由 CustomPopupPlacementCallback 决定. |
jk:
Absolute
没有实验出来
默认情况下, 除非设置 IsOpen=false
, 否则弹出窗会一直显示. 可以将 StaysOpen
设置为 false
, 它便会在失焦等操作后自动隐藏.
最后是动画:
AllowsTransparency
是否启用动画PopupAnimation
动画方式.None
不使用动画,Scroll
从一个角滚动显示,Slide
从上至下卷帘门显示,Fade
淡入淡出的显示.
TextBlock
用于显示只读的文本内容. 相比于 Label
, TextBlock
更为灵活:
- 可以设置文本样式, 例如粗体, 斜体, 或下划线等.
- 添加折行
- 修改间距
- 折叠文本
- 截断文本, 并显示省略号
- 添加装饰, 如中划线, 下划线, 上划线等
- 使用上下标等
一些常用属性有:
属性 | 含义 |
---|---|
LineHeight | 表示两线之间的距离 |
LineStackingStrategy | 可以是 BlockLineHeight (行高由 LineHeight 决定) 或 MaxHeight (每行的高度设置为适合其内容) |
TextTrimming | 表示如何处理不合适的单词. None 单词被截断; WordEllipsis 在单词边界处截断, 显示省略号; CharacterEllipsis 在句子边界处截断, 显示省略. |
TextWrapping | 确定文本框的末尾是否换行. 可取值: NoWrap , Wrap , WrapWithOverflow . 过长时, WrapWithOverflow 允许单词超出 TextBlock 外, 然后截断. |
TextBlock
内可以包含内联标签:
标签 | 含义 |
---|---|
Bold | 粗体. |
Hyperlink | 超链接. 可点击. |
InlineUIContainer | 包含其他控件. |
Italic | 斜体. |
LineBreak | 换行. |
Run | 包含一段文本. 有自己独立的字体属性. 逻辑上类似于 HTML 中的 span. |
Span | 一组内联元素的组. 类似于 Run. 感觉逻辑上与 html 中的 div 类似. |
Underline | 下划线. |
有一种写 HTML 的感觉.
<TextBlock>
TextBlock 比 Label <Italic>强大</Italic>的多.
</TextBlock>
TextBlock
控件中也可以包含其他控件, 与文本流混排.
ToolTip
在另一个控件上弹出一个层显示信息. 一般的用法是给控件设置 ToolTip
属性.
也可以构建
ToolTip
控件来实现.
空间控件
Border
用于显示边框或背景.
一个常见的用法是, 当做分组来使用, 给页面中的一组按钮等控件分组.
jk: 逻辑上与
GroupBox
有点像.
该组件是单子组件的, 但可以包含 Grid
等组件作为子组件使用. 其常见属性:
属性 | 含义 |
---|---|
Background | 设置背景 Brush . |
BorderBrush | 用于绘制边界的 Brush . |
BorderThickness | 用于设置四周的边框粗细. |
CornerRadius | 用于设置四周圆角半径. 0 则表示矩形. |
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
Width="150" Height="100" CornerRadius="20"
BorderBrush="#FFFF8000" BorderThickness="5">
<Border.BitmapEffect>
<DropShadowBitmapEffect/>
</Border.BitmapEffect>
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF00FF00" Offset="0"/>
<GradientStop Color="#FF004000" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<TextBlock Width="100" Height="60" FontWeight="Bold"
TextWrapping="Wrap"
TextAlignment="Center"
Text="This TextBlock is contained inside the Border."/>
</Border>
会得到:
BulletDecorator
该控件在列表项前显示引导点. 它是单子节点的组件.
其重要的属性是 Bullet
, 理论上可以包含任何内容.
<StackPanel>
<BulletDecorator Height="25">
<BulletDecorator.Bullet>
<Image Source="/1690436791750269.png" Height="20"></Image>
</BulletDecorator.Bullet>
<Label>一项</Label>
</BulletDecorator>
<BulletDecorator Height="25">
<BulletDecorator.Bullet>
<Image Source="/1690436791750269.png" Height="20"></Image>
</BulletDecorator.Bullet>
<Label>二项</Label>
</BulletDecorator>
</StackPanel>
有种像 HTML 中的 UL-LI 标签. 但它只是定义了一项, 需要多个组合使用.
GroupBox
常用属性有:
Header
头部显示的文字BorderBrush
边框颜色Background
描述背景颜色Foreground
头部文字颜色
可以设置边框粗细 (BorderThickness
) 为 0
来隐藏边框. 由于某些原因, 边框粗细大于 2
时会模糊.
该组件也是单子元素组件.
ListView
ListView
用于在一种或多种布局中展示一组数据. 可以自定义布局, 通常会使用类似 Grid
的布局. 例如:
ListBox
的优势是显示来自数据源的数据, 例如数据库等.
jk: 应该是数据绑定的功能比较强大. 可以按照多个维度显示多组数据.
数据绑定在 ch18 介绍, 这里仅介绍一些基本内容, 来熟悉一下该控件.
要理解该案例, 需要在后台代码与 XAML 中来回切换. 下面的示例定义了一个 BookInfo
类. 有四个属性 Author
, Title
, Year
, 和 Price
.
public class BookInfo {
public string Author { get; set; }
public string Title { get; set; }
public string Year { get; set; }
public string Price { get; set; }
}
jk: 作者的代码里还有构造函数, 并采用了完整的属性, 字段定义.
然后使用 ObservableCollection<T>
来创建 BookInfo
的集合, 并初始化. 将该集合赋值给 ListView
的 DataContext
属性.
然后在 ListView
中需要指定什么数据要被现实出来 (就是绑定数据源的什么属性).
<ListView Name="lvwPeople" Background="{x:Null}" ItemSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="Author" Width="100" DisplayMemberBinding="{Binding Path=Author}" />
<GridViewColumn Header="Title" Width="100" DisplayMemberBinding="{Binding Path=Title}" />
<GridViewColumn Header="Year" Width="100" DisplayMemberBinding="{Binding Path=Year}" />
<GridViewColumn Header="Price" Width="100" DisplayMemberBinding="{Binding Path=Price}" />
</GridView>
</ListView.View>
</ListView>
代码中, ListView
的 ItemSource
属性设置为 ItemSource="{Binding}"
在是在说所有的属性都是利用绑定来获取的. 本例中就是说, 控件需要使用, 附加在 DataContent
属性上的, 由 ObservableCollection
实例化的集合.
ListView
的 View
属性, 代码中的 ListView.View
定义了如何显示. GridView
则是以表格的形式进行显示, GridViewColumn
定义了每一列的显示.
GridViewColumn
的属性 Header
定义了表格列的表头内容, Width
定义了初始化的列宽. 而 DisplayMemberBinding
则定义了该怎么去找需要显示的数据.
jk: 典型的入门经典的风格, 作者解释的非常详细. 这里略.
然后作者说 ListView
非常强大, 结合触发器与 XAML
可以定义多种视图, 多种交互效果. 不幸的是, 强大代表着复杂, 细节不可能在这里说清楚. 细节可以参考微软MSDN文档.
最后运行的结果类似于
ProgressBar
用于展示进度条, 如果应用中存在耗时操作, 建议使用它, 来给用户提供执行进度的提示.
其主要属性有: Minimunm
, Maximum
, 以及 Value
.
但是需要注意的是, 如果在 UI 线程执行耗时操作, UI 不会被立即刷新, 也就是说耗时操作会阻塞进度条的变化.
一种办法是使用多线程, 然后作者简要解释了一下线程. 不幸的是, 控件只允许在本线程中被修改. 所以在其他线程中无法直接修改进度条控件的属性. 大多数的处理办法是在线程代码中通过 Dispatcher
对象来调用用户线程的代码.
需要使用:
this.Dispatcher.Invoke(() => {
prb.Value = i;
});
这个确实很麻烦, 但是还有一个更简单的办法. 使用 BackgroundWorker
. 它看起来好像在后台线程与前台线程中进行必要的切换. 它会在后台线程处理任务, 然后在前台线程处理 UI 的设置.
基本用法:
- 示例化
BackgroundWorker
- 设置属性
WorkerReportsProgress
为true
. 该属性表示是否可以报告进度更新. - 注册事件:
DoWork
事件, 启动时触发;ProgressChanged
事件, 进度发生变化时触发;RunWorkerCompleted
事件, 结束时触发. - 在
DoWork
事件处理函数中进行耗时处理, 并根据需要, 调用<backgroundWorker>.ReportProgress(...)
方法, 来引发ProgressChanged
事件. - 在
ProgressChanged
事件中操作 UI 控件,ReportProgress()
方法传入的参数可以在事件参数e.ProgressPercentage
中获得到. - 在程序需要触发
BackgroundWorker
的时候调用其RunWorkAsync()
方法.
值得研究一下其参数. https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.backgroundworker?view=net-9.0
然后作者详细解释了代码的执行.
Separator
Separator
在菜单项与工具栏的按钮之间绘制一条横线或一条竖线.
菜单使用 Menu
控件, 工具栏是 ToolBar
控件.
这里可以基于 VS 的数据源的向导来学习菜单控件的用法, 然后同时观察生成的 XAML 来学习其使用.
TreeView
容器控件是 TreeView
, 成员项控件是 TreeViewItem
. 其中 TreeViewItem
展开与否使用 IsExpanded
属性.
基本用法举例:
<TreeView >
<TreeViewItem Header="树节点1" IsExpanded="True">
<TreeViewItem Header="1-1"></TreeViewItem>
<TreeViewItem Header="1-2"></TreeViewItem>
<TreeViewItem Header="1-3"></TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="树节点2">
<TreeViewItem Header="2-1"></TreeViewItem>
<TreeViewItem Header="2-2"></TreeViewItem>
<TreeViewItem Header="2-3"></TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="树节点3"></TreeViewItem>
<TreeViewItem Header="树节点4"></TreeViewItem>
<TreeViewItem Header="树节点5"></TreeViewItem>
</TreeView>
ch18 会介绍到高级用法 (数据绑定)
小结
控件可以参考: https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/controls/control-library?redirectedfrom=MSDN