如何让自定义UserControl支持在XAML中直接添加Step实例?
实现XAML直接向自定义UserControl添加Step实例的方案
需求背景
我有一个自定义UserControl(StepProgressBar),内部是包含横向进度条和ItemsControl的Grid,ItemsControl的ItemTemplate由两个Ellipse和一个TextBlock组成。数据项是Step类,希望无需编写C#代码,直接在XAML中给StepProgressBar添加Step实例,达到如下效果:
<Grid> <local:StepProgressBar x:Name="FirmwareBar" Margin="0,0,0,0"> <local:Step Name="Test" InnerColor="Yellow" IsCompleted="false" /> <local:Step Name="Test1" InnerColor="Green" IsCompleted="true" CompletedColor="#FF7DEEAF" /> </local:StepProgressBar> </Grid>
实现步骤
1. 完善Step类(实现属性变更通知)
为了让Step的属性变化能自动同步到UI,需要实现INotifyPropertyChanged接口:
using System.ComponentModel; using System.Windows.Media; public class Step : INotifyPropertyChanged { public Step() { Name = string.Empty; } public Step(string name, Brush color) { Name = name; InnerColor = color; } private string _name; public string Name { get => _name; set { _name = value; OnPropertyChanged(nameof(Name)); } } private Brush _innerColor; public Brush InnerColor { get => _innerColor; set { _innerColor = value; OnPropertyChanged(nameof(InnerColor)); } } private Brush _outerColor; public Brush OuterColor { get => _outerColor; set { _outerColor = value; OnPropertyChanged(nameof(OuterColor)); } } private Brush _completedColor; public Brush CompletedColor { get => _completedColor; set { _completedColor = value; OnPropertyChanged(nameof(CompletedColor)); } } private bool _isCompleted; public bool IsCompleted { get => _isCompleted; set { _isCompleted = value; OnPropertyChanged(nameof(IsCompleted)); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 给StepProgressBar添加可绑定的集合依赖属性
在StepProgressBar的后台代码中,定义一个ObservableCollection<Step>类型的依赖属性,并通过ContentPropertyAttribute指定该属性为控件的内容属性,这样XAML中直接编写的子元素会自动添加到这个集合中:
using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; [ContentProperty(nameof(StepsCollection))] public partial class StepProgressBar : UserControl { public static readonly DependencyProperty StepsCollectionProperty = DependencyProperty.Register(nameof(StepsCollection), typeof(ObservableCollection<Step>), typeof(StepProgressBar), new PropertyMetadata(new ObservableCollection<Step>())); public ObservableCollection<Step> StepsCollection { get => (ObservableCollection<Step>)GetValue(StepsCollectionProperty); set => SetValue(StepsCollectionProperty, value); } public StepProgressBar() { InitializeComponent(); // 将ItemsControl的数据源绑定到StepsCollection ItemsControlSteps.ItemsSource = StepsCollection; } }
3. 修改StepProgressBar的XAML布局
调整横向进度条的自适应布局,同时给ItemsControl指定名称以便后台绑定:
<UserControl x:Class="YourNamespace.StepProgressBar" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid> <!-- 自适应宽度的横向进度条 --> <Rectangle Height="7" Fill="#FF00A8FF" Margin="10,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Center"/> <!-- 步骤项容器 --> <ItemsControl x:Name="ItemsControlSteps"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" VerticalAlignment="Center"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <DataTemplate.Resources> <Style TargetType="{x:Type Ellipse}"> <Setter Property="Margin" Value="10,15,0,0"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> <Style TargetType="{x:Type TextBlock}"> <Setter Property="Margin" Value="10,0,0,0"/> <Setter Property="TextTrimming" Value="CharacterEllipsis"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> </DataTemplate.Resources> <StackPanel MaxWidth="75"> <Grid> <Ellipse x:Name="EllipseOuter" Width="20" Height="20" Fill="#FF00A8FF"/> <Ellipse x:Name="EllipseInner" Width="10" Height="10" Fill="{Binding InnerColor}"/> </Grid> <TextBlock Text="{Binding Name}"/> </StackPanel> <DataTemplate.Triggers> <DataTrigger Binding="{Binding IsCompleted}" Value="true"> <Setter TargetName="EllipseInner" Property="Fill" Value="{Binding CompletedColor}"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </UserControl>
4. 在MainWindow中使用
现在可以直接在XAML中给StepProgressBar添加Step实例,无需额外C#代码:
<Window x:Class="YourNamespace.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:YourNamespace" Title="MainWindow" Height="450" Width="800"> <Grid> <local:StepProgressBar x:Name="FirmwareBar" Margin="20"> <local:Step Name="Test" InnerColor="Yellow" IsCompleted="false"/> <local:Step Name="Test1" InnerColor="Green" IsCompleted="true" CompletedColor="#FF7DEEAF"/> <local:Step Name="Test2" InnerColor="Orange" IsCompleted="false"/> </local:StepProgressBar> </Grid> </Window>
关键说明
[ContentProperty(nameof(StepsCollection))]:告诉WPF将控件的子元素自动填充到StepsCollection集合中,无需手动绑定。ObservableCollection<Step>:保证集合的增删操作能自动通知UI更新,结合INotifyPropertyChanged实现单个Step属性变化的实时同步。- 移除了原DataTemplate中不必要的
IsCompleted=false触发器,因为默认已绑定InnerColor,仅在IsCompleted=true时替换填充色即可。
内容的提问来源于stack exchange,提问作者Zero _000
相关产品推荐
相关产品推荐

