WPF控件开发之自定义控件
[WPF自定义控件]从ContentControl开始入门自定义控件
[WPF ⾃定义控件]从ContentControl 开始⼊门⾃定义控件1. 前⾔我去年写过⼀个在UWP ⾃定义控件的,⼤部分的经验都可以⽤在WPF 中(只有⼀点⼩区别)。
这篇⽂章的⽬的是快速⼊门⾃定义控件的开发,所以尽量精简了篇幅,更深⼊的概念在以后介绍各控件的⽂章中实际运⽤到才介绍。
是WPF 中最基础的⼀种控件,Window 、Button 、ScrollViewer 、Label 、ListBoxItem 等都继承⾃ContentControl 。
⽽且ContentControl 的结构⼗分简单,很适合⽤来⼊门⾃定义控件。
这篇⽂章通过⾃定义⼀个ContentControl 来介绍⾃定义控件的⼀些基础概念,包括⾃定义控件的基本步骤及其组成。
2. 什么是⾃定义控件在开始之前⾸先要了解什么是⾃定义控件以及为什么要⽤⾃定义控件。
在WPF 要创建⾃⼰的控件(Control ),通常可以使⽤⾃定义控件(CustomControl )或⽤户控件(UserControl ),两者最⼤的区别是前者可以通过对控件的外观灵活地进⾏定制。
如在下⾯的例⼦中,通过ControlTemplate 将Button改成⼀个圆形按钮:控件库中通常使⽤⾃定义控件⽽不是⽤户控件。
3. 创建⾃定义控件ContentControl 最简单的派⽣类应该是HeaderedContentControl 了吧,这篇⽂章会创建⼀个模仿HeaderedContentControl 的MyHeaderedContentControl ,它继承⾃ContentControl 并添加了⼀些细节。
在“添加新项”对话框选择“⾃定义控件(WPF )”,名称改为"MyHeaderedContentControl.cs"(⽤My-做前缀是⼗分差劲的命名⽅式,但只要⼀看到这种命名就明⽩这是个测试⽤的东西,不会和正规代码搞错,所以我习惯了测试⽤代码就这样命名。
简单易学 图文并茂 VB制作WPF自定义控件 范例1
简单易学图文并茂创作控件自己创作控件,分三种主要的形式:复合控件,扩展控件,和自定义控件。
复合控件,顾名思义就是把现有的进行组合,让它们协作形成功能强大的新控件;扩展控件,是以某一现有控件为基础,让它具有新的功能;自定义控件,则是由作者完全操刀,建立一个全新的控件。
可以用一个比喻来理解这三种形式的区别:复合控件,就是你买好各个电脑配件,组装成一台电脑;扩展控件,就是把显卡上的零件更换几个,让它能力比标准产品更强大;自定义控件,就是自己制作一个名为“生人勿近”的硬件,他可以通过PCI插槽,安装到电脑上,一旦生人走近,它能识别并发出狗叫……综上,三种形式中,复合控件相对来说是最简单的;扩展控件在其次;自定义控件最难。
通常,我们使用前两种技术,就能创作出很复杂的控件了。
范例1:Excel的单元格Cell,当它没有焦点的时候,就是一个TextBlock,当它获得焦点,可以编辑的时候,就是一个TextBox框。
这样一个控件,将是我们今后制作表格控件的基础。
范例1中,我们会用到两种技术,复合控件和扩展控件。
在VS中,要进行如下的工作(推荐使用VS2010)1.新建一个Solution,名为Cell;2.添加一个名为“TestAPP”的WFP项目。
我们用他来测试成果;3.添加一个名为“Ctrl_Cell”的WFP用户控件项目。
然后,我们需要一个TextBlock,和一个TextBox控件,这是我们的演员。
考虑一下它们应该怎样演出,才能达到我们需要的效果:●平时这个控件,应该表现出TextBlock的外观;●当我们点击这个Label时,隐藏的TextBox控件跑到TextBlock的前面,并且它显示的值和TextBlock一样。
同时,这个值是可以编辑的;●编辑完TextBox的内容,按下Enter,或者点击屏幕上的其他控件,让TextBox失去焦点,TextBox消失。
TextBlock跑到前面来,并且显示编辑后的内容。
WPF自定义ItemsControl控件
该控件叫 Sum m ar y, 主要是一些汇总信息的显示,有几个地方用,之前都是分散到各个XA ML 文件里,不统一。
本人WPF新手,对XAML了解不多,做这个软件基本上都是用CM,界面布局用Avalon Dock。
由于缺乏相关经验,又没有一个能问得上的人指导,写这个控件费了我很长时间(啥时有空啥时动动)。
之前主要做一些功能方面的,没有心思美化界面,现在虽然还有很多功能没写,但是基本上够自己用了,放下心思来做一些界面上的东西,要不然何苦选择WPF?先看一下图:该Custo m Cont rol 由4部分组成:大标题,小标题,值及Detail。
虽然细分这么多,但实质上还是一个列表类的控件,所以选择继承自 Item sC ontro l.做这个控件的时候,遇到了一些详细的问题不知道怎么解决,Google/Bing 都没有找到我要了解的,Baidu更不用提了,漫天的转载,Copy.1, 类似 Com boB ox 的 Displa yMemb erPat h 如何弄?既然都自定控件了,当然是想让它适用不同场景,不能局限于某一个实体类,最好是能像Displa yMemb erPat h ValueM em ber Path这样的属性。
这里,我定义了:Item Ti tlePa thPro perty及 Item Va luePa thPro perty来处理。
2,有了上面两个依赖属性,但是还不足以处理更多的场景,最好是能有不同的 Tem pla te 。
这里我定义了:TitleT em pla te / ValueT em pla te及De tailT em pla te.第一个问题,只需定义一个简单的 DataTe m plat e ,然后用 Tem pla teBin ding即可做到。
WPF自定义TabControl控件样式
WPF⾃定义TabControl控件样式⼀、前⾔程序中经常会⽤到TabControl控件,默认的控件样式很普通。
⽽且样式或功能不⼀定符合我们的要求。
⽐如:我们需要TabControl的标题能够居中、或平均分布;或者我们希望TabControl的标题能够进⾏关闭。
要实现这些功能我们需要对TabControl的样式进⾏定义。
⼆、实现TabControl的标题平均分布默认的TabControl标题是使⽤TabPanel容器包含的。
要想实现TabControl标题头平均分布,需要把TabPanel替换成UniformGrid;替换后的TabControl样式如下:<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}"><Setter Property="Padding" Value="2"/><Setter Property="HorizontalContentAlignment" Value="Center"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="Background" Value="White"/><Setter Property="BorderBrush" Value="#FFACACAC"/><Setter Property="BorderThickness" Value="1"/><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type TabControl}"><Grid x:Name="templateRoot" ClipToBounds="True" SnapsToDevicePixels="True" KeyboardNavigation.TabNavigation="Local"><Grid.ColumnDefinitions><ColumnDefinition x:Name="ColumnDefinition0"/><ColumnDefinition x:Name="ColumnDefinition1" Width="0"/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition x:Name="RowDefinition0" Height="Auto"/><RowDefinition x:Name="RowDefinition1" Height="*"/></Grid.RowDefinitions><UniformGrid x:Name="HeaderPanel" Rows="1" Background="Transparent" Grid.Column="0" IsItemsHost="True" Margin="0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex<Line X1="0" X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" Stroke="White" StrokeThickness="0.1" VerticalAlignment="Bottom" Margin="0 0 0 1" SnapsToDevicePixels="True"<Border x:Name="ContentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column<ContentPresenter x:Name="PART_SelectedContentHost" ContentTemplate="{TemplateBinding SelectedContentTemplate}" Content="{TemplateBinding SelectedContent}" ContentStringFormat</Border></Grid><ControlTemplate.Triggers><Trigger Property="TabStripPlacement" Value="Bottom"><Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/><Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/><Setter Property="Height" TargetName="RowDefinition0" Value="*"/><Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/></Trigger><Trigger Property="TabStripPlacement" Value="Left"><Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/><Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/><Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/><Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/><Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/><Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/><Setter Property="Height" TargetName="RowDefinition0" Value="*"/><Setter Property="Height" TargetName="RowDefinition1" Value="0"/></Trigger><Trigger Property="TabStripPlacement" Value="Right"><Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/><Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/><Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/><Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/><Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/><Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/><Setter Property="Height" TargetName="RowDefinition0" Value="*"/><Setter Property="Height" TargetName="RowDefinition1" Value="0"/></Trigger><Trigger Property="IsEnabled" Value="False"><Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style>即使这样设置了,TabControl的标题还是很丑,这个时候就需要通过设置TabItem来更改标题样式了。
简单易学 图文并茂 VB制作WPF自定义控件 范例1
简单易学图文并茂创作控件自己创作控件,分三种主要的形式:复合控件,扩展控件,和自定义控件。
复合控件,顾名思义就是把现有的进行组合,让它们协作形成功能强大的新控件;扩展控件,是以某一现有控件为基础,让它具有新的功能;自定义控件,则是由作者完全操刀,建立一个全新的控件。
可以用一个比喻来理解这三种形式的区别:复合控件,就是你买好各个电脑配件,组装成一台电脑;扩展控件,就是把显卡上的零件更换几个,让它能力比标准产品更强大;自定义控件,就是自己制作一个名为“生人勿近”的硬件,他可以通过PCI插槽,安装到电脑上,一旦生人走近,它能识别并发出狗叫……综上,三种形式中,复合控件相对来说是最简单的;扩展控件在其次;自定义控件最难。
通常,我们使用前两种技术,就能创作出很复杂的控件了。
范例1:Excel的单元格Cell,当它没有焦点的时候,就是一个TextBlock,当它获得焦点,可以编辑的时候,就是一个TextBox框。
这样一个控件,将是我们今后制作表格控件的基础。
范例1中,我们会用到两种技术,复合控件和扩展控件。
在VS中,要进行如下的工作(推荐使用VS2010)1.新建一个Solution,名为Cell;2.添加一个名为“TestAPP”的WFP项目。
我们用他来测试成果;3.添加一个名为“Ctrl_Cell”的WFP用户控件项目。
然后,我们需要一个TextBlock,和一个TextBox控件,这是我们的演员。
考虑一下它们应该怎样演出,才能达到我们需要的效果:●平时这个控件,应该表现出TextBlock的外观;●当我们点击这个Label时,隐藏的TextBox控件跑到TextBlock的前面,并且它显示的值和TextBlock一样。
同时,这个值是可以编辑的;●编辑完TextBox的内容,按下Enter,或者点击屏幕上的其他控件,让TextBox失去焦点,TextBox消失。
TextBlock跑到前面来,并且显示编辑后的内容。
WPF自定义控件与样式-自定义按钮(Button)
WPF⾃定义控件与样式-⾃定义按钮(Button)⼀、前⾔程序界⾯上的按钮多种多样,常⽤的就这⼏种:普通按钮、图标按钮、⽂字按钮、图⽚⽂字混合按钮。
本⽂章记录了不同样式类型的按钮实现⽅法。
⼆、固定样式的按钮固定样式的按钮⼀般在临时使⽤时或程序的样式⽐较固定时才会使⽤,按钮整体样式不需要做⼤的改动。
2.1 普通按钮-扁平化风格先看效果:定义Button的样式,详见代码:<Style x:Key="BtnInfoStyle" TargetType="Button"><Setter Property="Width" Value="70"/><Setter Property="Height" Value="25"/><Setter Property="Foreground" Value="White"/><Setter Property="BorderThickness" Value="0"/><Setter Property="Background" Value="#43a9c7"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels <TextBlock Text="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"/></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter TargetName="border" Property="Background" Value="#2f96b4"/></Trigger><Trigger Property="IsPressed" Value="True"><Setter TargetName="border" Property="Background" Value="#2a89a4"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style>引⽤⽅法:<Grid Background="White"><StackPanel Orientation="Horizontal" Margin="10" VerticalAlignment="Top"><Button Style="{StaticResource BtnInfoStyle}" Content="信息" Margin="5 0"/></Grid>上述代码实现了Button按钮的扁平化样式,如果你想调整颜⾊风格,通过修改Background的值可实现默认颜⾊,⿏标经过颜⾊以及⿏标按下颜⾊。
WPF使用Winform自定义控件
WPF使⽤Winform⾃定义控件在WPF的⽤户控件中使⽤Winfrom⾃定义控件的过程:1、添加引⽤WindowsFormsIntegration.dllSystem.Windows.Forms.dll2、在要使⽤WinForm控件的WPF窗体的XAML⽂件中添加如下内容:xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"xmlns:wfi ="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"如下图所⽰:3、在WPF的容器控件内如StackPanel内⾸先要添加WinForm控件的宿主容器,⽤于衔接WPF和WinForm,对应XAML如下:说明:<wfi:WindowsFormsHost></wfi:WindowsFormsHost>即为WinForm控件的宿主容器,每⼀个宿主容器只能放⼀个WinForm控件,如下例,放了三个WinForm控件,分别放在三个宿主容器⾥⾯,该容器可以设置属性来调整⼤⼩和布局注意:如上我添加的WinForm控件如在指定其Name时,必须加前缀x:,如添加Lable时<wf:Label x:Name="wpfLabel" Text="我是WPF中的WinForm控件” />,否则后台代码⽆法访问。
<local:UserControl1 Name="Header" Width="319" Height="30"></local:UserControl1>这个为Winform⾃定义控件。
.NetWInform开发笔记(三)谈谈自制控件(自定义控件)剖析
.Net WInform开发笔记(三)谈谈自制控件(自定义控件)自定义控件的出现有利于用户更好的实现自己的想法,可以封装一些常用的方法,属性等等,本文详细介绍一下自定义控件的实现,感兴趣的朋友可以了解下末日这天写篇博客吧,既然没来,那就纪念一下。
这次谈谈自制控件,也就是自定义控件,先上图,再说1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg,.excel等等)的预览功能2.重写ListBox,增加折叠、鼠标背影、分类等功能-----------------------------分割线--------------------------------------------------------------一、扩展OpenFileDialog许多软件的打开对话框都有预览功能,最常见的就是图片预览,用鼠标选择一个图片文件后,右侧或者下侧就会有该图片的缩略图(photoshop中属于后者)。
在winform编程中,有专门的打开文件对话框的类O penFileDialog,但是他不提供文件预览功能,封装得实在太好。
看看它公开那些接口提到扩展,很多人可能想到继承它就可以扩展它,可惜OpenFileDialog声明为sealed,不允许从他继承。
稍微底层一点的,想到可以通过Win32 API来修改它的显示方式,只可惜,如你所见,它根本没提供Handle 属性,更别说HandleCreated、HandleDestroyed等事件了。
那么怎么样子搞呢?其实答案还是通过Win32 API,只是我们取得它的句柄的方式要复杂一点而且调用API的时机隐晦了一点。
提示:1.Win32 API操作窗体需要知道窗体的句柄;2.不熟悉Win32编程的同学可以先上网查查资料,特别是不知道SetParent、SetWindowPos等API是干嘛的,我觉得以下的看不懂。
为什么说取得它的句柄复杂了一点?难道不是用“FindWindow”、“FindWindowEx”、“EnumChildWindows”等获取OpenFileDialog的句柄,再用“SetParent”、“SetWindowPos”等API将.net控件(本例中是DataGri dView控件,当然可以使其他任何一种)添加到OpenFileDialog中去?没错,以上列举出来的API都是基本要用到的,“只是用在什么地方、什么时候用”是个比较麻烦的问题,原因如下:1)我们知道OpenfileDialog显示的是模式对话框,也就是说,一旦它ShowDialog(),它以下的代码是不会再执行的,具体原因是什么(我以后的博客会专门讲为什么),你现在可以理解为OpenFileDialog()方法会阻塞调用线程,既然阻塞了调用线程,那么我们再无法控制程序了(直到它返回),根本谈不上再调用API获取OpenFileDialog的句柄然后去操作它。
wpf自定义控件(包含依赖属性以及事件)
wpf⾃定义控件(包含依赖属性以及事件)创建了这个依赖属性,就可以直接在对应的控件中使⽤了,就像是button中⼀开始就内置的width等属性⼀样,这个在设计⾃定义控件的时候⽤的尤其多下⾯讲的是⾃定义分页控件代码:<UserControl x:Class="WPFDataGridPaging.Pager"xmlns="/winfx/2006/xaml/presentation"xmlns:x="/winfx/2006/xaml"xmlns:mc="/markup-compatibility/2006"xmlns:d="/expression/blend/2008"xmlns:local="clr-namespace:WPFDataGridPaging"mc:Ignorable="d"d:DesignHeight="30" d:DesignWidth="220"><UserControl.Resources><Style TargetType="{x:Type Button}"><Setter Property="Width" Value="22"/><Setter Property="Height" Value="22"/></Style></UserControl.Resources><Grid><StackPanel Orientation="Horizontal"><Button x:Name="FirstPageButton" Margin="5,0" Click="FirstPageButton_Click"><Path Width="7" Height="10" Data="M0,0L0,10 M0,5L6,2 6,8 0,5" Stroke="Black" StrokeThickness="1"Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/></Button><Button x:Name="PreviousPageButton" Margin="0,0,5,0" Click="PreviousPageButton_Click"><Path Width="8" Height="8" Data="M0,4L8,0 8,8z" Stroke="Black" Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/></Button><TextBlock VerticalAlignment="Center"><Run Text="第"/><Run x:Name="rCurrent" Text="0"/><Run Text="页"/></TextBlock><Button Margin="5,0" x:Name="NextPageButton" Click="NextPageButton_Click"><Path Width="8" Height="8" Data="M0,4L8,0 8,8z" Stroke="Black" Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center"><Path.RenderTransform><RotateTransform Angle="180" CenterX="4" CenterY="4"/></Path.RenderTransform></Path></Button><Button Margin="0,0,5,0" x:Name="LastPageButton" Click="LastPageButton_Click"><Path x:Name="MainPath" Width="7" Height="10" Data="M0,0L0,10 M0,5 L6,2 6,8 0,5"Stroke="Black" StrokeThickness="1" Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center"><Path.RenderTransform><RotateTransform Angle="180" CenterX="3" CenterY="5"/></Path.RenderTransform></Path></Button><TextBlock VerticalAlignment="Center"><Run Text="共"/><Run x:Name="rTotal" Text="0"/><Run Text="页"/></TextBlock></StackPanel></Grid></UserControl>界⾯:后台代码:public partial class Pager : UserControl{#region声明事件和依赖属性public static RoutedEvent FirstPageEvent;public static RoutedEvent PreviousPageEvent;public static RoutedEvent NextPageEvent;public static RoutedEvent LastPageEvent;public static readonly DependencyProperty CurrentPageProperty;public static readonly DependencyProperty TotalPageProperty;#endregionpublic string CurrentPage{get { return (string)GetValue(CurrentPageProperty); }set { SetValue(CurrentPageProperty, value); }}public string TotalPage{get { return (string)GetValue(TotalPageProperty); }set { SetValue(TotalPageProperty, value); }}public Pager(){InitializeComponent();}static Pager(){#region注册事件以及依赖属性//注册FirstPageEvent事件,事件的拥有者是Pager,路由事件的名称是FirstPage,这是唯⼀的FirstPageEvent = EventManager.RegisterRoutedEvent("FirstPage", RoutingStrategy.Direct,typeof(RoutedEventHandler), typeof(Pager));PreviousPageEvent = EventManager.RegisterRoutedEvent("PreviousPage", RoutingStrategy.Direct,typeof(RoutedEventHandler), typeof(Pager));NextPageEvent = EventManager.RegisterRoutedEvent("NextPage", RoutingStrategy.Direct,typeof(RoutedEventHandler), typeof(Pager));LastPageEvent = EventManager.RegisterRoutedEvent("LastPage", RoutingStrategy.Direct,typeof(RoutedEventHandler), typeof(Pager));//注册⾃定义的依赖属性CurrentPageProperty = DependencyProperty.Register("CurrentPage",typeof(string), typeof(Pager), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnCurrentPageChanged))); TotalPageProperty = DependencyProperty.Register("TotalPage",typeof(string), typeof(Pager), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnTotalPageChanged)));#endregion}public event RoutedEventHandler FirstPage{add { AddHandler(FirstPageEvent, value); }remove { RemoveHandler(FirstPageEvent, value); }}public event RoutedEventHandler PreviousPage{add { AddHandler(PreviousPageEvent, value); }remove { RemoveHandler(PreviousPageEvent, value); }}public event RoutedEventHandler NextPage{add { AddHandler(NextPageEvent, value); }remove { RemoveHandler(NextPageEvent, value); }}public event RoutedEventHandler LastPage{add { AddHandler(LastPageEvent, value); }remove { RemoveHandler(LastPageEvent, value); }}///<summary>///依赖属性回调⽅法///</summary>///<param name="d"></param>///<param name="e"></param>public static void OnTotalPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){Pager p = d as Pager;if (p != null){Run rTotal = (Run)p.FindName("rTotal");rTotal.Text = (string)e.NewValue;}}private static void OnCurrentPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){Pager p = d as Pager;if (p != null){Run rCurrrent = (Run)p.FindName("rCurrent");rCurrrent.Text = (string)e.NewValue;}}private void FirstPageButton_Click(object sender, RoutedEventArgs e){RaiseEvent(new RoutedEventArgs(FirstPageEvent, this));}private void PreviousPageButton_Click(object sender, RoutedEventArgs e){RaiseEvent(new RoutedEventArgs(PreviousPageEvent, this));}private void NextPageButton_Click(object sender, RoutedEventArgs e){RaiseEvent(new RoutedEventArgs(NextPageEvent, this));}private void LastPageButton_Click(object sender, RoutedEventArgs e){RaiseEvent(new RoutedEventArgs(LastPageEvent, this));}}上⾯就是⾃定义的⼀个分页控件,如果我们想要使⽤这个控件,如下:<Window x:Class="WPFDataGridPaging.MainWindow"xmlns="/winfx/2006/xaml/presentation"xmlns:x="/winfx/2006/xaml"xmlns:d="/expression/blend/2008"xmlns:mc="/markup-compatibility/2006"xmlns:i="/expression/2010/interactivity"xmlns:local="clr-namespace:WPFDataGridPaging"mc:Ignorable="d"Title="MainWindow" Height="350" Width="525"><Grid><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><DataGrid Grid.Row="0" ItemsSource="{Binding FakeSource}" AutoGenerateColumns="False" CanUserAddRows="False"> <DataGrid.Columns><DataGridTextColumn Header="Item Id" Binding="{Binding Id}" Width="80"/><DataGridTextColumn Header="Item Name" Binding="{Binding ItemName}" Width="180"/></DataGrid.Columns></DataGrid><!-- TotalPage 和CurrentPage就是在Pager中⾃定义的依赖属性,可以在这⾥直接来⽤--><local:Pager TotalPage="{Binding TotalPage}"CurrentPage="{Binding CurrentPage, Mode=TwoWay}"HorizontalAlignment="Center"Grid.Row="1"><i:Interaction.Triggers><!--EventName="FirstPage" 这⾥FirstPage就是前⾯注册事件的时候默认的事件名称,必须要唯⼀ --><i:EventTrigger EventName="FirstPage"><i:InvokeCommandAction Command="{Binding FirstPageCommand}"/></i:EventTrigger><i:EventTrigger EventName="PreviousPage"><i:InvokeCommandAction Command="{Binding PreviousPageCommand}"/></i:EventTrigger><i:EventTrigger EventName="NextPage"><i:InvokeCommandAction Command="{Binding NextPageCommand}"/></i:EventTrigger><i:EventTrigger EventName="LastPage"><i:InvokeCommandAction Command="{Binding LastPageCommand}"/></i:EventTrigger></i:Interaction.Triggers></local:Pager></Grid></Window>界⾯:后台代码:///<summary>/// Interaction logic for MainWindow.xaml///</summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();//这⾥⼀定要注意,⼀定要将模型加到上下⽂中,要不然依赖属性和事件都不会触发,在实际开发中是不可能⼀打开 //界⾯就开始加载出来数据的,所以可以通过ObservableCollection形式绑定表格数据,//⽽需要绑定事件和依赖属性的需要将其绑定到上下⽂中DataContext = new MainViewModel();}}MainViewModel:public class MainViewModel : ViewModel{private ICommand _firstPageCommand;public ICommand FirstPageCommand{get{return _firstPageCommand;}set{_firstPageCommand = value;}}private ICommand _previousPageCommand;public ICommand PreviousPageCommand{get{return _previousPageCommand;}set{_previousPageCommand = value;}}private ICommand _nextPageCommand;public ICommand NextPageCommand{get{return _nextPageCommand;}set{_nextPageCommand = value;}}private ICommand _lastPageCommand;public ICommand LastPageCommand{get{return _lastPageCommand;}set{_lastPageCommand = value;}}private int _pageSize;public int PageSize{get{return _pageSize;}set{if(_pageSize != value){_pageSize = value;OnPropertyChanged("PageSize"); }}}private int _currentPage;public int CurrentPage{get{return _currentPage;}set{if(_currentPage != value){_currentPage = value;OnPropertyChanged("CurrentPage"); }}}private int _totalPage;public int TotalPage{get{return _totalPage;}set{if(_totalPage != value){_totalPage = value;OnPropertyChanged("TotalPage"); }}}///<summary>/// ObservableCollection表⽰⼀个动态数据集合,它可在添加、删除项⽬或刷新整个列表时提供通知。
在WPF中自定义控件
承,打造 CustomControl 时其会为从 System.Windows.Controls.Control 继承.但实际情况下,也许我们从他们的衍生类别开始继承会得到更多的好处(更好的重 用已 有的逻辑),比如你的控件拥有更多的类似于 Button 的某些特性,那么从 Button 开始继承就比从 Control 继承少写很多代码.
二,UserControl 还是 CustomControl? 要在 WPF 中自定义一个控件,使用 UserControl 与 CustomControl 都是 不错的选择(除此之外,还有更多选择,比如打造一个自定义的面板,但这不在本文
的讨论范围),他们的区别在于: UserControl,其更像 WinForm 中自定义控件的开发风格,在开发上更简单快速,几乎可以简单地理解为:利用设计器来将多个已有控件作为子元素来拼凑
在 VS 中右键单击你的项目,点击"添加新项目",在出现的选择列表中选择"UserControl",VS 会自动为你生成一个*.xaml 文件以及其对应 的后台代码文件
2
(*.cs 或其它). 值得注意的是,自动生成的代码中,你的控件是继承于 erControl 类的,这对应你的控件而 言并不一定是最恰当的基 类,你可以修改它,但注意你应该同时修改*.cs 文件和*.xaml 文件中的基类,而不只是修改*.cs 文件,否则当生成项目时会 报错"不是继承于同一基类".修改 *.xaml 文件的方法是:将该文件的第一行和最后一行的"UserControl" 改成与你认为恰当的基类名称.
WPF自定义ItemsControlListBoxListView控件样式
WPF⾃定义ItemsControlListBoxListView控件样式⼀、前⾔ItemsControl、ListBox、ListView这三种控件在WPF中都可作为列表信息展⽰控件。
我们可以通过修改这三个控件的样式来展⽰我们的列表信息。
既然都是展⽰列表信息的控件,那他们有什么相同点和不同点呢。
同: 1、这三个控件都是列表型控件,可以进⾏列表绑定(ItemsSource); 2、这三个控件均使⽤ItemsPresenter来展⽰列表信息;异: 1、ListBox继承于ItemsControl,⽐ItemsControl⼀个Selector对象; 2、ListView继承于ListBox,⽐ListBox多⼀个View属性。
也就是说ItemsControl不⽀持列表项选择功能,ListBox⽀持单选、多选。
ListView能以不同的视图形式展⽰列表信息。
下⾯我们通过例⼦来展现:1.1、ItemsControl实现的图⽚选择预览控件控件的效果如下:下⾯贴上代码:<StackPanel Orientation="Horizontal" Margin=" 0 0" HorizontalAlignment="Center" VerticalAlignment="Center"><Grid><Rectangle x:Name="rectangle" RadiusX="0" RadiusY="0" Width="50" Height="50" MouseLeftButtonUp="UpLoadCaptrue_Click" SnapsToDevicePixels="True" StrokeDashArray="3 3 3 3" StrokeThickness <Rectangle.Fill><ImageBrush ImageSource="/Images/UpLoad.png" Opacity="0.3" Stretch="Fill" Viewport="0.25 0.25 0.5 0.5"/></Rectangle.Fill><Rectangle.Style><Style TargetType="Rectangle"><Setter Property="Stroke" Value="#999999"/><Style.Triggers><Trigger Property="IsMouseOver" Value="true"><Setter Property="Stroke" Value="#1dc0a5"/></Trigger></Style.Triggers></Style></Rectangle.Style></Rectangle></Grid><ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Hidden" Width="360"><ItemsControl x:Name="itemsControl" Background="Transparent" ItemsSource="{Binding }"><ItemsControl.ItemsPanel><ItemsPanelTemplate><StackPanel Orientation="Horizontal"/></ItemsPanelTemplate></ItemsControl.ItemsPanel><ItemsControl.ItemTemplate><DataTemplate><Border><StackPanel><Grid Width="60" Height="60" SnapsToDevicePixels="True"><Border BorderBrush="#999999" Width="50" Height="50" BorderThickness="1" Margin="5 0"><Image Source="{Binding}" Width="40" Height="40" HorizontalAlignment="Center" VerticalAlignment="Center"/></Border><Image Source="/Images/Close.png" Width="16" Height="16" MouseLeftButtonUp="ReMoveCaptrue_Click" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0 2 0 0"<Image.Style><Style TargetType="Image"><Style.Triggers><Trigger Property="IsMouseOver" Value="true"><Setter Property="Effect"><Setter.Value><DropShadowEffect ShadowDepth="0" Color="Red" Opacity="1" BlurRadius="5"/></Setter.Value></Setter></Trigger></Style.Triggers></Style></Image.Style></Image></Grid></StackPanel></Border></DataTemplate></ItemsControl.ItemTemplate></ItemsControl></ScrollViewer></StackPanel>上⾯ItemsControl设置了ItemsPanel,由于我们的列表展⽰是横向的,所以设置StackPanel,并将Orientation设置为Horizontal。
[WPF自定义控件]简单的表单布局控件
[WPF ⾃定义控件]简单的表单布局控件1. WPF布局⼀个表单在WPF 中布局表单⼀直都很传统,例如使⽤上⾯的XAML ,它通过Grid 布局⼀个表单。
这样出来的结果整整齐齐,看上去没什么问题,但当系统⾥有⼏⼗个表单页以后需要统⼀将标签改为上对齐,或者标签和控件中加⼀个:号等需求都会难倒开发⼈员。
⼀个好的做法是使⽤某些控件库提供的表单控件;如果不想引⼊⼀个这么“重”的东西,可以⾃⼰定义⼀个简单的表单控件。
这篇⽂章介绍⼀个简单的⽤于布局表单的Form 控件,虽然是⼀个很⽼的⽅案,但我很喜欢这个控件,不仅因为它简单实⽤,⽽且是⼀个很好的结合了ItemsControl 、ContentControl 、附加属性的教学例⼦。
Form 是⼀个⾃定义的ItemsControl ,部分代码可以参考这篇⽂章。
2. ⼀个古⽼的⽅法即使抛开验证信息、确认取消这些更⾼级的需求(表单的其它功能真的很多很多,但这篇⽂章只谈论布局),表单布局仍是个⼗分复杂的⼯作。
幸好⼗年前ScottGu 分享过⼀个,很有参考价值:Karl Shifflett has another great WPF blog post that covers a cool way to perform flexible form layout for LOB scenarios.<Grid Width="400" HorizontalAlignment="Center" VerticalAlignment="Center"><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="Auto" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions><TextBlock Text="⽤户名" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="4" /><TextBox Grid.Column="1" Margin="4" /><TextBlock Text="密码" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="4" Grid.Row="1" /><PasswordBox Grid.Row="1" Grid.Column="1" Margin="4" /><TextBlock Grid.Row="2" Text="确认密码" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="4" /><PasswordBox Grid.Column="1" Grid.Row="2" Margin="4" /></Grid>使⽤代码和截图如上所⽰。
WPF自定义控件第一
WPF⾃定义控件第⼀本⽂主要针对WPF新⼿,⾼⼿可以直接忽略,更希望⾼⼿们能给出⼀些更好的实现思路。
前期⼀个⼩任务需要实现⼀个类似含步骤进度条的控件。
虽然对于XAML的了解还不是⾜够深⼊,还是摸索着做了⼀个。
这篇⽂章介绍下实现这个控件的步骤,最后会放出代码。
还请⾼⼿们给出更好的思路。
同时也希望这⾥的思路能给同道中⼈⼀些帮助。
话不多说,开始正题。
实现中的⼀些代码采⽤了⽹上现有的⽅案,代码中通过注释标记了来源,再次对代码作者⼀并表⽰感谢。
⾸先放⼀张最终效果图。
节点可以被点击控件会根据绑定的集合数据⽣成⼀系列节点,根据集合中的数据还可以按⽐例放置节点的位置。
节点的实体代码如下:1234 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19public class FlowItem{public FlowItem(){}public FlowItem(int id, string title,double offsetRate) {Id = id;Title = title;OffsetRate = offsetRate;}public int Id { get; set; }public string Title { get; set; }public double OffsetRate { get; set; }}其中三个属性分别代表了节点的编号,标题和偏移量(⽤来确定节点在整个条中的位置)。
控件的实现忘了很久以前在哪看到过⼀句话,说设计WPF控件时不⼀定按照MVVM模式来设计,但⼀定要确保设计的控件可以按照MVVM模式来使⽤。
本控件也是本着这么⽬标来完成。
控件实现为TemplatedControl,个⼈认为这种⽅式更为灵活,做出来的控件可复⽤度更⾼。
反之UserControl那种组合控件的⽅式更适⽤于⼀个项⽬内复⽤的需要。
遵循⼀般的原则,我们将控件单独放于⼀个项⽬中。
在TemplatedControl项⽬中,“模板”即XAML内容⼀般都放置在⼀个名为Generic.xaml⽂件中,这个⽂件应该放置在解决⽅案Themes⽂件夹下。
WPF自定义控件创建
WPF⾃定义控件创建本⽂简单的介绍⼀下WPF⾃定义控件的开发。
⾸先,我们打开VisualStudio创建⼀个WPF⾃定义控件库,如下图:然后,我们可以看到创建的解决⽅案如下:在解决⽅案中,我们看到了⼀个Themes⽂件夹和⼀个CS⽂件。
其中CS⽂件,就是我们需要编写的⾃定义控件,⾥⾯的类继承了Control类;⽽Themes则存放该控件的样式。
即,WPF⾃定义控件,是通过样式给我们的编辑的控件类披上外⾐⽽形成的。
下⾯,我们来编写⼀个简单的时间控件。
我们先将CustomControl1⽂件改名为KibaDateTime,然后打开KibaDateTime.cs⽂件,看到了⼀些控件应⽤提⽰,这些提⽰写的是⾃定义控件的应⽤⽅式,我们先不看这些提⽰,因为他写的不是很好理解。
接下来我们开始编写时间控件,修改KibaDateTime类如下:public class KibaDateTime : TextBox{private static Regex regex = new Regex("[0-9]+");#region ⼩时public static readonly DependencyProperty HourProperty = DependencyProperty.Register("Hour", typeof(int), typeof(KibaDateTime), new FrameworkPropertyMetadata(00));public int Hour{get{return (int)GetValue(HourProperty);}set{SetValue(HourProperty, value);}}#endregion#region 分钟public static readonly DependencyProperty MinuteProperty = DependencyProperty.Register("Minute", typeof(int), typeof(KibaDateTime), new FrameworkPropertyMetadata(00));public int Minute{get{return (int)GetValue(MinuteProperty);}set{SetValue(MinuteProperty, value);}}#endregion#region 秒public static readonly DependencyProperty SecondProperty = DependencyProperty.Register("Second", typeof(int), typeof(KibaDateTime), new FrameworkPropertyMetadata(00));public int Second{get{return (int)GetValue(SecondProperty);}set{SetValue(SecondProperty, value);}}#endregionstatic KibaDateTime(){//当此依赖项属性位于指定类型的实例上时为其指定替换元数据,以在该依赖项属性继承⾃基类型时重写该属性已存在的元数据。
WPF自定义控件与样式(3)-TextBoxRichTextBoxPasswordBox样。。。
WPF⾃定义控件与样式(3)-TextBoxRichTextBoxPasswordBox样。
⼀.前⾔.预览 申明:WPF⾃定义控件与样式是⼀个系列⽂章,前后是有些关联的,但⼤多是按照由简到繁的顺序逐步发布的等,若有不明⽩的地⽅可以参考本系列前⾯的⽂章,⽂末附有部分⽂章链接。
本⽂主要是对⽂本输⼊控件进⾏样式开发,及相关扩展功能开发,主要内容包括:基本⽂本框TextBox控件样式及扩展功能,实现了样式、⽔印、Label标签、功能扩展;富⽂本框RichTextBox控件样式;密码输⼊框PasswordBox控件样式及扩展功能;效果图:⼆.基本⽂本框TextBox控件样式及扩展功能2.1 TextBox基本样式样式代码如下:<!--TextBox默认样式--><Style TargetType="{x:Type TextBox}" x:Key="DefaultTextBox"><Setter Property="ContextMenu" Value="{DynamicResource TextBoxContextMenu}"/><Setter Property="SelectionBrush" Value="{StaticResource TextSelectionBrush}"/><Setter Property="FontFamily" Value="{StaticResource FontFamily}"/><Setter Property="FontSize" Value="{StaticResource FontSize}"/><Setter Property="BorderThickness" Value="1"/><Setter Property="MinHeight" Value="26"/><Setter Property="Width" Value="100"/><Setter Property="Background" Value="{StaticResource TextBackground}"/><Setter Property="Foreground" Value="{StaticResource TextForeground}"/><Setter Property="Padding" Value="0"/><Setter Property="BorderBrush" Value="{StaticResource ControlBorderBrush}"/><Setter Property="local:ControlAttachProperty.FocusBorderBrush" Value="{StaticResource FocusBorderBrush}"/><Setter Property="local:ControlAttachProperty.MouseOverBorderBrush" Value="{StaticResource MouseOverBorderBrush}"/><Setter Property="VerticalContentAlignment" Value="Center"/><!-- change SnapsToDevicePixels to True to view a better border and validation error --><Setter Property="SnapsToDevicePixels" Value="True"/><!--英 ['kærət] 美 ['kærət] 插⼊符号--><Setter Property="CaretBrush" Value="{StaticResource TextForeground}"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type TextBox}"><Grid x:Name="PART_Root"><Border x:Name="Bg" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"CornerRadius="{TemplateBinding local:ControlAttachProperty.CornerRadius}"BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/> <Grid x:Name="PART_InnerGrid"><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><!--Label区域--><ContentControl x:Name="Label" Margin="1" Template="{TemplateBinding local:belTemplate}"Content="{TemplateBinding local:bel}"/><!--内容区域--><ScrollViewer x:Name="PART_ContentHost" BorderThickness="0" Grid.Column="1" IsTabStop="False" Margin="2"VerticalAlignment="Stretch" Background="{x:Null}"/><!--⽔印--><TextBlock x:Name="Message" Padding="{TemplateBinding Padding}" Visibility="Collapsed"Text="{TemplateBinding local:ControlAttachProperty.Watermark}" Grid.Column="1"Foreground="{TemplateBinding Foreground}" IsHitTestVisible="False" Opacity="{StaticResource WatermarkOpacity}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="5,2,5,2"/><!--附加内容区域--><Border x:Name="PART_AttachContent" Grid.Column="2" Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center"><ContentControl VerticalAlignment="Center" VerticalContentAlignment="Center" Template="{TemplateBinding local:ControlAttachProperty.AttachContent}"/></Border></Grid></Grid><ControlTemplate.Triggers><!--显⽰⽔印--><DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value=""><Setter TargetName="Message" Property="Visibility" Value="Visible"/></DataTrigger><Trigger Property="IsMouseOver" Value="True"><Setter Property="BorderBrush" Value="{Binding Path=(local:ControlAttachProperty.MouseOverBorderBrush),RelativeSource={RelativeSource Self}}"/></Trigger><Trigger Property="IsFocused" Value="True"><Setter Property="BorderBrush" Value="{Binding Path=(local:ControlAttachProperty.FocusBorderBrush),RelativeSource={RelativeSource Self}}"/></Trigger><!--不可⽤--><Trigger Property="IsEnabled" Value="False"><Setter TargetName="PART_Root" Property="Opacity" Value="{StaticResource DisableOpacity}"/></Trigger><!--只读时,禁⽤PART_AttachContent--><Trigger Property="IsReadOnly" Value="True"><Setter TargetName="PART_AttachContent" Property="IsEnabled" Value="False"/><Setter TargetName="Bg" Property="Opacity" Value="{StaticResource ReadonlyOpacity}"/><Setter TargetName="PART_ContentHost" Property="Opacity" Value="{StaticResource ReadonlyOpacity}"/><Setter TargetName="Label" Property="Opacity" Value="{StaticResource ReadonlyOpacity}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style> 模板内容主要包含四部分:⽤于实现Label标签的预留区域;TextBox本⾝的⽂本输⼊显⽰部分;⽔印显⽰部分;功能扩展的预留区域; 其中Label标签、功能扩展,还有输⼊框的不同状态显⽰效果都是通过附加属性来实现的,其实从本质上附加属性和控件上定义的依赖属性是同⼀个概念,有些时候附加属性会更加⽅便,对于⼀些可共⽤的属性,就⽐较⽅便,这⼀点怎本⽂是有体现的。
WPF创建自定义控件并运用
WPF创建⾃定义控件并运⽤此项⽬源码:⾸先创建⾃定义控件库项⽬项⽬名称命名为:WpfCustomControlLibrary在CustomControl1.cs⽂件中添加新控件类BulletCheckBox///<summary>/// BulletCheckBox.xaml 的交互逻辑///</summary>public class BulletCheckBox : CheckBox{public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(BulletCheckBox), new PropertyMetadata("Off"));///<summary>///默认⽂本(未选中)///</summary>public string Text{get { return (string)GetValue(TextProperty); }set { SetValue(TextProperty, value); }}public static readonly DependencyProperty CheckedTextProperty = DependencyProperty.Register("CheckedText", typeof(string), typeof(BulletCheckBox), new PropertyMetadata("On"));///<summary>///选中状态⽂本///</summary>public string CheckedText{get { return (string)GetValue(CheckedTextProperty); }set { SetValue(CheckedTextProperty, value); }}public static readonly DependencyProperty CheckedForegroundProperty =DependencyProperty.Register("CheckedForeground", typeof(Brush), typeof(BulletCheckBox), new PropertyMetadata(Brushes.WhiteSmoke));///<summary>///选中状态前景样式///</summary>public Brush CheckedForeground{get { return (Brush)GetValue(CheckedForegroundProperty); }set { SetValue(CheckedForegroundProperty, value); }}public static readonly DependencyProperty CheckedBackgroundProperty =DependencyProperty.Register("CheckedBackground", typeof(Brush), typeof(BulletCheckBox), new PropertyMetadata(Brushes.LimeGreen));///<summary>///选中状态背景⾊///</summary>public Brush CheckedBackground{get { return (Brush)GetValue(CheckedBackgroundProperty); }set { SetValue(CheckedBackgroundProperty, value); }}static BulletCheckBox(){DefaultStyleKeyProperty.OverrideMetadata(typeof(BulletCheckBox), new FrameworkPropertyMetadata(typeof(BulletCheckBox)));}}为BulletCheckBox这个控件增加样式<Style TargetType="{x:Type local:BulletCheckBox}"><Setter Property="Background" Value="#FF4A9E4A"></Setter><Setter Property="Foreground" Value="#DDE8E1"></Setter><Setter Property="CheckedForeground" Value="White"></Setter><Setter Property="CheckedBackground" Value="#FF0CC50C"></Setter><Setter Property="FontSize" Value="13"></Setter><Setter Property="Cursor" Value="Hand"></Setter><Setter Property="Width" Value="750"></Setter><Setter Property="Height" Value="280"></Setter><Setter Property="Margin" Value="1"></Setter><Setter Property="Template"><Setter.Value><!--控件模板--><ControlTemplate TargetType="{x:Type local:BulletCheckBox}"><Viewbox Stretch="Uniform" VerticalAlignment="Center" HorizontalAlignment="Center"><Border x:Name="border" Width="75" Height="28" Background="{TemplateBinding Background}" SnapsToDevicePixels="True"Margin="{TemplateBinding Margin}" CornerRadius="14"><StackPanel Orientation="Horizontal"><!--状态球--><Border x:Name="state" Width="24" Height="24" Margin="3,2,1,2" CornerRadius="12" SnapsToDevicePixels="True"Background="{TemplateBinding Foreground}"><Border.RenderTransform><TranslateTransform x:Name="transState" X="0"></TranslateTransform></Border.RenderTransform></Border><!--⽂本框--><TextBlock Width="40" Foreground="{TemplateBinding Foreground}" x:Name="txt" Text="{TemplateBinding Text}" VerticalAlignment="Center" TextAlignment="Center"> <TextBlock.RenderTransform><TranslateTransform x:Name="transTxt" X="0"></TranslateTransform></TextBlock.RenderTransform></TextBlock></StackPanel></Border></Viewbox><!--触发器:设置选中状态符号--><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=CheckedText}" TargetName="txt"/><Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=CheckedForeground}" TargetName="state"/><Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=CheckedForeground}" TargetName="txt"/><Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=CheckedBackground}" TargetName="border"/><Trigger.EnterActions><BeginStoryboard><Storyboard><DoubleAnimation Storyboard.TargetName="transState" Storyboard.TargetProperty="X" To="45" Duration="0:0:0.2" /><DoubleAnimation Storyboard.TargetName="transTxt" Storyboard.TargetProperty="X" To="-24" Duration="0:0:0.2" /></Storyboard></BeginStoryboard></Trigger.EnterActions><Trigger.ExitActions><BeginStoryboard><Storyboard><DoubleAnimation Storyboard.TargetName="transState" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2" /><DoubleAnimation Storyboard.TargetName="transTxt" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2" /></Storyboard></BeginStoryboard></Trigger.ExitActions></Trigger><Trigger Property="IsEnabled" Value="false"><Setter Property="Opacity" Value="{StaticResource DisableOpacity}" TargetName="border"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style>编译项⽬得到DLL控件库⽂件⾃定义控件库⽣成完后,就可以创建WPF应⽤程序来调⽤它了,右击解决⽅案->添加->新建项⽬->WPF应⽤右击WpfApp1项⽬设为启动项⽬,右击该项⽬下的引⽤,添加引⽤从浏览中找到我们刚才⽣成的DLL控件库⽂件此时展开引⽤就可以看到刚才⽣成的控件库已经加载进来了打开MainWindow.xaml,添加引⽤xmlns:MyNamespace="clr-namespace:WpfCustomControlLibrary;assembly=WpfCustomControlLibrary"这句引⽤是在CustomControl1.cs⽂件中复制⽽来的,其中xmlns:MyNamespace是可以更改的后台⽂件不⽤改动,整个MainWindow.xaml⽂件如下:<Window x:Class="WpfApp1.MainWindow"xmlns="/winfx/2006/xaml/presentation"xmlns:x="/winfx/2006/xaml"xmlns:d="/expression/blend/2008"xmlns:mc="/markup-compatibility/2006"xmlns:MyNamespace="clr-namespace:WpfCustomControlLibrary;assembly=WpfCustomControlLibrary"xmlns:local="clr-namespace:WpfApp1"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid><MyNamespace:BulletCheckBox Text="关闭" CheckedText="开启" IsChecked="True" Width="100" Height="100" /></Grid></Window>最后运⾏程序:。
WPF设计自定义控件
在实际工作中,WPF提供的控件并不能完全满足不同的设计需求。
这时,需要我们设计自定义控件。
这里LZ总结一些自己的思路,特性如下:∙Coupling∙UITemplate∙Behaviour∙Function Package下面举例说说在项目中我们经常用到调音台音量条,写一个自定义控件模拟调音台音量条。
自定义控件SingnalLight,实现功能∙接收来自外部的范围0~100的数值∙实时显示接收数值∙数值范围0~50显示绿色,50~85显示黄色,85~100显示红色,没有数值显示褐色∙可在父控件上拖拽该控件首先New WPF Application Project,在Ui上放2个Button,代码:1 <Grid>2 <StackPanelOrientation="Horizontal" VerticalAlignment="Bottom">3<Button Content="Start" Click="Start_Click"></Button>4<Button Content="Stop" Click="Stop_Click"></Button>5</StackPanel>6 </Grid>Start,Stop事件实现1private void Start_Click(object sender, RoutedEventArgs e) {2SignalManager.Instance.Start();3 }45private void Stop_Click(object sender, RoutedEventArgs e) {6SignalManager.Instance.Stop();7 }这里创建一个SignalManager类,在Start时开启一个计时器,每隔1秒生成一个0~100的随机数,并作为模拟数值输出。
WPF自定义控件与样式(7)-列表控件DataGrid与ListView自定义样式
WPF⾃定义控件与样式(7)-列表控件DataGrid与ListView⾃定义样式⼀.前⾔ 申明:WPF⾃定义控件与样式是⼀个系列⽂章,前后是有些关联的,但⼤多是按照由简到繁的顺序逐步发布的等,若有不明⽩的地⽅可以参考本系列前⾯的⽂章,⽂末附有部分⽂章链接。
本⽂主要内容:DataGrid⾃定义样式;ListView⾃定义样式;⼆.DataGrid⾃定义样式DataGrid是常⽤的数据列表显⽰控件,先看看实现的效果(动态图,有点⼤):DataGrid控件样式结构包括以下⼏个部分:列头header样式调整列头宽度的列分割线样式⾏样式⾏头调整⾼度样式⾏头部样式单元格样式 在本⽂的样式定义中,默认开启了DataGrid的虚拟化,以⽀持⼤数据,若实际使⽤数据确定很⼩,应该关闭虚拟化,因为虚拟化本⾝也是有成本的。
样式代码:<!--调整列头宽度样式--><Style x:Key="DefaultColumnHeaderGripperStyle" TargetType="{x:Type Thumb}"><Setter Property="Width" Value="8"/><Setter Property="Background" Value="{StaticResource HeaderBorderBrush}"/><Setter Property="Cursor" Value="SizeWE"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type Thumb}"><Border Padding="{TemplateBinding Padding}" Background="Transparent" Margin="0 0 0 2"><Rectangle HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Width="1" Fill="{TemplateBinding Background}"/></Border></ControlTemplate></Setter.Value></Setter></Style><!--列头header样式--><Style x:Key="DefaultDataGridColumnHeader" TargetType="{x:Type DataGridColumnHeader}"><Setter Property="SnapsToDevicePixels" Value="True"/><Setter Property="MinWidth" Value="5"/><Setter Property="MinHeight" Value="25"/><Setter Property="Height" Value="30"/><Setter Property="Foreground" Value="{StaticResource TextForeground}"/><Setter Property="HorizontalContentAlignment" Value="Left"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="Padding" Value="10,4,4,7"/><Setter Property="Margin" Value="0,0,0,0"/><Setter Property="FontWeight" Value="SemiBold"></Setter><Setter Property="FontSize" Value="{StaticResource HeaderFontSize}"/><Setter Property="BorderThickness" Value="0,0,0,3"/><Setter Property="BorderBrush" Value="{StaticResource HeaderBorderBrush}"/><Setter Property="Background" Value="{StaticResource HeaderBackground}"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type DataGridColumnHeader}"><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions><Border x:Name="BackgroundBorder" BorderThickness="{TemplateBinding BorderThickness}"Grid.ColumnSpan="2" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"/><ContentPresenter x:Name="HeaderContent"Content="{TemplateBinding Content}"ContentTemplate="{TemplateBinding ContentTemplate}"Margin="{TemplateBinding Padding}"VerticalAlignment="{TemplateBinding VerticalContentAlignment}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"RecognizesAccessKey="True"SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/><TextBlock x:Name="SortArrow" Style="{StaticResource FIcon}" Text="" Grid.Column="1" Width="20"Visibility="Collapsed" FontSize="16" Margin="1,1,3,1"/><Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" HorizontalContentAlignment="Left"Style="{StaticResource DefaultColumnHeaderGripperStyle}"/><Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" HorizontalContentAlignment="Right" Background="Transparent"Style="{StaticResource DefaultColumnHeaderGripperStyle}" Grid.Column="1"/></Grid><ControlTemplate.Triggers><!--显⽰排序标⽰--><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsMouseOver" Value="True"/><Condition Property="SortDirection" Value="{x:Null}"/><Condition Property="CanUserSort" Value="true"/></MultiTrigger.Conditions><Setter TargetName="SortArrow" Property="Visibility" Value="Visible"/></MultiTrigger><!--可排序列⿏标样式--><Trigger Property="CanUserSort" Value="True"><Setter Property="Cursor" Value="Hand"></Setter></Trigger><!--升序--><Trigger Property="SortDirection" Value="Ascending"><Setter TargetName="SortArrow" Property="Visibility" Value="Visible"/></Trigger><!--降序--><Trigger Property="SortDirection" Value="Descending"><Setter TargetName="SortArrow" Property="Visibility" Value="Visible"/><Setter TargetName="SortArrow" Property="Text" Value=""/></Trigger><!--第⼀列左边不显⽰分割线--><Trigger Property="DisplayIndex" Value="2"><Setter Property="Visibility" Value="Collapsed" TargetName="PART_LeftHeaderGripper"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style><!--⾏样式--><Style x:Key="DefaultDataGridRow" TargetType="{x:Type DataGridRow}"><Setter Property="Foreground" Value="{StaticResource TextForeground}"/><Setter Property="Background" Value="Transparent"/><Setter Property="Margin" Value="0,0,0,0"/><Style.Triggers><Trigger Property="ItemsControl.AlternationIndex" Value="1"><Setter Property="Background" Value="{StaticResource ItemsAlternationContentBackground}"/></Trigger><Trigger Property="IsSelected" Value="True"><Setter Property="Background" Value="{StaticResource ItemSelectedBackground}"/><Setter Property="Foreground" Value="{StaticResource ItemSelectedForeground}"/></Trigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsSelected" Value="True"/><Condition Property="Selector.IsSelectionActive" Value="True"/></MultiTrigger.Conditions><Setter Property="Background" Value="{StaticResource ItemSelectedBackground}"/><Setter Property="Foreground" Value="{StaticResource ItemSelectedForeground}"/></MultiTrigger><MultiDataTrigger><MultiDataTrigger.Conditions><Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/></MultiDataTrigger.Conditions><Setter Property="Background" Value="{StaticResource ItemMouseOverBackground}"/><Setter Property="Foreground" Value="{StaticResource ItemMouseOverForeground}"/></MultiDataTrigger></Style.Triggers></Style><!--⾏头调整⾼度样式 --><Style x:Key="DefaultRowHeaderGripperStyle" TargetType="{x:Type Thumb}"><Setter Property="Height" Value="6"/><Setter Property="Background" Value="Transparent"/><Setter Property="Cursor" Value="SizeNS"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type Thumb}"><Border Padding="{TemplateBinding Padding}" Background="Transparent"/></ControlTemplate></Setter.Value></Setter></Style><!--⾏头部样式--><Style x:Key="DefaultDataGridRowHeader" TargetType="{x:Type DataGridRowHeader}"><Setter Property="HorizontalContentAlignment" Value="Stretch"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="Background" Value="Transparent"/><Setter Property="BorderBrush" Value="Transparent"/><Setter Property="BorderThickness" Value="0,0,1,0"/><Setter Property="Margin" Value="0,0,0,0"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type DataGridRowHeader}"><Grid><Border BorderBrush="{TemplateBinding BorderBrush}"Background="{TemplateBinding Background}"BorderThickness="{TemplateBinding BorderThickness}"Padding="{TemplateBinding Padding}"Margin="{TemplateBinding Margin}"SnapsToDevicePixels="True"><ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"VerticalAlignment="{TemplateBinding VerticalContentAlignment}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/></Border><Thumb x:Name="PART_TopHeaderGripper" VerticalContentAlignment="Top"VerticalAlignment="Top" Background="Transparent" Style="{StaticResource DefaultRowHeaderGripperStyle}"/> <Thumb x:Name="PART_BottomHeaderGripper" VerticalContentAlignment="Bottom"VerticalAlignment="Bottom" Style="{StaticResource DefaultRowHeaderGripperStyle}"/></Grid></ControlTemplate></Setter.Value></Setter></Style><!--单元格样式--><Style x:Key="DefaultDataGridCell"TargetType="{x:Type DataGridCell}"><Setter Property="Background" Value="Transparent"/><Setter Property="BorderBrush" Value="Transparent"/><Setter Property="HorizontalContentAlignment" Value="Stretch"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="SnapsToDevicePixels" Value="True"/><Setter Property="Padding" Value="0"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type DataGridCell}"><Border BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"Background="{TemplateBinding Background}"SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"><ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}"Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"></ContentPresenter></Border><ControlTemplate.Triggers><Trigger Property="IsSelected" Value="True"><Setter Property="Foreground" Value="{StaticResource ItemSelectedForeground}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style><!--表格DataGrid样式--><Style x:Key="DefaultDataGrid" TargetType="{x:Type DataGrid}"><Setter Property="MinRowHeight" Value="25"/><Setter Property="Background" Value="{StaticResource ItemsContentBackground}"/><Setter Property="BorderBrush" Value="{StaticResource ControlBorderBrush}"/><Setter Property="BorderThickness" Value="1"/><Setter Property="HorizontalGridLinesBrush" Value="{StaticResource GridLinesBrush}"/><Setter Property="VerticalGridLinesBrush" Value="{StaticResource GridLinesBrush}"/><Setter Property="ColumnHeaderStyle" Value="{StaticResource DefaultDataGridColumnHeader}"/><Setter Property="RowHeaderStyle" Value="{StaticResource DefaultDataGridRowHeader}"/><Setter Property="CellStyle" Value="{StaticResource DefaultDataGridCell}"/><Setter Property="RowStyle" Value="{StaticResource DefaultDataGridRow}"/><Setter Property="HeadersVisibility" Value="All"/><Setter Property="EnableRowVirtualization" Value="True"/><Setter Property="EnableColumnVirtualization" Value="False"/><Setter Property="AutoGenerateColumns" Value="False"/><Setter Property="IsReadOnly" Value="True"/><Setter Property="SelectionMode" Value="Single"/><Setter Property="SelectionUnit" Value="FullRow"/><Setter Property="GridLinesVisibility" Value="All"/><Setter Property="AlternationCount" Value="2"></Setter><Setter Property="ScrollViewer.CanContentScroll" Value="True"/><Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter><Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/><Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/><!--列头移动列时候分割线样式--><Setter Property="DropLocationIndicatorStyle"><Setter.Value><Style TargetType="Separator"><Setter Property="Background" Value="{StaticResource HeaderBorderBrush}"/><Setter Property="Width" Value="2.5"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Separator"><Rectangle Fill="{TemplateBinding Background}" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"/></ControlTemplate></Setter.Value></Setter></Style></Setter.Value></Setter><!--DataGrid控件模板--><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type DataGrid}"><Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" x:Name="border"Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True"><ScrollViewer x:Name="DG_ScrollViewer" Focusable="false"><ScrollViewer.Template><ControlTemplate TargetType="{x:Type ScrollViewer}"><Grid><Grid.ColumnDefinitions><ColumnDefinition x:Name="col_rowheader" Width="1"/><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><!--表格头部--><DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1" Grid.ColumnSpan="2"Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorTyp <!--主数据区--><Grid Grid.Row="1" Grid.ColumnSpan="2"><ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2"/></Grid><!--垂直滑动条--><ScrollBar x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}"Orientation="Vertical" Grid.Row="0" Grid.RowSpan="3" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"ViewportSize="{TemplateBinding ViewportHeight}"/><!--横向滑动条--><ScrollBar x:Name="PART_HorizontalScrollBar" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2"Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal"Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"ViewportSize="{TemplateBinding ViewportWidth}"/></Grid></ControlTemplate></ScrollViewer.Template><ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/></ScrollViewer></Border><ControlTemplate.Triggers><Trigger Property="IsEnabled" Value="false"><Setter Property="Opacity" Value="{StaticResource DisableOpacity}" TargetName="border"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter><Style.Triggers><Trigger Property="IsGrouping" Value="true"><Setter Property="ScrollViewer.CanContentScroll" Value="false"/></Trigger></Style.Triggers></Style>View Code 最后⼀个就是DataGrid的样式了,主要是重写定义了滑动条的布局,使得整体效果更加协调,属性AlternationCount可⽤来设置⾏交替⾊,⽽具体实现交替⾊是在上⾯⾏(DataGridRow)样式中通过触发器实现的。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
WPF控件开发之自定义控件Windows Presentation Foundation(WPF)控件模型的扩展性极大减少了创建新控件的需要。
但在某些情况下,仍可能需要创建自定义控件。
本主题讨论可最大限度减少在Windows Presentation Foundation(WPF)中创建自定义控件以及其他控件创作模型的需要的功能。
本主题还演示如何创建新控件。
AD:Windows Presentation Foundation(WPF)控件模型的扩展性极大减少了创建新控件的需要。
但在某些情况下,仍可能需要创建自定义控件。
本主题讨论可最大限度减少在Windows Presentation Foundation(WPF)中创建自定义控件以及其他控件创作模型的需要的功能。
本主题还演示如何创建新控件。
编写新控件的替代方法以前,如果要通过现有控件获取自定义体验,您只能更改控件的标准属性,例如背景色、边框宽度和字号。
如果希望在这些预定义参数的基础之上扩展控件的外观或行为,则需要创建新的控件,通常的方法是继承现有控件并重写负责绘制该控件的方法。
虽然这仍是一种可选方法,但也可以利用WPF丰富内容模型、样式、模板和触发器来自定义现有的控件。
下面的列表提供了一些示例,演示如何在不创建新控件的情况下使用这些功能来实现统一的自定义体验。
丰富内容。
很多标准WPF控件支持丰富内容。
例如,Button的内容属性为Object类型,因此从理论上讲,任何内容都可以显示在Button上。
若要让按钮显示图像和文本,可以将图像和TextBlock添加到StackPanel中,然后将StackPanel分配给Content属性。
由于这些控件可以显示WPF可视化元素和任意数据,因此,减少了创建新控件或修改现有控件来支持复杂可视化效果的需要。
样式。
Style是表示控件属性的值的集合。
使用样式可创建所需控件外观和行为的可重用表示形式,而无需编写新控件。
例如,假设希望所有TextBlock控件都呈现字号为14的红色Airal字体。
您可以创建一个样式作为资源,然后相应地设置适当的属性。
这样,添加到应用程序中的每个TextBlock都将具有相同的外观。
数据模板。
DataTemplate可用于自定义数据在控件上的显示方式。
例如,DataTemplate 可用于指定数据在ListBox中的显示方式。
有关这种情况的示例,请参见数据模板概述。
除了自定义数据外观之外,DataTemplate还可以包含UI元素,这样大大增加了自定义UI 的灵活性。
例如,使用DataTemplate可以创建一个ComboBox,其中每一项都包含一个复选框。
控件模板WPF中的很多控件都使用ControlTemplate来定义控件的结构和外观,这样可将控件外观和控件功能分离开。
通过重新定义控件的ControlTemplate,可以彻底更改控件的外观。
例如,假设您希望控件看起来像一个交通信号灯。
此控件具有简单的用户界面和功能。
该控件有三个圆形,一次只能点亮其中的一个。
经过考虑之后,您可能意识到RadioButton提供了一次只选中一项的功能,但是RadioButton的默认外观完全不像交通信号灯上的灯。
由于RadioButton使用控件模板来定义其外观,因此很容易重新定义ControlTemplate以符合该控件的要求,从而使用单选按钮来制作交通信号灯。
说明:尽管RadioButton可以使用DataTemplate,但在本例中,只使用DataTemplate还不够。
DataTemplate定义控件内容的外观。
对于RadioButton,指示RadioButton是否选中的那个圆形右侧显示出来的全部都是该控件的内容。
在交通信号灯的示例中,单选按钮只需要成为可“点亮”的圆形。
由于交通信号灯的外观要求与RadioButton的默认外观存在很大差异,因此,有必要重新定义ControlTemplate。
一般而言,DataTemplate用于定义控件的内容(或数据),ControlTemplate用于定义控件的构成方式。
触发器。
Trigger用于在不创建新控件的情况下动态更改控件的外观和行为。
例如,假设应用程序中有多个ListBox控件,而您希望每个ListBox中的项在选中时都显示为红色粗体。
您首先想到的可能是创建一个从ListBox继承的类,然后重写OnSelectionChanged方法,以更改选中项的外观,不过,更好的方法是向ListBoxItem的样式添加一个更改选中项外观的触发器。
触发器用于更改属性值或根据属性值执行操作。
EventTrigger用于在发生事件时执行操作。
一般而言,如果控件完全复制现有控件的功能,但您希望该控件具有不同的外观,则应先考虑是否可以使用本节中讨论的某些方法来更改现有控件的外观。
控件创作模型通过丰富内容模型、样式、模板和触发器,最大程度地减少了创建新控件的需要。
但是,如果确实需要创建新控件,那么理解WPF中的不同控件创作模型就显得非常重要。
WPF提供三个用于创建控件的一般模型,每个模型都提供不同的功能集和灵活度。
这三个模型的基类分别为UserControl、Control和FrameworkElement。
从UserControl派生在WPF中创建控件的最简单方法是从UserControl派生。
如果生成继承自UserControl 的控件,需要将现有组件添加到UserControl,命名这些组件,然后在可扩展应用程序标记语言(XAML)中引用事件处理程序。
执行这些操作之后,即可在代码中引用这些命名元素和定义事件处理程序。
此开发模型非常类似于用于WPF应用程序开发的模型。
如果生成正确,UserControl可以利用丰富内容、样式和触发器的优点。
但是,如果控件继承自UserControl,则使用该控件的用户将无法使用DataTemplate或ControlTemplate来自定义其外观。
因此,有必要从Control类或其派生类(UserControl 除外)进行派生,以便创建支持模板的自定义控件。
从UserControl派生的优点如果符合以下所有情况,请考虑从UserControl派生:希望以类似于生成应用程序的方式生成控件。
控件仅由现有组件组成。
不需要支持复杂自定义项。
从Control派生从Control类派生是大多数现有WPF控件使用的模型。
在创建继承自Control类的控件时,可使用模板定义其外观。
通过这种方式,可以将运算逻辑从可视化表示形式中分离出来。
通过命令和绑定(而不是事件),也可确保分离UI和逻辑,尽可能避免引用ControlTemplate中的元素。
如果控件的UI和逻辑正确分离,该控件的用户即可重新定义其ControlTemplate,从而自定义其外观。
尽管生成自定义Control不像生成UserControl 那样简单,自定义Control还是提供了最大的灵活性。
从Control派生的优点如果符合以下任一情况,请考虑从Control派生,而不要使用UserControl类:希望控件外观能通过ControlTemplate进行自定义。
希望控件支持不同的主题。
从FrameworkElement派生从UserControl或Control派生的控件依赖于组合现有元素。
很多情况下,这是一种可接受的解决方案,因为从FrameworkElement继承的任何对象都可以位于ControlTemplate 中。
但是,某些时候,简单的元素组合不能满足控件的外观需要。
对于这些情况,使组件基于FrameworkElement才是正确的选择。
生成基于FrameworkElement的组件有两种标准方法:直接呈现和自定义元素组合。
直接呈现涉及的操作包括:重写FrameworkElement的OnRender方法,并提供显式定义组件视觉效果的DrawingContext操作。
此方法由Image和Border使用。
自定义元素组合涉及的操作包括使用Visual类型的对象组合组件的外观。
有关示例,请参见使用DrawingVisual对象。
Track是WPF中使用自定义元素组合的控件示例。
在同一控件中,也可以混合使用直接呈现和自定义元素组合。
从FrameworkElement派生的优点如果符合以下任一情况,请考虑从FrameworkElement派生:希望对控件的外观进行精确控制,而不仅仅是简单的元素组合提供的效果。
想要通过定义自己的呈现逻辑来定义控件的外观。
想要以一种UserControl和Control之外的新颖方式组合现有元素。
控件创作基础知识如前所述,WPF最强大的功能之一是,无需创建自定义控件即可实现远比设置控件的基本属性来更改其外观和行为更强的功能。
WPF属性系统和WPF事件系统使样式、数据绑定和触发器功能成为可能。
如果在控件中实现依赖项属性和路由事件,则无论使用什么模型创建自定义控件,自定义控件的用户都可以像对WPF随附的控件那样使用这些功能。
使用依赖项属性当属性为依赖项属性时,可以进行下面的操作:在样式中设置该属性。
将该属性绑定到数据源。
使用动态资源作为该属性的值。
动画处理该属性。
如果希望控件的属性支持以上任一功能,则应将该属性实现为依赖项属性。
下面的示例定义一个依赖项属性。
这段代码执行下面的操作:将一个名为ValueProperty的DependencyProperty标识符定义为publicstaticreadonly字段。
通过调用DependencyProperty..::.Register向属性系统注册该属性名,以指定以下内容:属性的名称。
属性的类型。
拥有该属性的类型。
属性的元数据。
元数据包含该属性的默认值、CoerceValueCallback和PropertyChangedCallback。
通过实现该属性的get和set访问器,定义一个名为Value的CLR“包装”属性,这个名称也就是用来注册该依赖项属性的名称。
请注意,get和set访问器只是分别调用GetValue和SetValue。
建议依赖项属性的访问器不要包含其他逻辑,这是因为客户端和WPF可绕过这两个访问器直接调用GetValue和SetValue。
例如,如果属性绑定到数据源,则不会调用该属性的set访问器。
不要向get和set访问器添加其他逻辑,而应使用ValidateValueCallback、CoerceValueCallback和PropertyChangedCallback委托在值更改时进行响应或检查该值。
为CoerceValueCallback定义一个名为CoerceValue的方法。
CoerceValue确保Value大于或等于MinValue且小于或等于MaxValue。