WPF上位机自定义控件系列:步骤 WxStepBar
WPF 上位机自定义控件系列
- 步骤 WxStepBar
类似 ListBox、TabControl 等控件,包含两项内容:WxStepBar 和 WxStepBarItem,分别继承自 ContentControl 和 ItemsControl,支持数据绑定
WxStepBarItem 控件,包含三种状态
public enum StepBarState
Default,
Busy,
Complete,
using System.Windows;
using System.Windows.Controls;
namespace WpfControlsX.ControlX
/// ----------------------------------------------------------------
/// Author : BigWang
/// Created Time: 2023/4/4 22:32:06
/// Description :
/// ----------------------------------------------------------------
/// Version Modified Time Modified By Modified Content
/// V1.0.0.0 2023/4/4 22:32:06 BigWang 首次编写
public class WxStepBarItem : ContentControl
/// <summary>
/// 序号
/// </summary>
public int Index
get => (int)GetValue(IndexProperty);
internal set => SetValue(IndexProperty, value);
public static readonly DependencyProperty IndexProperty =
DependencyProperty.Register("Index", typeof(int), typeof(WxStepBarItem), new PropertyMetadata(-1));
/// <summary>
/// 当前状态
/// </summary>
public StepBarState State
get => (StepBarState)GetValue(StateProperty);
set => SetValue(StateProperty, value);
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(StepBarState), typeof(WxStepBarItem), new PropertyMetadata(StepBarState.Default));
/// <summary>
/// 图标尺寸
/// </summary>
public double IconSize
get => (double)GetValue(IconSizeProperty);
set => SetValue(IconSizeProperty, value);
public static readonly DependencyProperty IconSizeProperty =
DependencyProperty.Register("IconSize", typeof(double), typeof(WxStepBarItem), new PropertyMetadata(12d));
/// <summary>
/// 圆角
/// </summary>
public CornerRadius CornerRadius
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(WxStepBarItem), new PropertyMetadata(new CornerRadius(0)));
WxStepBar 控件
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace WpfControlsX.ControlX
[TemplatePart(Name = ProgressBarTemplateName, Type = typeof(ProgressBar))]
public class WxStepBar : ItemsControl
static WxStepBar()
DefaultStyleKeyProperty.OverrideMetadata(typeof(WxStepBar), new FrameworkPropertyMetadata(typeof(WxStepBar)));
private const string ProgressBarTemplateName = "PART_ProgressBar";
private ProgressBar _progressBar;
public int StepIndex
get => (int)GetValue(StepIndexProperty);
set => SetValue(StepIndexProperty, value);
public static readonly DependencyProperty StepIndexProperty =
DependencyProperty.Register("StepIndex", typeof(int), typeof(WxStepBar), new PropertyMetadata(0, OnStepIndexChanged));
private static void OnStepIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
WxStepBar step = (WxStepBar)d;
int stepIndex = (int)e.NewValue;
step.UpdateStepItemState(stepIndex);
private void UpdateStepItemState(int stepIndex)
int count = Items.Count;
if (count <= 0)
return;
for (int i = 0; i < Items.Count; i++)
if (ItemContainerGenerator.ContainerFromIndex(i) is WxStepBarItem stepItem)
stepItem.State = i < stepIndex ? StepBarState.Complete : i == stepIndex ? StepBarState.Busy : StepBarState.Default;
public override void OnApplyTemplate()
base.OnApplyTemplate();
_progressBar = GetTemplateChild(ProgressBarTemplateName) as ProgressBar;
protected override void OnRender(DrawingContext drawingContext)
base.OnRender(drawingContext);
int count = Items.Count;
if (_progressBar == null || count <= 0)
return;
_progressBar.Maximum = count - 1;
_progressBar.Value = StepIndex;
_progressBar.Width = ActualWidth / count * (count - 1);
protected override bool IsItemItsOwnContainerOverride(object item)
return item is WxStepBarItem;
protected override DependencyObject GetContainerForItemOverride()
return new WxStepBarItem();
public WxStepBar()
ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
public void Next()
if (StepIndex >= Items.Count)
StepIndex = Items.Count - 1;
StepIndex++;
public void Prev()
if (StepIndex < 0)
StepIndex = -1;
StepIndex--;
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
int count = Items.Count;
if (count <= 0)
return;
UpdateStepItemState(StepIndex);
xaml 样式
<!--#region WxStepBar -->
<Style x:Key="StepBarItemStyle" TargetType="{x:Type cx:WxStepBarItem}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource BrushWaterMark}"/>
<Setter Property="Foreground" Value="{DynamicResource BrushText}"/>
<Setter Property="Background" Value="{DynamicResource BrushRegion}"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="CornerRadius" Value="16"/>
<Setter Property="IconSize" Value="32"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type cx:WxStepBarItem}">
<StackPanel>
<cx:WxSimplePanel>
<Ellipse Width="{TemplateBinding IconSize}" Height="{TemplateBinding IconSize}" Fill="{TemplateBinding Background}" HorizontalAlignment="Center"/>
<Border Background="{TemplateBinding Background}" HorizontalAlignment="Center" CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"
Height="{TemplateBinding IconSize}" Width="{TemplateBinding IconSize}">
<cx:WxSimplePanel>
<TextBlock Name="PART_Index" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="{TemplateBinding FontSize}"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type cx:WxStepBarItem}}, Converter={StaticResource WxStepBarItem2Index}}"/>
<Path Name="PART_PathComplete" Data="{StaticResource IconCheck}" Fill="{TemplateBinding Foreground}" Stretch="Uniform"
Width="14" Height="14" Visibility="Collapsed"/>
</cx:WxSimplePanel>
</Border>
</cx:WxSimplePanel>
<ContentPresenter HorizontalAlignment="Center" TextElement.FontWeight="Black" TextElement.Foreground="{DynamicResource BrushText}" Margin="0,6,0,0"
ContentTemplate="{Binding ItemTemplate,RelativeSource={RelativeSource AncestorType=cx:WxStepBar}}"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="State" Value="Default">
<Setter Property="Foreground" Value="{DynamicResource BrushText}"/>
<Setter Property="Background" Value="{DynamicResource BrushRegion}"/>
<Setter TargetName="PART_PathComplete" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="PART_Index" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="State" Value="Busy">
<Setter Property="Background" Value="{DynamicResource BrushPrimary}"/>
<Setter Property="BorderBrush" Value="{DynamicResource BrushPrimary}"/>
<Setter Property="Foreground" Value="{DynamicResource BrushReversedText}"/>
<Setter TargetName="PART_PathComplete" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="PART_Index" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="State" Value="Complete">
<Setter Property="Background" Value="{DynamicResource BrushSuccess}"/>
<Setter Property="BorderBrush" Value="{DynamicResource BrushSuccess}"/>
<Setter Property="Foreground" Value="{DynamicResource BrushReversedText}"/>
<Setter TargetName="PART_PathComplete" Property="Visibility" Value="Visible"/>
<Setter TargetName="PART_Index" Property="Visibility" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type cx:WxStepBar}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Background" Value="Red"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource StepBarItemStyle}"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type cx:WxStepBar}">
<cx:WxSimplePanel>
<ProgressBar x:Name="PART_ProgressBar" Margin="0,18" Height="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Value="{Binding StepIndex,RelativeSource={RelativeSource AncestorType=cx:WxStepBar}}"/>
<ItemsPresenter/>
</cx:WxSimplePanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>