[WPF自定义控件库]为Form和自定义Window添加FunctionBar

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

原文地址:https://www.cnblogs.com/dino623/p/FunctionBar.html

时间: 2024-10-29 03:43:31

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

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

1. 需求 加载后让第一个输入框或者焦点是个很基本的功能,典型的如"登录"对话框.一般来说"登录"对话框加载后"用户名"应该马上获得焦点,用户只需输入用户名,点击Tab,再输入密码,点击回车就完成了登录操作. 在WPF中要让一个控件在加载时获得焦点应该很简单,只需要在Loaded事件后调用Focus()就行了.但有时表单是动态添加的,或者第一个表单元素会根据某些条件显示或隐藏,这时很难简单地让第一个控件获得焦点. 为了实现这个功能我创建了一个叫F

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

原文:[WPF自定义控件库]使用WindowChrome自定义RibbonWindow 1. 为什么要自定义RibbonWindow 自定义Window有可能是设计或功能上的要求,可以是非必要的,而自定义RibbonWindow则不一样: 如果程序使用了自定义样式的Window,为了统一外观需要把RibbonWindow一起修改样式. 为了解决RibbonWindow的BUG. 如上图所示,在Windows 10 上运行打开RibbonWindow,可以看到标题栏的内容(包括分隔符)没有居中对齐

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

1. 前言 上一篇文章介绍了使用Resizer实现Expander简单的动画效果,运行效果也还好,不过只有展开/折叠而缺少了淡入/淡出的动画(毕竟Resizer模仿Expander只是附带的功能).这篇继续Measure的话题,自定义了一个带有动画的ExtendedExpander. 2. ExtendedExpander的需求 使用Resizer实现的简易Expander没办法在折叠时做淡出动画,因为ControlTemplate中的ExpandSite在Collapsed状态下直接设置为隐藏

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

1. 为什么选择Aero2 除了以外观为卖点的控件库,WPF的控件库都默认使用"素颜"的外观,然后再提供一些主题包.这样做的最大好处是可以和原生控件或其它控件库兼容,而且对于大部分人来说模仿原生的主题也比自己设计一套好看的UI容易得多. WPF有以下几种原生主题: 主题文件 桌面主题 Classic.xaml Windows XP 操作系统上的经典 Windows 外观(Windows 95.Windows 98 和 Windows 2000). Luna.NormalColor.xa

[WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互

1. 前言 WPF有一个灵活的UI框架,用户可以轻松地使用代码控制控件的外观.例设我需要一个控件在鼠标进入的时候背景变成蓝色,我可以用下面这段代码实现: protected override void OnMouseEnter(MouseEventArgs e) { base.OnMouseEnter(e); Background = new SolidColorBrush(Colors.Blue); } 但一般没人会这么做,因为这样做代码和UI过于耦合,难以扩展.正确的做法应该是使用代码告诉C

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

1. 什么是滚动轮劫持 这篇文章介绍一个很简单的继承自ScrollViewer的控件: public class ExtendedScrollViewer : ScrollViewer { protected override void OnMouseWheel(MouseWheelEventArgs e) { if (ViewportHeight + VerticalOffset >= ExtentHeight && e.Delta <= 0) return; if (Ver

WPF自定义控件与样式(13)-自定义窗体Window &amp; 自适应内容大小消息框MessageBox

一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 自定义Window窗体样式: 基于自定义窗体实现自定义MessageBox消息提示框: 二.自定义Window窗体样式 自定义的Window窗体效果:   因为WPF默认的窗体比较简陋,大都需要自己实现Window窗体样式效果,基本思路很简单: 第一步:干掉默认样式:WindowStyle = Windo

[WPF自定义控件]?Window(窗体)的UI元素及行为

原文:[WPF自定义控件]?Window(窗体)的UI元素及行为 1. 前言 本来打算写一篇<自定义Window>的文章,但写着写着发觉内容太多,所以还是把使用WindowChrome自定义Window需要用到的部分基础知识独立出来,于是就形成了这篇文章. 无论是桌面编程还是日常使用,Window(窗体)都是最常接触的UI元素之一,既然Window这么重要那么多了解一些也没有坏处. 2.标准Window 这篇文章主要讨论标准的Window,不包括奇形怪状的无边框.非矩形Window,即只讨论W

WPF自定义控件与样式(7)-列表控件DataGrid与ListView自定义样式

一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: DataGrid自定义样式: ListView自定义样式: 二.DataGrid自定义样式 DataGrid是常用的数据列表显示控件,先看看实现的效果(动态图,有点大): DataGrid控件样式结构包括以下几个部分: 列头header样式 调整列头宽度的列分割线样式 行样式 行头调整高度样式 行头部样式