基于WPF实现步骤控件的示例代码

Fronde ·
更新时间:2024-05-19
· 631 次阅读

WPF 实现步骤控件

框架使用.NET40

Visual Studio 2019;

Step 继承 ItemsControl 使用 Grid 嵌套 ProgressBar 和 ItemsPresenter.

ProgressBar 用来当作步骤后面的线条,宽等于控件的(ActualWidth / Items.Count) * (Items.Count - 1),Maximum = Items.Count - 1

ItemsPresenter 用来展示步骤 Item 。

ItemsPanel - ItemsPanelTemplate - UniformGrid Rows="1" 横向展示,UniformGrid Columns="1" 可以控制竖向显示,只不过需要重新自定义 ItemContainerStyle 的样式。

然后创建 StepItem 继承 ContentControl 增加两个属性 Index 用来记录当前是步骤 与 State 记录状态 (等待中、进行中、已完成)。

因为继承了 ContentControl 所以可以在使用时指定 Content 显示内容,在每个步骤下方显示。

实现代码

1) Step.xaml 代码如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                     xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"                     xmlns:controls="clr-namespace:WPFDevelopers.Controls"                     xmlns:converts="clr-namespace:WPFDevelopers.Converts">     <ResourceDictionary.MergedDictionaries>         <ResourceDictionary Source="Basic/ControlBasic.xaml"/>     </ResourceDictionary.MergedDictionaries>     <converts:IndexConverter x:Key="IndexConverter"/>     <Style x:Key="DefaultStepItem" TargetType="{x:Type controls:StepItem}"            BasedOn="{StaticResource ControlBasicStyle}">         <Setter Property="BorderThickness" Value="1"/>         <Setter Property="Template">             <Setter.Value>                 <ControlTemplate TargetType="{x:Type controls:StepItem}">                     <StackPanel>                         <controls:SmallPanel>                             <Ellipse                                      Width="45"                                     Height="30"                                     Fill="{DynamicResource WindowForegroundColorBrush}"                                     HorizontalAlignment="Center"/>                             <Border                                          Background="{TemplateBinding Background}"                                         HorizontalAlignment="Center"                                         CornerRadius="15"                                         BorderThickness="{TemplateBinding BorderThickness}"                                         BorderBrush="{TemplateBinding BorderBrush}"                                         Height="30"                                          Width="30">                                 <controls:SmallPanel>                                     <TextBlock Foreground="{TemplateBinding Foreground}"                                                         VerticalAlignment="Center"                                                        HorizontalAlignment="Center"                                                        FontSize="{TemplateBinding FontSize}"                                                        Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:StepItem}}, Converter={StaticResource IndexConverter}}"                                                        Name="PART_Index"/>                                     <Path Data="{StaticResource PathComplete}"                                                   Fill="{TemplateBinding Foreground}"                                                   Stretch="Uniform"                                                   Width="12"                                                   Height="12"                                                   Name="PART_PathComplete"                                                   Visibility="Collapsed"/>                                 </controls:SmallPanel>                             </Border>                         </controls:SmallPanel>                         <ContentPresenter HorizontalAlignment="Center"                                                    TextElement.FontWeight="Black"                                                   ContentTemplate="{Binding ItemTemplate,RelativeSource={RelativeSource AncestorType=controls:Step}}"                                                   TextElement.Foreground="{DynamicResource RegularTextSolidColorBrush}"                                                   Margin="0,6,0,0"/>                     </StackPanel>                     <ControlTemplate.Triggers>                         <Trigger Property="Status" Value="Waiting">                             <Setter Property="Foreground" Value="{DynamicResource PrimaryTextSolidColorBrush}"/>                             <Setter Property="Visibility" TargetName="PART_PathComplete" Value="Collapsed"/>                             <Setter Property="Visibility" TargetName="PART_Index" Value="Visible"/>                             <Setter Property="Background" Value="{DynamicResource BaseSolidColorBrush}"/>                         </Trigger>                         <Trigger Property="Status" Value="InProgress">                             <Setter Property="Foreground" Value="{DynamicResource DefaultBackgroundSolidColorBrush}"/>                             <Setter Property="Visibility" TargetName="PART_PathComplete" Value="Collapsed"/>                             <Setter Property="Visibility" TargetName="PART_Index" Value="Visible"/>                             <Setter Property="Background" Value="{DynamicResource PrimaryNormalSolidColorBrush}"/>                         </Trigger>                         <Trigger Property="Status" Value="Complete">                             <Setter Property="BorderBrush" Value="{DynamicResource DefaultBackgroundSolidColorBrush}"/>                             <Setter Property="Background" Value="{DynamicResource DefaultBackgroundSolidColorBrush}"/>                             <Setter Property="Visibility" TargetName="PART_PathComplete" Value="Visible"/>                             <Setter Property="Visibility" TargetName="PART_Index" Value="Collapsed"/>                             <Setter Property="Foreground" Value="{DynamicResource PrimaryNormalSolidColorBrush}"/>                         </Trigger>                     </ControlTemplate.Triggers>                 </ControlTemplate>             </Setter.Value>         </Setter>     </Style>     <Style x:Key="DefaultStep" TargetType="{x:Type controls:Step}"             BasedOn="{StaticResource ControlBasicStyle}">         <Setter Property="ItemContainerStyle" Value="{StaticResource DefaultStepItem}"/>         <Setter Property="VerticalContentAlignment" Value="Top"/>         <Setter Property="HorizontalContentAlignment" Value="Center"/>         <Setter Property="Template">             <Setter.Value>                 <ControlTemplate TargetType="{x:Type controls:Step}">                     <controls:SmallPanel>                         <ProgressBar x:Name="PART_ProgressBar"                                               Margin="0,18"                                              Height="1"                                              Value="{Binding StepIndex,RelativeSource={RelativeSource AncestorType=controls:Step}}"                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>                         <ItemsPresenter/>                     </controls:SmallPanel>                 </ControlTemplate>             </Setter.Value>         </Setter>         <Setter Property="ItemsPanel">             <Setter.Value>                 <ItemsPanelTemplate>                     <UniformGrid Rows="1"/>                 </ItemsPanelTemplate>             </Setter.Value>         </Setter>     </Style>     <Style TargetType="{x:Type controls:StepItem}" BasedOn="{StaticResource DefaultStepItem}" />     <Style TargetType="{x:Type controls:Step}" BasedOn="{StaticResource DefaultStep}" /> </ResourceDictionary>

2) Step.cs 代码如下:

using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; namespace WPFDevelopers.Controls {     [TemplatePart(Name = ProgressBarTemplateName, Type = typeof(ProgressBar))]     public class Step : ItemsControl     {         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(Step), new PropertyMetadata(0, OnStepIndexChanged));         private static void OnStepIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)         {             var step = (Step)d;             var stepIndex = (int)e.NewValue;             step.UpdateStepItemState(stepIndex);         }         void UpdateStepItemState(int stepIndex)         {             var count = Items.Count;             if (count <= 0) return;             if (stepIndex >= count)             {                 StepIndex--;                 return;             }             if (stepIndex < 0)             {                 StepIndex++;                 return;             }             for (var i = 0; i < stepIndex; i++)             {                 if (ItemContainerGenerator.ContainerFromIndex(i) is StepItem stepItem)                     stepItem.Status = Status.Complete;             }             if (ItemContainerGenerator.ContainerFromIndex(stepIndex) is StepItem itemInProgress)                 itemInProgress.Status = Status.InProgress;             for (var i = stepIndex + 1; i < Items.Count; i++)             {                 if (ItemContainerGenerator.ContainerFromIndex(i) is StepItem stepItem)                     stepItem.Status = Status.Waiting;             }         }         public override void OnApplyTemplate()         {             base.OnApplyTemplate();             _progressBar = GetTemplateChild(ProgressBarTemplateName) as ProgressBar;         }         protected override void OnRender(DrawingContext drawingContext)         {             base.OnRender(drawingContext);             var 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 StepItem;         }         protected override DependencyObject GetContainerForItemOverride()         {             return new StepItem();         }         public Step()         {             ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;         }         public void Next()         {             StepIndex++;         }         public void Previous()         {             StepIndex--;         }         private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)         {             if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)             {                 var count = Items.Count;                 if (count <= 0) return;                 UpdateStepItemState(StepIndex);             }         }     } }

3) StepItem.cs 代码如下:

using System.Windows; using System.Windows.Controls; namespace WPFDevelopers.Controls {     public class StepItem : ContentControl     {         public static readonly DependencyProperty IndexProperty = DependencyProperty.Register(             "Index", typeof(int), typeof(StepItem), new PropertyMetadata(-1));         public int Index         {             get => (int)GetValue(IndexProperty);             internal set => SetValue(IndexProperty, value);         }         public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(             "Status", typeof(Status), typeof(StepItem), new PropertyMetadata(Status.Waiting));         public Status Status         {             get => (Status)GetValue(StatusProperty);             internal set => SetValue(StatusProperty, value);         }     } }

4) Status.cs 代码如下:

namespace WPFDevelopers.Controls {     /// <summary>     ///状态值     /// </summary>     public enum Status     {         /// <summary>         /// 等待中         /// </summary>         Waiting,         /// <summary>         /// 正在进行中         /// </summary>         InProgress,         /// <summary>         /// 完成         /// </summary>         Complete     } }

5) StepExample.xaml 代码如下:

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.StepExample"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"               xmlns:controls="clr-namespace:WPFDevelopers.Samples.Controls"              xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"              xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"              mc:Ignorable="d"               d:DesignHeight="450" d:DesignWidth="800">     <controls:CodeViewer>         <StackPanel  VerticalAlignment="Center" >             <UniformGrid Columns="2" Name="PART_UniformGrid">                 <wd:Step x:Name="PART_Step" StepIndex="{Binding Progress}">                     <wd:StepItem Content="填写账号"/>                     <wd:StepItem Content="身份验证"/>                     <wd:StepItem Content="设置新密码"/>                     <wd:StepItem Content="完成"/>                 </wd:Step>                 <wd:Step StepIndex="0" ItemsSource="{Binding Steps}">                 </wd:Step>             </UniformGrid>             <StackPanel Orientation="Horizontal"                         VerticalAlignment="Center"                          HorizontalAlignment="Center"                         Margin="10">                 <Button Content="上一步"  Command="{Binding PreviousCommand}"                      CommandParameter="{Binding ElementName=PART_UniformGrid}"                     Style="{StaticResource PrimaryButton}"/>                 <Button Content="下一步"   Command="{Binding NextCommand}"                      CommandParameter="{Binding ElementName=PART_UniformGrid}"                     Style="{StaticResource PrimaryButton}"/>             </StackPanel>         </StackPanel>         <controls:CodeViewer.SourceCodes>             <controls:SourceCodeModel                  CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/StepExample.xaml"                  CodeType="Xaml"/>             <controls:SourceCodeModel                  CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/StepExample.xaml.cs"                  CodeType="CSharp"/>         </controls:CodeViewer.SourceCodes>     </controls:CodeViewer> </UserControl>

6) StepExample.xaml.cs 代码如下:

using System; using System.Collections.ObjectModel; using System.Linq; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using WPFDevelopers.Controls; using WPFDevelopers.Samples.Helpers; namespace WPFDevelopers.Samples.ExampleViews {     /// <summary>     /// StepExample.xaml 的交互逻辑     /// </summary>     public partial class StepExample : UserControl     {         public ObservableCollection<string> Steps         {             get;             set;         }         public StepExample()         {             InitializeComponent();             Steps = new ObservableCollection<string>();             Steps.Add("Step 1");             Steps.Add("Step 2");             Steps.Add("Step 3");             Steps.Add("Step 4");             this.DataContext = this;         }         public ICommand NextCommand => new RelayCommand(new Action<object>((sender) =>         {             var uniformGrid = sender as UniformGrid;             if (uniformGrid == null) return;             foreach (var step in uniformGrid.Children.OfType<Step>())                 step.Next();         }));         public ICommand PreviousCommand => new RelayCommand(new Action<object>((sender) =>         {             var uniformGrid = sender as UniformGrid;             if (uniformGrid == null) return;             foreach (var step in uniformGrid.Children.OfType<Step>())                 step.Previous();         }));     } }

效果图

以上就是基于WPF实现步骤控件的示例代码的详细内容,更多关于WPF步骤控件的资料请关注软件开发网其它相关文章!



wpf 示例

需要 登录 后方可回复, 如果你还没有账号请 注册新账号