WPF开发快速入门【4】自定义控件与用户控件

  • WPF开发快速入门【4】自定义控件与用户控件已关闭评论
  • 162 次浏览
  • A+
所属分类:.NET技术
摘要

概述 本文描述WPF的自定义控件和用户控件。   自定义控件 前面文章介绍了WPF的ControlTemplate,当我们对系统控件自带的样式不太满意时,我们可以通过控件模板自定义用户的样式,以Button为例,我们可以设计一个圆形的按钮,并通过触发器控制一些动态效果。在使用控件模板时,我们通过TemplateBinding来引用控件的一些属性,这个属性的范围仅限于Button本身所拥有的属性。

概述

本文描述WPF的自定义控件和用户控件。

 

自定义控件

前面文章介绍了WPF的ControlTemplate,当我们对系统控件自带的样式不太满意时,我们可以通过控件模板自定义用户的样式,以Button为例,我们可以设计一个圆形的按钮,并通过触发器控制一些动态效果。在使用控件模板时,我们通过TemplateBinding来引用控件的一些属性,这个属性的范围仅限于Button本身所拥有的属性。

如果我想设计一款带图片的按钮,通过控件模板就实现不了了,因为这个图片按钮的控件应该具备一个类似Image这样的属性,但Button控件没有这个属性,所以就实现不了我们想要的功能了。

这时候可以使用自定义控件来解决问题。

我们新建一个ImageButton的自定义控件,系统自动生成一个类文件和一个样式文件: 

//ImageButton.cs     public class ImageButton : Control     {         static ImageButton()         {             DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));         }     }  //Generic.xaml     <Style TargetType="{x:Type local:ImageButton}">         <Setter Property="Template">             <Setter.Value>                 <ControlTemplate TargetType="{x:Type local:ImageButton}">                     <Border Background="{TemplateBinding Background}"                             BorderBrush="{TemplateBinding BorderBrush}"                             BorderThickness="{TemplateBinding BorderThickness}">                     </Border>                 </ControlTemplate>             </Setter.Value>         </Setter>     </Style>

 首先我们需要把ImageButton 父类修改为Button,表示这个控件功能继承于Button,然后我们为这个类增加一个Image属性

        public ImageSource Image         {             get { return (ImageSource)GetValue(ImageProperty); }             set { SetValue(ImageProperty, value); }         }          public static readonly DependencyProperty ImageProperty =             DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(null));

此时,这个控件的后台代码就完成了。

然后我们再仔细看看Generic.xaml中对于local:ImageButton这个控件的样式描述,这不就是修改它的控件模板吗?不过此时,除了Button的现有属性,我们还多了一个Image属性可以使用。

下面我们完善一下这个控件模板的描述。

    <Style TargetType="{x:Type local:ImageButton}">         <Setter Property="Template">             <Setter.Value>                 <ControlTemplate TargetType="{x:Type local:ImageButton}">                     <Border Background="{TemplateBinding Background}"                             BorderBrush="Gray"                             BorderThickness="1">                         <StackPanel Orientation="Horizontal">                             <Image Source="{TemplateBinding Image}"/>                             <Label Content="{TemplateBinding Content}" VerticalAlignment="Center"/>                         </StackPanel>                     </Border>                 </ControlTemplate>             </Setter.Value>         </Setter>     </Style>

 这样一个自定义控件就做好了。

WPF开发快速入门【4】自定义控件与用户控件 

 和控件模板一样,我们还是可以通过Trigger控制控件的一些动态效果:

    <Style TargetType="{x:Type local:ImageButton}">         <Setter Property="Template">             <Setter.Value>                 <ControlTemplate TargetType="{x:Type local:ImageButton}">                     <Border x:Name="border">                         ...                     </Border>                                        <ControlTemplate.Triggers>                         <Trigger Property="IsMouseOver" Value="True">                             <Setter TargetName="border" Property="Background" Value="LightBlue" />                             <Setter TargetName="border" Property="BorderBrush" Value="Gray" />                             <Setter TargetName="image" Property="Margin" Value="2" />                         </Trigger>                         <Trigger Property="IsEnabled" Value="False">                             <Setter TargetName="border" Property="Background" Value="LightGray" />                             <Setter TargetName="image" Property="Opacity" Value="0.2" />                         </Trigger>                     </ControlTemplate.Triggers>                 </ControlTemplate>             </Setter.Value>         </Setter>     </Style>

  

用户控件

用户控件比较简单,就是通过一些现有控件的组合,形成一个可以通用的控件。例如:通过组合一个加号的图片、一个减号的图片、一个文本框,我们可以组合一个NumericUpDown控件。 

<UserControl x:Class="LearnWPF.Controls.NumericUpDown">         <Border BorderThickness="1" BorderBrush="LightGray" >         <Grid >             <Grid.ColumnDefinitions>                 <ColumnDefinition Width="30"/>                 <ColumnDefinition Width="50"/>                 <ColumnDefinition Width="30"/>             </Grid.ColumnDefinitions>             <Border Grid.Column="0" >                 <Image Source="Images/Subtract.png" Margin="4"                        MouseDown="ButtonSubtract_Click">                 </Image>             </Border>             <TextBox x:Name="txtValue" Text="1.220"                                            TextChanged="txtValue_TextChanged"/>             <Border Grid.Column="2">                 <Image Source="Images/Add.png" Margin="4"                        MouseDown="ButtonAdd_Click">                 </Image>             </Border>         </Grid>     </Border> </UserControl>

 我们可以给控件增加一个Value的属性,然后当用户点击图片按钮时,我们修改Value的值即可。

        private void ButtonAdd_Click(object sender, RoutedEventArgs e)         {             Value += Increment;                    }          private void ButtonSubtract_Click(object sender, RoutedEventArgs e)         {             Value -= Increment;                     }

 此时,这个用户控件就开发完成了。但它不支持MVVM模式,因为Value属性不是依赖属性,我们需要把Value属性定义为依赖属性:

        public decimal Value         {             get { return (decimal)GetValue(ValueProperty); }             set { SetValue(ValueProperty, value); }         }          public static readonly DependencyProperty ValueProperty =                DependencyProperty.Register("Value", typeof(decimal), typeof(NumericUpDown), new UIPropertyMetadata(new decimal(), ValuePropertyChanged));          private static void ValuePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arg)         {             NumericUpDown control = obj as NumericUpDown;             decimal Data = (decimal)arg.NewValue;             control.txtValue.Text = Math.Round(Data, control.DecimalPlaces).ToString();         }

 此时Value属性就支持MVVM,而且支持双向Binding模式

<xyc:NumericUpDown Value="{Binding Hour,Mode=TwoWay}" Minimum="0" Maximum="23"/>

 由于 Minimum和Maximum不是依赖属性,只能直接幅值,不能绑定。

 

自绘的用户控件

有时候我们需要做一些很奇怪的控件,如下:

WPF开发快速入门【4】自定义控件与用户控件

这个也是通过用户控件实现。我的经验是:在用户控件里放一个Image控件,缩放模式设置为Fill,然后在后台画个图并复值给Image控件即可。

在绘图时,由于用户会调整控件尺寸,所以绘图控件的定位是比较麻烦的,我们可以画一个指定尺寸的图,然后让Image来处理缩放,这就比较简单了。

设计代码:

<UserControl x:Class="LearnWPF.Controls.Clock"     <Grid>      
<Image x:Name="imageBitmap" Stretch="Fill" /> </Grid> </UserControl>

 后台代码:

    public partial class Clock : UserControl     {                 public Clock()         {             InitializeComponent();             this.Loaded += Clock_Loaded;         }          private void Clock_Loaded(object sender, RoutedEventArgs e)         {             DrawImage();         }          private void DrawImage()         {             DrawingGroup group = new DrawingGroup();             using (DrawingContext ctx = group.Open())             {                 DrawImageByContext(ctx);             }             group.Freeze();             DrawingImage drawimage = new DrawingImage(group);             this.imageBitmap.Source = drawimage;         }          private void DrawImageByContext(DrawingContext ctx)         {             int PicWidth = 500;             int PicHeight = 500;                       ctx.DrawRectangle(Brushes.Transparent, null, new Rect(0, 0, PicWidth, PicHeight));             //拿着ctx尽情绘图吧                    }             }

   

资源

系列目录:WPF开发快速入门【0】前言与目录 

代码下载:Learn WPF: WPF学习笔记 (gitee.com)