资讯动态

WPF 自定义控件

发布时间:2026/9/3 7:30:19 来源:尧图企业网站定制
当现有控件不满足业务需要时WPF也支持自定义控件。以下提供一个评分控件以供学习!--StarRating.xaml-- ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:ctrlsclr-namespace:CustomControlDemo.Controls !--没有 x:Key默认样式自动应用到所有 StarRating-- Style TargetType{x:Type ctrls:StarRating} Setter PropertyHorizontalAlignment ValueLeft/ Setter PropertyTemplate Setter.Value ControlTemplate TargetType{x:Type ctrls:StarRating} !--①ItemsControl ItemsSource{Binding Stars, RelativeSource{RelativeSource AncestorTypectrls:StarRating}}-- !--②ItemsControl ItemsSource{TemplateBinding Stars}-- !--①②与下面这行等价不过用TemplateBinding和TemplatedParent时要求父节点为ControlTemplate-- ItemsControl ItemsSource{Binding Stars, RelativeSource{RelativeSource TemplatedParent}} !--ItemsSource要求Stars是一个集合比如List,Dictionary,ObservableCollection等-- ItemsControl.ItemsPanel ItemsPanelTemplate StackPanel OrientationHorizontal/ /ItemsPanelTemplate /ItemsControl.ItemsPanel ItemsControl.ItemTemplate DataTemplate !--Path data用于描述控件形状本例中11个点坐标围成五角星-- Path DataM12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z Width{Binding StarSize, RelativeSource{RelativeSource AncestorTypectrls:StarRating}} Height{Binding StarSize, RelativeSource{RelativeSource AncestorTypectrls:StarRating}} Margin2 CursorHand Path.Style Style TargetTypePath Setter PropertyFill Value{Binding EmptyStarBrush, RelativeSource{RelativeSource AncestorTypectrls:StarRating}}/ Style.Triggers DataTrigger Binding{Binding} ValueTrue Setter PropertyFill Value{Binding FilledStarBrush, RelativeSource{RelativeSource AncestorTypectrls:StarRating}}/ /DataTrigger /Style.Triggers /Style /Path.Style /Path /DataTemplate /ItemsControl.ItemTemplate /ItemsControl /ControlTemplate /Setter.Value /Setter /Style Style x:KeyBlueTheme TargetType{x:Type ctrls:StarRating} BasedOn{StaticResource {x:Type ctrls:StarRating}} Setter PropertyFilledStarBrush Value#2196F3/ Setter PropertyEmptyStarBrush Value#BBDEFB/ /Style Style x:KeyRedLargeTheme TargetType{x:Type ctrls:StarRating} BasedOn{StaticResource {x:Type ctrls:StarRating}} Setter PropertyFilledStarBrush Value#F44336/ Setter PropertyEmptyStarBrush Value#FFCDD2/ Setter PropertyStarSize Value50/ /Style Style x:KeyCircleTheme TargetType{x:Type ctrls:StarRating} Setter PropertyHorizontalAlignment ValueLeft/ Setter PropertyTemplate Setter.Value ControlTemplate TargetType{x:Type ctrls:StarRating} ItemsControl ItemsSource{Binding Stars, RelativeSource{RelativeSource TemplatedParent}} ItemsControl.ItemsPanel ItemsPanelTemplate StackPanel OrientationHorizontal/ /ItemsPanelTemplate /ItemsControl.ItemsPanel ItemsControl.ItemTemplate DataTemplate Ellipse Width{Binding StarSize, RelativeSource{RelativeSource AncestorTypectrls:StarRating}} Height{Binding StarSize, RelativeSource{RelativeSource AncestorTypectrls:StarRating}} Margin3 CursorHand Ellipse.Style Style TargetTypeEllipse Setter PropertyFill Value{Binding EmptyStarBrush, RelativeSource{RelativeSource AncestorTypectrls:StarRating}}/ Setter PropertyStroke ValueGray/ Setter PropertyStrokeThickness Value2/ Style.Triggers !--Binding{Binding}意思是绑定到当前默认的DataContext在此例中就是Stars中的每一个Bool元素-- DataTrigger Binding{Binding} ValueTrue Setter PropertyFill Value{Binding FilledStarBrush, RelativeSource{RelativeSource AncestorTypectrls:StarRating}}/ /DataTrigger /Style.Triggers /Style /Ellipse.Style /Ellipse /DataTemplate /ItemsControl.ItemTemplate /ItemsControl /ControlTemplate /Setter.Value /Setter /Style /ResourceDictionary //StarRating.cs using System; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace CustomControlDemo.Controls { public class StarRating : Control { public static readonly DependencyProperty RatingProperty DependencyProperty.Register( Rating, typeof(int), typeof(StarRating), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnRatingChanged)); public static readonly DependencyProperty MaxRatingProperty DependencyProperty.Register( MaxRating, typeof(int), typeof(StarRating), new PropertyMetadata(1, OnRatingChanged)); public static readonly DependencyProperty FilledStarBrushProperty DependencyProperty.Register( FilledStarBrush, typeof(Brush), typeof(StarRating), new PropertyMetadata(Brushes.Gold)); public static readonly DependencyProperty EmptyStarBrushProperty DependencyProperty.Register( EmptyStarBrush, typeof(Brush), typeof(StarRating), new PropertyMetadata(Brushes.LightGray)); public static readonly DependencyProperty StarSizeProperty DependencyProperty.Register( StarSize, typeof(double), typeof(StarRating), new PropertyMetadata(30.0)); // 星星列表直接供 ItemsControl 绑定 public ObservableCollectionbool Stars { get; } new ObservableCollectionbool(); public event EventHandlerint RatingChanged; static StarRating() { //注册StarRating的默认Style和ControlTemplate, WPF将会去Generic.xaml中寻找 DefaultStyleKeyProperty.OverrideMetadata( typeof(StarRating), new FrameworkPropertyMetadata(typeof(StarRating))); } public StarRating() { UpdateStars(); } public int Rating { get (int)GetValue(RatingProperty); set SetValue(RatingProperty, value); } public int MaxRating { get (int)GetValue(MaxRatingProperty); set SetValue(MaxRatingProperty, value); } public Brush FilledStarBrush { get (Brush)GetValue(FilledStarBrushProperty); set SetValue(FilledStarBrushProperty, value); } public Brush EmptyStarBrush { get (Brush)GetValue(EmptyStarBrushProperty); set SetValue(EmptyStarBrushProperty, value); } public double StarSize { get (double)GetValue(StarSizeProperty); set SetValue(StarSizeProperty, value); } private static void OnRatingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control (StarRating)d; control.UpdateStars(); } // Rating 或 MaxRating 变化时更新星星列表 private void UpdateStars() { Stars.Clear(); for (int i 1; i MaxRating; i) { Stars.Add(i Rating); } } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); var position e.GetPosition(this); var starWidth ActualWidth / MaxRating; var clickedStar (int)(position.X / starWidth) 1; if (clickedStar 1 clickedStar MaxRating) { Rating clickedStar; RatingChanged?.Invoke(this, clickedStar); } } } } !--Mainwindow.xaml-- Window x:ClassCustomControlDemo.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:ctrlsclr-namespace:CustomControlDemo.Controls xmlns:localclr-namespace:CustomControlDemo TitleCustomControl 示例 Height400 Width500 StackPanel Margin20 TextBlock TextCustomControl 演示 - 可以换肤 FontSize20 FontWeightBold Margin0,0,0,20/ TextBlock Text默认金色星星 Margin0,0,0,5/ ctrls:StarRating Rating{Binding Rating1, ModeTwoWay} MaxRating{Binding MaxRating} Margin0,0,0,15/ TextBlock Text蓝色主题 Margin0,0,0,5/ ctrls:StarRating Rating{Binding Rating2, ModeTwoWay} MaxRating{Binding MaxRating} Style{StaticResource BlueTheme} Margin0,0,0,15/ TextBlock Text红色大尺寸主题 Margin0,0,0,5/ ctrls:StarRating Rating{Binding Rating3, ModeTwoWay} MaxRating{Binding MaxRating} Style{StaticResource RedLargeTheme} Margin0,0,0,15/ TextBlock Text圆形主题 Margin0,0,0,5/ ctrls:StarRating Rating{Binding Rating4, ModeTwoWay} MaxRating{Binding MaxRating} Style{StaticResource CircleTheme} Margin0,0,0,15/ TextBlock Text{Binding ResultText} FontSize14 ForegroundBlue Margin0,10,0,0/ /StackPanel /Window !--App.xaml-- Application x:ClassCustomControlDemo.App xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml Application.Resources ResourceDictionary ResourceDictionary.MergedDictionaries ResourceDictionary Sourcepack://application:,,,/CustomControlDemo;Component/Themes/Generic.xaml/ /ResourceDictionary.MergedDictionaries /ResourceDictionary /Application.Resources /Application //App.xaml.cs using System; using System.Windows; namespace CustomControlDemo { public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var window new MainWindow();//创建UI窗口 window.DataContext new MainViewModel();//注入ViewModel window.Show();//显示窗口 } } } !--Generic.xaml-- ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml ResourceDictionary.MergedDictionaries ResourceDictionary Sourcepack://application:,,,/CustomControlDemo;Component/Controls/Styles/StarRating.xaml/ /ResourceDictionary.MergedDictionaries /ResourceDictionary //MainViewModel.cs using System.ComponentModel; using System.Runtime.CompilerServices; namespace CustomControlDemo { public class MainViewModel : INotifyPropertyChanged { private int _rating1 3; private int _rating2 4; private int _rating3 2; private int _rating4 3; private int _maxRating 6; public int Rating1 { get _rating1; set { if (_rating1 ! value) { _rating1 value; OnPropertyChanged(); OnPropertyChanged(nameof(ResultText)); } } } public int Rating2 { get _rating2; set { if (_rating2 ! value) { _rating2 value; OnPropertyChanged(); } } } public int Rating3 { get _rating3; set { if (_rating3 ! value) { _rating3 value; OnPropertyChanged(); } } } public int Rating4 { get _rating4; set { if (_rating4 ! value) { _rating4 value; OnPropertyChanged(); } } } public int MaxRating { get _maxRating; set { if (_maxRating ! value) { _maxRating value; OnPropertyChanged(); OnPropertyChanged(nameof(ResultText)); } } } public string ResultText $当前评分: {Rating1} / {MaxRating}; public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }说明1.ControlTemplate用法所有继承Control的控件有一个ControlTemplate类型的属性Template 可用于挂载ControlTemplate模板定义控件整体的外观和布局。(整体外观由ControlTemplate控制)2. Datatemplate用法xaml中写的Datatemplate被用于赋值给ContentControl控件的ContentTemplate或ItemsControl控件的ItemTemplate(二者都是Datatemplate类型的依赖属性), 当wpf要去渲染控件的Content或ItemsSource时会按照ContentTemplate/ItemTemplate 中定义的样子对content/ItemsSource数据进行绘制(内部个体外观由DataTemplate控制)。这样拥有Content/ItemsSource属性的控件被称为数据呈现控件可以设置DataTemplate让它显示复杂的业务对象。而像布局类控件(Grid, StackPanelBorder等)和纯视觉控件(TextBlock等)没有Content或ItemsSource这样的数据承载属性因此无法设置Datatemplate。3. ControlTemplate和Datatemplate 内部都只能放一个根控件如果想要放多个则需要先放布局控件作为根再在布局控件里面放置多个。4. 上述StarRating控件继承自Control是利用ControlTemplate完全重绘的一个自包含型控件它默认不支持DataTemplate但它在ControlTemplate内部硬编码实例化了ItemsControl, 并在ItemsControl内部使用DataTemplate这是合法的。5. 继承ContentControl或ItemsControl时控件制作方只需负责边框、阴影、标题栏、布局等整体设计控件内部的具体内容由用户确定用户可以在该控件内放任意UI。如果不需要将内部UI开放给用户则应该直接继承Control由控件的提供者确定控件内外的全部形态。6. 在继承ContentControl或ItemsControl实现自定义控件时需要在Controltemplate模板中使用ContentPresenter或ItemsPresenter占位引导ContentContentTemplate或ItemsPanel在控件指定位置展开。ContentPresenter可显式绑定Content和ContentTemplate(默认隐式从父控件找)而ItemsPresenter不能显示绑定ItemsPanel。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价