[WPF] UserControl vs CustomControl
介绍
WPF中有两种控件:UserControl和CustomControl,但是这两者有什么区别呢?这篇博客中将介绍两者之间的区别,这样可以在项目中合理的使用它们。
UserControl
- 将多个WPF控件(例如:TextBox,TextBlock,Button)进行组合成一个可复用的控件组;
- 由XAML和Code Behind代码组成;
- 不支持样式/模板重写;
- 继承自UserControl;
下面创建的一个RGBControl由3个TextBlock,3个TextBox,1个Rectangle组成。我们可以在WPF的任意窗体/Page上面复用该UserControl。
XAML Code:
<Grid Background="LightGray"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="2*" /> </Grid.ColumnDefinitions> <TextBlock Text="Red" /> <TextBlock Text="Green" Grid.Row="1" /> <TextBlock Text="Blue" Grid.Row="2" /> <TextBox Text="{Binding Red, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Grid.Column="1" Height="25" Width="80" Margin="0,5" /> <TextBox Text="{Binding Green, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Grid.Row="1" Grid.Column="1" Height="25" Width="80" Margin="0,5" /> <TextBox Text="{Binding Blue, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Grid.Row="2" Grid.Column="1" Height="25" Width="80" Margin="0,5" /> <Rectangle Fill="{Binding Color, Converter={StaticResource ColorToSolidBrushConverter}}" Grid.Column="2" Grid.RowSpan="3" Margin="10, 5" Width="100" Height="100"/> </Grid>
C# Code
public partial class RGBControl : UserControl { public RGBControl() { InitializeComponent(); this.DataContext = new RGBViewModel(); } } public class RGBViewModel : ObservableObject { private byte _red = 0; public byte Red { get { return _red; } set { if(_red != value) { _red = value; RaisePropertyChanged("Red"); RaiseColorChanged(); } } } private byte _green = 0; public byte Green { get { return _green; } set { if(_green != value) { _green = value; RaisePropertyChanged("Green"); RaiseColorChanged(); } } } private byte _blue = 0; public byte Blue { get { return _blue; } set { if(_blue != value) { _blue = value; RaisePropertyChanged("Blue"); RaiseColorChanged(); } } } private Color _color; public Color Color { get { return _color; } set { RaiseColorChanged(); } } private void RaiseColorChanged() { _color = Color.FromRgb(Red, Green, Blue); RaisePropertyChanged("Color"); } } public class ObservableObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void RaisePropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public class ColorToSolidBrushConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Color color = (Color)value; return new SolidColorBrush(color); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } }
使用RGBControl:
<Grid> <local:RGBControl Width="320" Height="120"/> </Grid>
CustomControl
- 自定义控件,扩展自一个已经存在的控件,并添加新的功能/特性;
- 由C#/VB.NET Code和样式文件组成(Themes/Generic.xaml);
- 支持样式/模板重写;
- 如果项目中自定义控件较多,建议创建一个WPF自定义控件库(WPF Control Library)
怎样创建一个WPF CustomControl呢?
选择合适的控件基类,或者说选择合适的控件进行功能扩展
UIElement 最轻量级的基类,支持Layout, Input, Focus, Event
FrameworkElement 继承自UIElement,支持styling,tooltips,context menus,data binding,resouce look up
Control 最基础的控件,支持template, 并增加了一些额外属性,例如Foreground, Background, FontSize等
ContentControl 在Control的基础上增加了Content属性,常见的控件有,布局控件,Button等
HeaderedContentControl 在ContentControl基础增加了一个Header属性,常见的控件有:Expander,TabControl,GroupBox等
ItemsControl 一个具有Items集合的控件,用来展示数据,但是不包含 Selection 特性
Selector 是一个ItemsControl,增加了Indexed,Selected特性,典型的控件有: ListBox, ComboBox, ListView, TabControl等
RangeBase 典型的控件有Sliders, ProgressBars. 增加了Value,Minimum和Maximum属性
WPF的控件行为和表现是分离的。行为在Code中定义,Template在XAML中定义。
重写Default Style
static NumericTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericTextBox), new FrameworkPropertyMetadata(typeof(NumericTextBox))); }
重写默认样式文件
<Style TargetType="{x:Type local:NumericTextBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:NumericTextBox}"> ... </ControlTemplate> </Setter.Value> </Setter> </Style>
以一个Numeric up/down控件为例:控件如下:
很直观的可以看到,Numeric up/down TextBox可以通过扩展WPF的TextBox控件实现,在WPF TextBox的基础上添加两个Button,然后重写这个自定义控件样式。
C# Code:
[TemplatePart(Name = UpButtonKey, Type = typeof(Button))] [TemplatePart(Name = DownButtonKey, Type = typeof(Button))] public class NumericTextBox : TextBox { private const string UpButtonKey = "PART_UpButton"; private const string DownButtonKey = "PART_DownButton"; private Button _btnUp = null; private Button _btnDown = null; static NumericTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericTextBox), new FrameworkPropertyMetadata(typeof(NumericTextBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); _btnUp = Template.FindName(UpButtonKey, this) as Button; _btnDown = Template.FindName(DownButtonKey, this) as Button; _btnUp.Click += delegate { Operate("+"); }; _btnDown.Click += delegate { Operate("-"); }; } private void Operate(string operation) { int input = 0; if(int.TryParse(this.Text, out input)) { if (operation == "+") { this.Text = (input + 1).ToString(); } else { this.Text = (input - 1).ToString(); } } } }
Style Code:
<Style TargetType="{x:Type local:NumericTextBox}"> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="FontSize" Value="12" /> <Setter Property="Height" Value="40" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:NumericTextBox}"> <Border x:Name="OuterBorder" BorderBrush="LightGray" BorderThickness="1"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="30" /> </Grid.ColumnDefinitions> <Border Grid.ColumnSpan="2" Grid.RowSpan="2" Background="White"> <ScrollViewer x:Name="PART_ContentHost" Margin="5,0" VerticalAlignment="Center" FontSize="12" /> </Border> <Button x:Name="PART_UpButton" Grid.Column="1" Content="+" VerticalContentAlignment="Center" /> <Button x:Name="PART_DownButton" Grid.Row="1" Grid.Column="1" Content="-" VerticalContentAlignment="Center" /> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
使用:
<StackPanel> <custom:NumericTextBox Width="200" Text="1" /> </StackPanel>
感谢您的阅读~
参考文章: