1. 前言

我常常看到同一个应用程序中的表单的按钮————也就是“确定”、“取消”那两个按钮————实现得千奇百怪,其实只要使用统一的Style起码就可以统一按钮的大小,而我喜欢更进一步将”确定“、”取消“或其它按钮封装进一个自定义控件里。

这篇文章介绍了另一种ItemsControl的实现方式,并使用它为表单及自定义Window添加常用的按钮及其它功能。

2. 为Form添加FunctionBar

本来打算派生自ToolBar,或者参考UWP的CommandBar,但最后决定参考MahApps.Metro的WindowCommands创建了FormFunctionBar,它继承自HeaderedItemsControl,代码里没有任何功能,DefaultStyle如下:

<Style TargetType="Button"
x:Key="FormFunctionBarButtonBase">
<Setter Property="MinWidth"
Value="48" />
<Setter Property="Margin"
Value="4,0,0,0" />
<Style.Triggers>
<Trigger Property="IsDefault"
Value="true">
<Setter Property="MinWidth"
Value="96" />
</Trigger>
</Style.Triggers>
</Style> <Style x:Key="FormFunctionBarExtendedButton"
TargetType="local:ExtendedButton"
BasedOn="{StaticResource FormFunctionBarButtonBase}" /> <Style x:Key="FormFunctionBarButton"
TargetType="Button"
BasedOn="{StaticResource FormFunctionBarButtonBase}" /> <Style TargetType="{x:Type local:FormFunctionBar}">
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Focusable"
Value="False" />
<Setter Property="IsTabStop"
Value="False" />
<Setter Property="Margin"
Value="0" />
<Setter Property="Padding"
Value="12,0,12,12" />
<Setter Property="HorizontalContentAlignment"
Value="Right" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:FormFunctionBar">
<ControlTemplate.Resources>
<Style BasedOn="{StaticResource FormFunctionBarButton}"
TargetType="{x:Type Button}" />
<Style BasedOn="{StaticResource FormFunctionBarExtendedButton}"
TargetType="{x:Type local:ExtendedButton}" />
</ControlTemplate.Resources>
<Grid Margin="{TemplateBinding Padding}">
<ContentPresenter Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalAlignment="Left" />
<ItemsPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Grid.Column="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>

这是ItemsControl的另一种实现方式,放进FormFunctionBar的Button及KinoButton都会自动应用DefaultStyle预设的样式。然后在Form中添加FunctionBar属性,并在控件底部放一个PlaceHolder:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:PageTitle Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}" />
<local:ExtendedScrollViewer Grid.Row="1"
Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="True"
UseLayoutRounding="True" />
</local:ExtendedScrollViewer>
<ContentPresenter Content="{TemplateBinding FunctionBar}"
Grid.Row="2" />
</Grid>

最终FormFunctionBar的使用方式如下:

<kino:Form>
<kino:Form.FunctionBar>
<kino:FormFunctionBar>
<Button Content="OK"
Click="OnOK"
IsDefault="True" />
<Button Content="Cancel"
IsCancel="True"
Click="OnCancel" />
</kino:FormFunctionBar>
</kino:Form.FunctionBar>
</kino:Form>

这样做可以统一所有Form的按钮。由于做得很简单,后期可以再按需要添加其他控件的样式。其实这种方式很像Toolbar,我本来也考虑从Toolbar派生FunctionBar,但考虑到Toolbar本身的功能不少,而我只想要实现最简单的功能,所以直接从HeaderedItemsControl派生。(我将这个控件库定位为入门教材,所以越简单越好。)

有必要的话可以设置IsDefaultIsCancel属性,前者表示按钮会在表单点击Enter时触发,后者表示按钮会在表单点击ESC时触发。在FormFunctionBar我通过Trigger设置了IsDefault=True的按钮比其它按钮更长。

3. 为自定义Window添加按钮

为自定义Window在标题栏添加一些按钮也是个常见的需求,原理和FormFunctionBar一样,只需要在自定义的Window的适当位置放置一个PlaceHolder,然后把WindowFunctionBar放进去,使用方式如下:

<kino:ExtendedWindow.FunctionBar>
<kino:WindowFunctionBar>
<Button Content="Dino.C" />
<Separator />
<Menu>
<MenuItem Header="发送反馈">
<MenuItem Header="报告问题"
IsCheckable="True" />
<MenuItem Header="提供反馈"
IsCheckable="True" />
<Separator />
<MenuItem Header="设置..." />
</MenuItem>
</Menu>
<Button ToolTip="Help">
<Grid UseLayoutRounding="True">
<Path Data="some data"
Width="12"
Height="12"
UseLayoutRounding="True"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Fill="White" />
</Grid>
</Button>
</kino:WindowFunctionBar>
</kino:ExtendedWindow.FunctionBar>

WindowFunctionBar的DefaultStyle和FormFunctionBar大同小异,只是多了一些常用控件(如Menu、Separator)的样式,这里不一一展示。

4. 结语

FunctionBar展示了另一种自定义控件的方式:它本身基本上没有功能,只是方便添加Items并为为Items套用Style。如果派生自Toolbar的话可以使用OverflowItems功能,这很有趣,但现在还用不到所以没做。将来把FunctionBar添加到ListBoxItem之类的地方可能会需要。

有必要的话还可以添加多个FunctionBar,如Window上可以添加LeftWindowCommands、RightWindowCommands等各个功能区域,我工作上没遇到这种需求为求简单就只添加了一个功能区。

其实实现FunctionBar最大的难题是命名,我在CommandBar、ActionBar、Toolbar、ButtonsBar等名称之间犹豫了很久,根据反馈也许还是会修改。

5. 参考

MahApps.Metro_WindowCommands.cs at master

Button.IsDefault Property (System.Windows.Controls) Microsoft Docs

Button.IsCancel Property (System.Windows.Controls) Microsoft Docs

6. 源码

Kino.Toolkit.Wpf_FunctionBar at master

[WPF自定义控件库]为Form和自定义Window添加FunctionBar的更多相关文章

  1. [WPF自定义控件库] 让Form在加载后自动获得焦点

    原文:[WPF自定义控件库] 让Form在加载后自动获得焦点 1. 需求 加载后让第一个输入框或者焦点是个很基本的功能,典型的如"登录"对话框.一般来说"登录" ...

  2. [WPF自定义控件库]使用WindowChrome自定义RibbonWindow

    原文:[WPF自定义控件库]使用WindowChrome自定义RibbonWindow 1. 为什么要自定义RibbonWindow 自定义Window有可能是设计或功能上的要求,可以是非必要的,而自 ...

  3. WPF 如何创建自己的WPF自定义控件库

    在我们平时的项目中,我们经常需要一套自己的自定义控件库,这个特别是在Prism这种框架下面进行开发的时候,每个人都使用一套统一的控件,这样才不会每个人由于界面不统一而造成的整个软件系统千差万别,所以我 ...

  4. [WPF自定义控件库] 关于ScrollViewer和滚动轮劫持(scroll-wheel-hijack)

    原文:[WPF自定义控件库] 关于ScrollViewer和滚动轮劫持(scroll-wheel-hijack) 1. 什么是滚动轮劫持# 这篇文章介绍一个很简单的继承自ScrollViewer的控件 ...

  5. [WPF自定义控件库]使用WindowChrome的问题

    1. 前言 上一篇文章介绍了使用WindowChrome自定义Window,实际使用下来总有各种各样的问题,这些问题大部分都不影响使用,可能正是因为不影响使用所以一直没得到修复(也有可能别人根本不觉得 ...

  6. [WPF自定义控件库]自定义Expander

    1. 前言 上一篇文章介绍了使用Resizer实现Expander简单的动画效果,运行效果也还好,不过只有展开/折叠而缺少了淡入/淡出的动画(毕竟Resizer模仿Expander只是附带的功能).这 ...

  7. [WPF自定义控件库]了解如何自定义ItemsControl

    1. 前言 对WPF来说ContentControl和ItemsControl是最重要的两个控件. 顾名思义,ItemsControl表示可用于呈现一组Item的控件.大部分时候我们并不需要自定义It ...

  8. [WPF自定义控件库]简单的表单布局控件

    1. WPF布局一个表单 <Grid Width="400" HorizontalAlignment="Center" VerticalAlignment ...

  9. [WPF自定义控件库]以Button为例谈谈如何模仿Aero2主题

    1. 为什么选择Aero2 除了以外观为卖点的控件库,WPF的控件库都默认使用"素颜"的外观,然后再提供一些主题包.这样做的最大好处是可以和原生控件或其它控件库兼容,而且对于大部分 ...

随机推荐

  1. 转载:4412环境搭建:arm-linux-gcc: 没有那个文件或目录

    4412环境搭建:arm-linux-gcc: 没有那个文件或目录 2014年10月15日 ⁄ 环境搭建 ⁄ 共 993字 ⁄ 字号 小 中 大 ⁄ 评论 11 条 ⁄ 阅读 6,125 次 最近弄了 ...

  2. [POI2008]Sta

    Description 给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大 Input 给出一个数字N,代表有N个点.N<=1000000 下面N-1条边. Output ...

  3. ASP.NET网站发布到服务器

    我们一个项目做好了之后想要上线,首先得发布,然后在上传到服务器. 所用到的工具:vs2013(其它vs版本也可以,大致上是一样的) FTP破解版下载链接:http://files.cnblogs.co ...

  4. C#连接数据库_使用读取配置文件的方式

    using System; using System.Collections.Generic; using System.Configuration; using System.Data.SqlCli ...

  5. negroni-gzip源码简单分析解读

    negroni-gzip源码简单分析解读 这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip,阅读了不长的源码之后,总结了一些关键要点和注意点. 检查是否 ...

  6. 501在全志r16平台tinav3.0系统下调通pwm1的10KHZ波形

    501在全志r16平台tinav3.0系统下调通pwm1的10KHZ波形 2018/10/19 19:52 版本:V1.0 开发板:SC3817R SDK:tina v3.0 1.01原始编译全志r1 ...

  7. ESSENTIALS OF PROGRAMMING LANGUAGES (THIRD EDITION) :编程语言的本质 —— (一)

    # Foreword> # 序 This book brings you face-to-face with the most fundamental idea in computer prog ...

  8. Linux下支持mysql支持远程ip访问

    示例代码: use mysql; SELECT `Host`,`User` FROM user; UPDATE user SET `Host` = '%' WHERE `User` = 'use**' ...

  9. qt5.8 链接mysql错误:driver not load

    转载请注明出处:http://www.cnblogs.com/dachen408/p/7155858.html 问题:qt5.8 链接mysql错误:driver not load. 解决方案:1.安 ...

  10. 迅为7寸工业平板电脑|人机界面|工业触摸屏|工控机|HMI|工业显示器

    型号:iTOP-HMI070-C 7寸工业平板电脑特点: 1.iTOP-HMI070-C(CAN) 7寸工业触摸屏,CAN总线型触摸屏,配有2组独立的串口和一路CAN总线口: 2.串口都支持各种PLC ...