概述 本文描述WPF的自定義控制項和用戶控制項。 自定義控制項 前面文章介紹了WPF的ControlTemplate,當我們對系統控制項自帶的樣式不太滿意時,我們可以通過控制項模板自定義用戶的樣式,以Button為例,我們可以設計一個圓形的按鈕,並通過觸發器控制一些動態效果。在使用控制項模板時,我們通過Temp ...
概述
本文描述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>
這樣一個自定義控制項就做好了。
和控制項模板一樣,我們還是可以通過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不是依賴屬性,只能直接幅值,不能綁定。
自繪的用戶控制項
有時候我們需要做一些很奇怪的控制項,如下:
這個也是通過用戶控制項實現。我的經驗是:在用戶控制項里放一個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)
簽名區:
如果您覺得這篇博客對您有幫助或啟發,請點擊右側【推薦】支持,謝謝!