双11狂欢大促

盲盒赢 定制羽绒服

开启惊喜盲盒,抽取你的专属好礼

— 惊喜奖品抢先看 —

[翻译]具有TreeView下拉控件的ComboBox

threenewbee threenewbee 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区
具有TreeView下拉控件的ComboBox



没错,如标题所说的那样,在下拉框中是一个TreeView,但是,为什么我们需要这样的控件?事实上这样的需求我已经遇到很多次了,比如适用于:

 当遇到层次结构的数据
 让用户选择树上的一个节点
 需要TreeView但是界面上缺少足够的空间
 用于不经常修改的选项
 当一个对话框看起来太笨重和突兀

在这些情况下,一个普通的ComboBox不符合要求,无论你喜欢不喜欢,它不会同时显示每一个数据条目以及它们的结构。

我会大篇幅探讨过在Windows窗体中写一个这样的控件面临的挑战以及分享围绕ToolStripDropDown控件的常见的陷阱以及最终的解决方案。

告诉你下拉框的真相

当考虑这个问题时,大多数人会立即告诉你下拉框是一个窗体。毕竟,它表现出了很多窗体具有的行为:

 它的位置可以超出父窗体客户区的范围
 能够独立处理鼠标和键盘输入
 出现在最顶层,其他窗口不能掩盖它

然而,这种观点是根本错误的。下拉框有一些非常不同于窗体的东西:

 它们打开时,父窗体的焦点不会被转移过来
 当它们捕捉鼠标/键盘输入,父窗体的焦点不会被转移过来
 同它们的子控件/项交互的时候,不切换焦点

上述的这几点代表它和窗体、控件的工作方式迥然不同,那么是如何做到能拥有这些特性呢?

错在哪里

考虑到这一点,在Windows窗体中用如下“伟大的”方式实现下拉框时会遭遇失败:

使用一个窗体(懵懂无知期)

于是,你决定用一个Form实现下拉框。你将这个窗体设置为无边框的并且在任务栏上不显示图标,也许它被设置了TopMost属性。你甚至可能会设置它的父窗体,不管其中细节的差异,都是这样:用户点击下拉按钮然后你的窗体被显示。当焦点切换到下拉框窗体时,会有一个轻微的闪烁。父窗体的标题栏会变色,窗体阴影变淡(在Aero下)。然后用户通过下拉列表控件所在的窗体做出选择,然后窗体被关闭。直到再次单击父窗体焦点改变,标题栏改变了颜色,并且窗体的阴影又变深。又会出现一次尴尬的闪烁。最后结果如何?需要额外的点击,非常差的用户体验。

还是使用一个窗体,尝试变得更聪明

那些已经尝试了以上办法(或对WinForms/Win32的问题有更深入的理解)的人会尽力解决最初的问题,那就是,下拉窗体会从父窗体抢到焦点。一个鲜有人知的秘密是,Form类有一个受保护的属性,称为ShowWithoutActivation,(在大多数情况下),它将在显示窗体的时候跳过窗体的激活:

protected override bool ShowWithoutActivation {
get { return true; }
}


如果设置ShowWithoutActivation没有效果,可以通过重写CreateParams属性强制设置控件样式的WS_EX_NOACTIVATE标志:

protected override CreateParams CreateParams {
get {
const int WS_EX_NOACTIVATE = 0x08000000;
CreateParams p = base.CreateParams;
p.ExStyle |= WS_EX_NOACTIVATE;
return p;
}
}


请注意,这样做不会阻止当窗体可见时它获得焦点,也就是说只要单击了窗体,这个问题还是会出现。因此,要想成功预防在窗体显示的时候成功获得焦点,下一个挑战是,防止它任何时候获得焦点。这是所有Win32拦截窗口消息技术中最常用的。特别来讲是响应WM_MOUSEACTIVATE消息(当单击事件被触发但是没有获得焦点时发送)设置为MA_NOACTIVATE(不改变焦点,处理单击事件):

 protected override void DefWndProc(ref Message m) {
const int WM_MOUSEACTIVATE = 0x21;
const int MA_NOACTIVATE = 0x0003;
if (m.Msg == WM_MOUSEACTIVATE)
m.Result = (IntPtr)MA_NOACTIVATE;
else
base.DefWndProc(ref m);
}


实际上这样是可以工作的,当点击下拉窗体的时候也不会导致转移焦点。问题是,下拉不只是窗体本身,单击窗体上任意的控件你会发现之前做的工作算白做了。可以在控件上再如法炮制在窗体上做过的消息拦截技术,但一些控件(例如TreeView)不行。比方说选择一个树节点,始终会转移焦点。

所以,在这个问题之后,你留下了一堆子类化控件和底层的代码,又得到什么了呢?一个很好的用户体验,至少是在实际点击它之前,最后还是没用。

替换为使用控件实现

最后,你可能会认为使用一个控件并且直接添加到父窗体上可能是一个避免焦点问题的聪明的办法……,是的,它完全解决了这个问题。不幸的是,现在下拉框的范围不能超过父窗体的客户区。你可能会认为这也挺好,只要您的父窗体在下拉控件的位置下面有足够空间,但是这没有完全实现使用一个下拉框的首要的用途。这个“解决方案”同样不可取。

进阶,ToolStripDropDown

事实上,随着对下拉列表展现的行为观察的深入,会越来越发现,他们更类似菜单而不是窗体。菜单可以独立处理键盘和鼠标输入同时不会从它们的父窗体争夺焦点,所以,让我们看看在框架库中一个做明显的例子,ContextMenuStrip控件。这是一个仅仅被继承的控件,在所有其他方面,它的行为就像一个组件。不过更重要的是,这是不是一个窗体,从而避免了焦点的问题。事实上,菜单在焦点处理上相比其他元素时,代表了第三类:

 窗体:从父窗体获得焦点,而且焦点系统独立于父窗体的控件。
 控件:从它们的父控件获得焦点,但隶属于父窗体,和它的其它控件是同一个焦点体系。
 菜单:和它的父控件、子控件共享焦点体系。

这种行为精确地代表了我们在下拉框中的需求,然而ContextMenuStrip有一个非常明确的用途。深入观察它的继承链,可以找到ToolStrip,ToolStripDropDown和ToolStripDropDownMenu。第一个并不代表一个菜单,所以它对我们来说没用。第二个是下拉框的基类,是一个不错的开端。值得一提的是,ToolStripDropDown不支持滚动(而它未来的派生类可以),然而在我用来实现下拉框的方法中,自动滚动支持是不必要的。

任何ToolStripItem的派生类(标签,按钮等)可以被添加到下拉列表框中,包括了ToolStripControlHost(虽然我不是选择简单地将一个TreeView控件放入下拉框中,这样仍然会有一些诡异的焦点问题,没有提及的间接开销)。下拉列表框自身必须包含至少一个项,即使它不会占用空间。作为一个控件类的派生类,它可以通过重写OnPaint方法重绘,并且同时响应鼠标和键盘事件。结合了无焦点行为,它提供了符合我们创建下拉框所预期的所有行为所需的工具。

实现

全功能的TreeView在下拉控件中并不需要,所以我们基于一个类似(但是更简单)的数据模型来实现。



DropDownBase类包含可编辑部分的基本功能,绘制,事件处理和设计时支持,以及DroppedDown状态的管理。数据模型围绕类似于TreeNode的ComboTreeNode类。它和它的集合类型ComboTreeNodeCollection构成创建一棵树的循环关系。ComboTreeBox类是DropDownBase类主要的实现类。它拥有和控制数据模型并且可视化选定的节点。它还拥有ComboTreeDropDown——实际的下拉列表(ToolStripDropDown类的派生类)。

在此实现中,数据模型和视图完全分离,节点可以单独于控件/下拉框被定义和操纵。

模型——ComboTreeNode类和ComboTreeNodeCollection集合

ComboTreeNode类是一个简单用来保存节点的名称,文本,状态以及和模型树中其它节点之间关系的原子类。ComboTreeNodeCollection集合表示子树,并因此关联一个父节点。此规则的唯一例外是对于尚未被添加到控件的子树,以及根集合属于控件。无论这个类和ComboTreeBox类关系如何,当添加到ComboTreeBox控件中时都会带上附加的属性和行为。

ComboTreeNodeCollection集合实现了IList<ComboTreeNode>,因为节点的顺序是很重要的。一个内部对象List<ComboTreeNode>用来作为后备存储字段。集合负责分配每个节点的父节点,以确保树的CollectionChanged事件(来自INotifyCollectionChanged接口)被递归触发:

 public void Add(ComboTreeNode item) {
innerList.Add(item);
item.Parent = node;
// changes in the subtree will fire the event on the parent
item.Nodes.CollectionChanged += this.CollectionChanged;
OnCollectionChanged(
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)
);
}


它还实现了非泛型的IList,以便提供对Windows窗体设计器使用的CollectionEditor类的兼容。

视图——下拉框自身

ComboTreeDropDown类负责显示以及提供和model.Visually的数据交互,节点可以被表示为一系列的行,其上界由它们在树中的深度决定,其可见性由它们的父节点是否被扩展决定。每个节点的可见性,通过递归检查节点的父节点的Expanded属性决定,只有树的根节点到它的路径上的所有节点都没有被折叠,这个节点才可见:

 internal bool IsNodeVisible(ComboTreeNode node) {
bool displayed = true;
ComboTreeNode parent = node;
while ((parent = parent.Parent) != null) {
if (!parent.Expanded) {
displayed = false;
break;
}
}
return displayed;
}


渲染

一组简单合理的规则规定了节点之间的连接线如何绘制,事实上分离不同的排列组合并且分别缓存结果位图以节省时间和内存是可行的(这些独立的排列可以用BITMAPINFO结构体表示)。展开或折叠节点会使得可见节点的超集改变。从树中添加/删除节点。更改父控件的大小和字体的将导致缓存集被重新构建。

滚动

滚动是手工实现的,基于一个非常简单的原理:可见节点的邻接子集,取决于下拉框的最大高度的大小,被用来作为一个偏移量。

 滚动条的范围等于可见项的总数-在下拉列表框中可以显示的最大的子集项数。
 滚动条的位置可以表示为相对范围内的偏移量的百分比。
 滚动条滑块的大小等于滚动区域内条目所占可见项的总数的百分比。

要实际渲染下拉框,接下来,要确定可见滚动范围内的节点相对于顶部的位置。使用NodeInfo对象表示每个节点的可视属性。对于每个节点有两个绘制操作,缩进,节点图标,手柄、连接线和节点文本被用(缓存的)位图表示。如果滚动的范围内的节点数目小于可见节点的总数,滚动条同时被绘制。

响应鼠标事件时也需要绘制每个节点的边界以及滚动条(保存在ScrollBarInfo中)部分。如果鼠标指针落在某个项的范围之内,相关联的ComboTreeNode对象可以被确定。单击节点旁的加/减手柄可以使其折叠/展开,而在节点上的任意位置单击将改变选择项并关闭下拉框。(值得注意的是,一些鼠标特性,比如拖动滚动条,并按住按钮的自动重复行为会有些复杂,并不属于本文讨论范围。然而要提一下一个最值得一提的问题——下拉框的MouseWheel事件之能由父控件来处理。)

高亮显示项目 vs 选中节点

每次下拉列表显示,它滚动到(并高亮显示)父控件的SelectedNode属性对应的节点。高亮显示项目和选定的节点是两个概念,如同普通的ComboBox的下拉框。这些规则适用于:

 移动鼠标到某个项目上,使其高亮,但除非单击该项,否则选中的节点不会改变。
 使用键盘的上/下键导航会同时改变高亮项目和选择的节点。
 滚动(包括鼠标滚轮、键盘(上页/下页)或使用滚动条)既不会改变高亮项目也不会改变选中的节点。

控制器——父控件

ComboTreeBox是这个实现中的控制器。另外它还暴露了用来管理数据模型并提供对视图的访问的操作——将所有的概念封装成成一个可重用的Windows窗体控件。因此,它结合了ComboBox和TreeView控件两方面的行为:

 为每个节点分配一个名称,这个名称将被用来通过集合访问节点。
 使用ImageIndex/ImageKey为每个节点指定一个图标。
 保存每个节点的展开状态。
 SelectedNode属性用来获取/设置用户在树中的选择。
 PathSeparator和Path属性来表示选定节点的路径字符串。
 BeginUpdate()/EndUpdate()方法实现批量添加。
 ExpandAll()/CollapseAll()方法管理树的节点全部收起/全部展开。
 对树的排序(执行递归排序,使用默认或自定义的比较器)。

提供以下附加功能:

 递归的枚举器(AllNodes)遍历整棵树。
 根据节点名称或文本判断路径是否构成。
 使用Path属性设置选定的节点。
 ExpandedImageIndex/ExpandedImageKey:以允许展开的节点显示一个不同的图标。

TreeView控件的以下功能没有在ComboTreeBox中实现:

 水平滚动(在下拉框中不引人注目)。
 节点旁的复选框(多选)。
 自定义元素的外观或使用事件重绘控件。
 SelectedImageIndex/SelectedImageKey属性(其实,个人而言,我觉得没什么用)。
 节点上的工具提示,拖放功能等超过下拉列表框之外的其他功能。
 一个Sorted属性,用来创建自动排序的树,取而代之的是,它可以按需排序。

写在最后

ComboTreeBox既是一个自定义下拉列表框的例子,同时也是创建一个Windows窗体的解决方案。ToolStripDropDown组件使这一切成为可能,即使(在本例中)需要手工实现下拉框中的内容。对于编写自定义控件,保持原有的外观,行为和处理输入,这也是一个非常有益的练习。我特别满意的是优雅的滚动机制和使用位图缓存,这些使得我能够成功填充几十万个节点到下拉框中而不会导致性能损失(即使这样的规模被认为是不适合下拉框的)。

通过演示如何实现一个自定义下拉框,我希望能起到抛砖引玉的作用,发掘它更多的用处。

下载

源代码
(基于.NET 3.5, 引用System.Design和WindowsBase)

注:本文翻译自http://www.brad-smith.info/blog/archives/193,作者是Bradley Smith。
...全文

相关推荐

03 Python 界面库tkinter学习笔记

某付费课程的学习笔记,循序渐进让你入门并具备自学能力。

hh.scorpio

DevExpree Winforms_主要控件帮助译文

DevExpree Winforms_主要控件帮助译文

果然很田

易语言源码windowsXP界面模拟模块3.0

易语言源码windowsXP界面模拟模块3.0

zz_ll9023

实用代码脚本易语言源码常量支持库

实用代码脚本易语言源码常量支持库

zz_ll9023

一款非常好的WPF编程宝典2010 源代码

第1章 WPF概述 1 1.1 理解Windows图形 1 1.1.1 DirectX:新的图形引擎 1 1.1.2 硬件加速与WPF 2 1.2 WPF:高级API 4 1.2.1 分辨率无关性 5 1.2.2 WPF的演化 9 1.2.3 Windows窗体将继续保留 11 1.2.4 DirectX也将继续保留 12 1.2.5 Silverlight 12 1.3 WPF体系结构 13 1.4 结束语 17 第2章 XAML 19 2.1 理解XAML 20 2.1.1 WPF之前的图形用户界面 20 2.1.2 XAML变体 21 2.1.3 XAML编译 22 2.2 XAML基础 22 2.2.1 XAML名称空间 23 2.2.2 后台代码类 24 2.3 XAML中的属性和事件 26 2.3.1 简单属性与类型转换器 27 2.3.2 复杂属性 29 2.3.3 标记扩展 30 2.3.4 附加属性 31 2.3.5 嵌套元素 32 2.3.6 特殊字符与空白字符 35 2.3.7 事件 36 2.3.8 完整的Eight Ball示例程序 38 2.4 使用其他名称空间中的类型 38 2.5 加载和编译XAML 40 2.5.1 只使用代码 41 2.5.2 使用代码和未编译的XAML 43 2.5.3 使用代码和编译过的XAML 44 2.5.4 只使用XAML 46 2.6 结束语 47 第3章 Application类 48 3.1 应用程序的生命周期 48 3.1.1 创建Application对象 48 3.1.2 派生一个自定义的 Application类 49 3.1.3 应用程序的关闭方式 51 3.1.4 应用程序事件 51 3.2 Application类的任务 53 3.2.1 处理命令行参数 54 3.2.2 访问当前Application对象 55 3.2.3 在窗口之间进行交互 56 3.2.4 单实例应用程序 57 3.3 结束语 63 第4章 布局 64 4.1 理解WPF中的布局 64 4.1.1 WPF布局原则 65 4.1.2 布局过程 66 4.1.3 布局包容器 66 4.2 使用StackPanel面板 进行简单布局 68 4.2.1 布局属性 69 4.2.2 对齐方式 70 4.2.3 外边距 71 4.2.4 最小尺寸、最大尺寸以及 显式地设置尺寸 72 4.3 WrapPanel面板和DockPanel 面板 74 4.3.1 WrapPanel面板 74 4.3.2 DockPanel面板 75 4.3.3 嵌套布局包容器 77 4.4 Grid面板 78 4.4.1 调整行和列 80 4.4.2 跨越行和列 82 4.4.3 分割窗口 83 4.4.4 共享尺寸组 86 4.4.5 UniformGrid面板 88 4.5 使用Canvas面板进行 基于坐标的布局 89 4.5.1 Z顺序 90 4.5.2 lnkCanvas元素 91 4.6 布局示例 93 4.6.1 列设置 93 4.6.2 动态内容 94 4.6.3 组合式用户界面 96 4.7 结束语 97 第5章 内容 99 5.1 理解内容控件 99 5.1.1 Content属性 101 5.1.2 对齐内容 102 5.1.3 WPF内容原则 103 5.2 特殊包容器控件 104 5.2.1 ScrollViewer包容器控件 104 5.2.2 GroupBox和TabItem: 带标题的内容控件 107 5.2.3 Expander控件 109 5.3 装饰控件 112 5.3.1 Border控件 112 5.3.2 Viewbox控件 113 5.4 结束语 115 第6章 依赖项属性和路由事件 116 6.1 理解依赖项属性 116 6.1.1 定义和注册依赖项属性 117 6.1.2 WPF使用依赖项属性的方式 124 6.2 理解路由事件 126 6.2.1 定义和注册路由事件 126 6.2.2 关联事件处理程序 128 6.2.3 事件路由 129 6.3 WPF事件 137 6.3.1 生命周期事件 137 6.3.2 输入事件 139 6.3.3 键盘输入 140 6.3.4 鼠标输入 145 6.4 结束语 149 第7章 经典控件 151 7.1 控件类 151 7.1.1 背景画刷和前景画刷 151 7.1.2 字体 155 7.1.3 鼠标光标 159 7.2 内容控件 160 7.2.1 标签 160 7.2.2 按钮 161 7.2.3 工具提示 164 7.3 文本控件 170 7.3.1 多行文本 171 7.3.2 选择文本 172 7.3.3 TextBox控件的其他特征 172 7.3.4 PasswordBox控件 173 7.4 列表控件 174 7.4.1 ListBox控件 174 7.4.2 ComboBox控件 177 7.5 基于范围的控件 178 7.5.1 Slider控件 178 7.5.2 进度条 179 7.6 结束语 180 第8章 窗口 181 8.1 Window类 181 8.1.1 显示窗口 183 8.1.2 定位窗口 184 8.1.3 保存和恢复窗口位置 185 8.2 窗口交互 187 8.2.1 窗口所有权 188 8.2.2 对话框模型 189 8.2.3 通用对话框 190 8.3 非矩形窗口 191 8.3.1 简单形状窗口 191 8.3.2 具有形状内容的透明窗口 194 8.3.3 移动任意形状窗口 195 8.3.4 改变任意形状窗口的尺寸 196 8.4 Vista风格的窗口 197 8.4.1 使用Windows Vista玻璃效果 198 8.4.2 任务对话框和文件对话框 202 8.5 结束语 204 第9章 页面和导航 205 9.1 理解基于页面的导航 205 9.2 基于页面的用户界面 206 9.2.1 一个具有导航功能的基于 页面的简单应用程序 206 9.2.2 Page类 208 9.2.3 超链接 209 9.2.4 在一个框架中宿主页面 211 9.2.5 在另外一个页面中宿主页面 212 9.2.6 在Web浏览器中宿主页面 213 9.3 页面历史 214 9.3.1 深入分析WPF中的URI 214 9.3.2 导航历史 215 9.3.3 维护自定义的属性 216 9.4 导航服务 217 9.4.1 通过编程进行导航 217 9.4.2 导航事件 218 9.4.3 管理日志 219 9.4.4 向日志添加自定义项 221 9.4.5 页函数 225 9.5 XAML浏览器应用程序 227 9.5.1 XBAP应用程序的运行要求 228 9.5.2 创建XBAP应用程序 229 9.5.3 部署XBAP应用程序 230 9.5.4 更新XBAP应用程序 231 9.5.5 XBAP应用程序的安全性 232 9.5.6 完全信任的XBAP应用程序 233 9.5.7 联合XBAP应用程序和 标准的单机应用程序 234 9.5.8 为不同的安全级别编写代码 234 9.5.9 在Web页面中嵌入XBAP 应用程序 239 9.6 结束语 240 第10章 命令 241 10.1 理解命令 241 10.2 WPF命令模型 243 10.2.1 ICommand接口 243 10.2.2 RoutedCommand类 244 10.2.3 RoutedUICommand类 245 10.2.4 命令库 245 10.3 执行命令 246 10.3.1 命令源 246 10.3.2 命令绑定 247 10.3.3 使用多命令源 249 10.3.4 微调命令文本 250 10.3.5 直接调用命令 251 10.3.6 禁用命令 252 10.3.7 具有内置命令的控件 253 10.4 高级命令 255 10.4.1 自定义命令 255 10.4.2 在不同的位置使用 相同的命令 257 10.4.3 使用命令参数 259 10.4.4 跟踪和翻转命令 259 10.5 结束语 263 第11章 资源 264 11.1 程序集资源 264 11.1.1 添加资源 264 11.1.2 检索资源 266 11.1.3 pack URI 267 11.1.4 内容文件 268 11.2 本地化 269 11.2.1 构建本地化用户界面 269 11.2.2 使应用程序为本地化 做好准备 270 11.2.3 翻译过程 271 11.3 对象资源 276 11.3.1 资源集合 276 11.3.2 资源层次 277 11.3.3 静态资源和动态资源 279 11.3.4 非共享资源 280 11.3.5 通过代码访问资源 280 11.3.6 应用程序资源 281 11.3.7 系统资源 281 11.3.8 使用资源字典组织资源 283 11.3.9 在程序集之间共享资源 284 11.4 结束语 287 第12章 样式 288 12.1 样式基础 288 12.1.1 创建样式对象 291 12.1.2 设置属性 292 12.1.3 关联事件处理程序 293 12.1.4 多层样式 294 12.1.5 通过类型自动应用样式 296 12.2 触发器 297 12.2.1 简单触发器 297 12.2.2 事件触发器 299 12.3 结束语 301 第13章 形状、变换和画刷 302 13.1 理解形状 302 13.1.1 Shape类 303 13.1.2 矩形和椭圆 304 13.1.3 改变形状的尺寸和 放置形状 305 13.1.4 直线 309 13.1.5 折线 310 13.1.6 多边形 311 13.1.7 直线线帽和直线交点 313 13.1.8 点划线 314 13.1.9 像素对齐 315 13.2 变换 316 13.2.1 变换形状 317 13.2.2 变换元素 318 13.3 更好的画刷 319 13.3.1 LinearGradientBrush画刷 320 13.3.2 RadialGradientBrush画刷 322 13.3.3 ImageBrush画刷 323 13.3.4 平铺的ImageBrush画刷 325 13.3.5 VisualBrush画刷 327 13.3.6 透明掩码 328 13.4 位图效果 330 13.4.1 模糊效果 331 13.4.2 斜面边缘效果 331 13.4.3 浮雕边缘效果 332 13.4.4 光环和阴影 332 13.5 结束语 334 第14章 图形、图画和可视化对象 335 14.1 路径和图形 335 14.1.1 直线、矩形和椭圆图形 336 14.1.2 使用GeometryGroup 组合形状 337 14.1.3 使用CombinedGeometry 融合图形 339 14.1.4 使用PathGeometry绘制曲线 和直线 341 14.1.5 微语言图形 345 14.1.6 使用图形进行剪裁 347 14.2 图画 348 14.2.1 显示图画 350 14.2.2 导出插图 352 14.3 可视化对象 353 14.3.1 绘制可视化对象 354 14.3.2 在元素中包装可视化对象 355 14.3.3 命中测试 358 14.3.4 复杂的命中测试 360 14.4 结束语 363 第15章 控件模板 364 15.1 理解逻辑树和可视化树 364 15.2 理解模板 369 15.2.1 修饰类 371 15.2.2 剖析控件 372 15.3 创建控件模板 375 15.3.1 简单按钮模板 375 15.3.2 模板绑定 376 15.3.3 模板触发器 378 15.4 组织模板资源 380 15.4.1 分解按钮控件的模板 381 15.4.2 通过样式应用模板 383 15.4.3 自动应用模板 384 15.4.4 由用户选择的皮肤 385 15.5 构建更复杂的模板 387 15.5.1 多部分模板 387 15.5.2 ItemsControl控件中的 控件模板 388 15.5.3 修改滚动条 390 15.5.4 创建自定义窗口 394 15.5.5 SimpleStyle示例项目 398 15.6 结束语 400 第16章 数据绑定 401 16.1 数据绑定基础 401 16.1.1 绑定到元素的属性 401 16.1.2 使用代码创建绑定 404 16.1.3 多绑定 405 16.1.4 绑定方向 408 16.1.5 绑定更新 410 16.1.6 绑定到非元素对象 411 16.2 使用自定义对象绑定 到数据库 414 16.2.1 构建数据访问组件 414 16.2.2 构建数据对象 417 16.2.3 显示绑定对象 418 16.2.4 更新数据库 419 16.2.5 更改通知 420 16.3 绑定到对象集合 422 16.3.1 显示和编辑集合元素 422 16.3.2 插入和移除集合元素 425 16.3.3 绑定到ADO.NET对象 426 16.3.4 绑定到LINQ表达式 428 16.4 数据转换 430 16.4.1 使用值转换器格式化 字符串 431 16.4.2 使用值转换器创建对象 434 16.4.3 应用条件格式化 436 16.4.4 评估多个属性 438 16.5 验证 439 16.5.1 在数据对象中进行验证 439 16.5.2 自定义验证规则 443 16.5.3 响应验证错误 445 16.5.4 获取异常列表 445 16.5.5 显示不同的错误指示符号 446 16.6 结束语 449 第17章 数据模板、数据视图 和数据提供者 451 17.1 数据绑定回顾 451 17.2 数据模板 452 17.2.1 分离和重用模板 454 17.2.2 更高级的模板 455 17.2.3 改变模板 457 17.2.4 模板选择器 458 17.2.5 模板与选择 462 17.2.6 样式选择器 466 17.2.7 改变元素布局 468 17.3 数据视图 469 17.3.1 检索视图对象 470 17.3.2 过滤集合 470 17.3.3 过滤DataTable对象 473 17.3.4 排序 474 17.3.5 分组 475 17.3.6 声明式地创建视图 478 17.3.7 视图导航 480 17.4 数据提供者 483 17.4.1 ObjectDataProvider 484 17.4.2 XmlDataProvider 486 17.5 结束语 488 第18章 列表、树、工具条和菜单 489 18.1 ItemsControl类 489 18.1.1 ComboBox控件 492 18.1.2 包含复选框或单选 按钮的ListBox控件 495 18.2 ListView控件 497 18.2.1 使用GirdView视图创建列 499 18.2.2 改变列的尺寸 500 18.2.3 单元格模板 500 18.2.4 创建自定义视图 503 18.3 TreeView控件 510 18.3.1 使用数据绑定的TreeView 控件 511 18.3.2 将DataSet对象绑定 到树视图 514 18.3.3 即时节点创建 515 18.4 菜单 518 18.4.1 Menu类 518 18.4.2 菜单项 519 18.4.3 ContextMenu类 520 18.4.4 菜单分隔条 521 18.5 工具条和状态栏 522 18.5.1 ToolBar控件 522 18.5.2 StatusBar控件 525 18.6 结束语 526 第19章 文档 527 19.1 理解文档 527 19.2 流文档 528 19.2.1 流内容元素 529 19.2.2 格式化流内容元素 530 19.2.3 创建简单的流文档 532 19.2.4 块级别元素 533 19.2.5 内联级别元素 538 19.2.6 通过代码与元素进行交互 543 19.2.7 调整文本 547 19.3 只读流文档包容器 548 19.3.1 缩放 549 19.3.2 页面和列 549 19.3.3 从文件加载文档 552 19.3.4 打印 552 19.4 编辑流文档 553 19.4.1 加载文件 553 19.4.2 保存文件 555 19.4.3 格式化选择的文本 556 19.4.4 获取单个单词 558 19.5 固定文档 559 19.6 批注 561 19.6.1 批注类 562 19.6.2 启用批注服务 562 19.6.3 创建批注 563 19.6.4 检查批注 567 19.6.5 响应批注更改 569 19.6.6 在固定文档中保存批注 570 19.6.7 自定义便笺外观 571 19.7 结束语 572 第20章 打印 573 20.1 基本打印 573 20.1.1 打印元素 574 20.1.2 变换打印输出 576 20.1.3 打印不显示的元素 578 20.1.4 打印文档 579 20.1.5 在文档打印输出中 控制页面 582 20.2 自定义打印 584 20.2.1 使用可视化层中的类 进行打印 584 20.2.2 自定义多页打印 587 20.3 打印设置和管理 592 20.3.1 保存打印设置 592 20.3.2 打印页面范围 593 20.3.3 管理打印队列 593 20.4 通过XPS进行打印 596 20.4.1 为打印预览创建XPS文档 597 20.4.2 通过XPS直接打印 到打印机 598 20.4.3 异步打印 598 20.5 结束语 599 第21章 动画 600 21.1 理解WPF动画 600 21.1.1 基于时间的动画 600 21.1.2 基于属性的动画 601 21.2 基本动画 602 21.2.1 Animation类 602 21.2.2 使用代码创建动画 605 21.2.3 同时发生的动画 609 21.2.4 动画生命期 610 21.2.5 Timeline类 611 21.3 声明式动画和故事板 614 21.3.1 故事板 614 21.3.2 事件触发器 615 21.3.3 重叠动画 619 21.3.4 同时发生的动画 620 21.3.5 控制播放 620 21.3.6 监视动画进度 624 21.3.7 期望的帧速率 626 21.4 动画类型回顾 628 21.4.1 使用动画改变变换 629 21.4.2 使用动画改变画刷 633 21.4.3 关键帧动画 635 21.4.4 基于路径的动画 638 21.4.5 基于帧的动画 640 21.5 结束语 644 第22章 声音和视频 645 22.1 播放WAV音频 645 22.1.1 SoundPlayer类 646 22.1.2 SoundPlayerAction类 647 22.1.3 系统声音 648 22.2 MediaPlayer类 648 22.3 MediaElement类 650 22.3.1 使用代码播放音频 650 22.3.2 处理错误 651 22.3.3 使用触发器播放音频 652 22.3.4 播放多个声音 654 22.3.5 改变音量、平衡、速度 以及播放位置 655 22.3.6 将动画同步到音频 657 22.3.7 播放视频 658 22.3.8 视频效果 659 22.4 语音 661 22.4.1 语音合成 662 22.4.2 语音识别 663 22.5 结束语 665 第23章 3D绘图 666 23.1 3D绘图基础 666 23.1.1 视口 667 23.1.2 3D对象 667 23.1.3 摄像机 674 23.2 深入研究3D绘图 678 23.2.1 着色和法线 679 23.2.2 更加复杂的形状 682 23.2.3 Model3DGroup集合 683 23.2.4 使用材质 684 23.2.5 纹理映射 686 23.3 交互和动画 690 23.3.1 变换 690 23.3.2 旋转 691 23.3.3 飞过 692 23.3.4 跟踪球 694 23.3.5 命中测试 696 23.3.6 3D表面上的2D元素 700 23.4 结束语 702 第24章 自定义元素 704 24.1 理解WPF中的 自定义元素 705 24.2 构建基本的用户控件 707 24.2.1 定义依赖项属性 707 24.2.2 定义路由事件 710 24.2.3 添加标记 711 24.2.4 使用控件 713 24.2.5 命令支持 713 24.2.6 深入分析用户控件 716 24.3 无外观控件 717 24.3.1 修改颜色拾取器的代码 717 24.3.2 修改颜色拾取器的标记 718 24.3.3 流线化控件模板 720 24.3.4 特定主题样式和默认样式 722 24.4 扩展已有控件 724 24.4.1 理解掩码编辑控件 724 24.4.2 屏蔽语法 725 24.4.3 MaskedTextProvider类 726 24.4.4 实现一个WPF屏蔽文本框 726 24.4.5 改进MaskedTextBox控件 730 24.5 自定义面板 731 24.5.1 两步布局处理 732 24.5.2 Canvas面板克隆 734 24.5.3 更好的换行面板 735 24.6 自定义绘图元素 738 24.6.1 OnRender( )方法 739 24.6.2 评估自定义绘图 740 24.6.3 自定义绘图元素 741 24.6.4 自定义装饰元素 743 24.7 结束语 744 第25章 与Windows窗体的互操作 745 25.1 访问互操作性 745 25.2 混合窗口和窗体 748 25.2.1 为WPF应用程序添加窗体 748 25.2.2 为Windows窗体应用程序 添加WPF窗口 748 25.2.3 显示模态窗口和窗体 749 25.2.4 显示非模态窗口和窗体 749 25.2.5 Windows窗体控件的 可视化风格 750 25.2.6 不需要互操作性的 Windows窗体类 750 25.3 创建具有混合内容的窗口 754 25.3.1 WPF和Windows 窗体"空域" 755 25.3.2 在WPF中宿主Windows 窗体控件 756 25.3.3 WPF和Windows窗体 用户控件 758 25.3.4 在Windows窗体中宿主 WPF控件 759 25.3.5 访问键、助记码和焦点 761 25.3.6 属性映射 762 25.4 结束语 764 第26章 多线程和插件 765 26.1 多线程 765 26.1.1 Dispatcher类 766 26.1.2 DispatcherObject类 766 26.1.3 BackgroundWorker类 769 26.2 应用程序插件 775 26.2.1 插件管线 776 26.2.2 使用插件的应用程序 780 26.2.3 与宿主交互 787 26.2.4 可视化插件 792 26.3 结束语 794 第27章 ClickOnce部署 795 27.1 应用程序部署 795 27.1.1 理解ClickOnce 796 27.1.2 ClickOnce安装模型 796 27.1.3 ClickOnce的局限 798 27.2 简单的ClickOnce发布 798 27.2.1 选择位置 799 27.2.2 部署文件 803 27.2.3 安装ClickOnce应用程序 803 27.2.4 更新ClickOnce应用程序 805 27.3 ClickOnce选项 805 27.3.1 发布版本 806 27.3.2 更新策略 807 27.3.3 发布选项 808 27.4 结束语 809

lyhsea
评论 120
收藏 已收藏
分享
返回顶部
threenewbee
C# 111,129 成员 642,540 内容