介紹
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>
感謝您的閱讀~
參考文章: