WPF教程002 - 实现Step步骤条控件

简介: 原文:WPF教程002 - 实现Step步骤条控件在网上看到这么一个效果,刚好在用WPF做控件,就想着用WPF来实现一下1、实现原理1.1、该控件分为2个模块,类似ComboBox控件分为StepBar和StepBarItem1.
原文: WPF教程002 - 实现Step步骤条控件

在网上看到这么一个效果,刚好在用WPF做控件,就想着用WPF来实现一下

img_db66e80011fb0773e071cb467d8dcbc1.png

1、实现原理

1.1、该控件分为2个模块,类似ComboBox控件分为StepBar和StepBarItem
1.2、StepBarItem定义一个Number依赖属性,用来实现每个节点上显示的步骤编号
1.3、StepBar定义一个Progress依赖属性,用来标识该控件走到了第几步,之外可以根据Progress属性与当前Item的Index来进行比较,来实现当控件进入到不同进度时,各个Item节点的样式

2、控件实现

StepBar类似ComboBox控件,但与ComboBox不同的是,由于控件本身并不需要选中,因此直接继承自ItemsControl,而不是ListBox。StepBarItem继承自ContentControl
2.1、新增StepBar、StepBarItem,分别继承自ItemsControl与ContentControl
 
  
  1. public class StepBar : ItemsControl
  2. {
  3. #region Constructors
  4. static StepBar()
  5. {
  6. DefaultStyleKeyProperty.OverrideMetadata(typeof(StepBar), new FrameworkPropertyMetadata(typeof(StepBar)));
  7. }
  8. #endregion
  9. }
 
   
  1. public class StepBarItem : ContentControl
  2. {
  3. #region Constructors
  4. static StepBarItem()
  5. {
  6. DefaultStyleKeyProperty.OverrideMetadata(typeof(StepBarItem), new FrameworkPropertyMetadata(typeof(StepBarItem)));
  7. }
  8. #endregion
  9. }
2、2、在Generic.xaml资源文件中定义这两个控件的基本样式
首先讲下大概的布局思路,StepBar比较简单,就是使用UniformGrid布局,然后设置Rows="1",这样所有的StepBarItem就会在一行显示,并且平分一行大小,否则UniformGrid默认一行是只显示2个的,重点是在StepBarItem的布局上面。
首先将文字和那根横线进行拆分,分为2列,文字那列根据内容自适应,横线则填充,因此每个StepBarItem由两部分组成:文字+横线。但是这样会有个问题,前面几个Item都会很正常的显示,但一到最后一个Item的时候,就会多出来右边的那根横线,那怎么去掉呢。我这边参照网上的思路,使用了Converter转换器,将每个Item作为参数传入转换器中,判断Item的Index与整个空间的Items.Count进行比较,如果当前的Index和Items的最后一个相等,那么久将最后一个Item的右边的那根横线给隐藏,这样就能达到文章一开始的那个效果。
 
  
  1. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  3. xmlns:ec="http://schemas.microsoft.com/expression/2010/controls"
  4. xmlns:ZUI="clr-namespace:ZdfFlatUI"
  5. xmlns:Converters="clr-namespace:ZdfFlatUI.Converters">
  6. <Converters:IsLastItemConverter x:Key="IsLastItemConverter" />
  7. <Converters:IsProgressedConverter x:Key="IsProgressedConverter" />
  8. <PathGeometry x:Key="Icon_Gou" Figures="M378.410667 850.450963C364.491852 850.450963 350.610963 845.293037 340.02963 834.939259L20.920889 523.529481C-0.279704 502.821926-0.279704 469.295407 20.920889 448.587852 42.121481 427.880296 76.48237 427.880296 97.682963 448.587852L378.410667 722.526815 925.75763 188.491852C946.958222 167.784296 981.319111 167.784296 1002.519704 188.491852 1023.720296 209.161481 1023.720296 242.688 1002.519704 263.395556L416.791704 834.939259C406.172444 845.293037 392.291556 850.450963 378.410667 850.450963L378.410667 850.450963Z" />
  9. <Style TargetType="{x:Type ZUI:StepBarItem}">
  10. <Setter Property="FontWeight" Value="Bold" />
  11. <Setter Property="FontSize" Value="14" />
  12. <Setter Property="FontFamily" Value="宋体" />
  13. <Setter Property="Template">
  14. <Setter.Value>
  15. <ControlTemplate TargetType="{x:Type ZUI:StepBarItem}">
  16. <Grid>
  17. <Grid.ColumnDefinitions>
  18. <ColumnDefinition Width="auto" />
  19. <ColumnDefinition Width="*" />
  20. </Grid.ColumnDefinitions>
  21. <StackPanel Orientation="Horizontal">
  22. <Grid Margin="0,0,10,0">
  23. <Border x:Name="Bd" BorderBrush="#CCCCCC" Width="25" Height="25" CornerRadius="100"
  24. BorderThickness="1" SnapsToDevicePixels="True" UseLayoutRounding="True">
  25. </Border>
  26. <TextBlock x:Name="Number" Text="{TemplateBinding Number}"
  27. HorizontalAlignment="Center" VerticalAlignment="Center" />
  28. <Path x:Name="path" Data="{StaticResource Icon_Gou}" Stretch="Uniform" Width="12" Fill="#3399FF" Visibility="Collapsed" />
  29. </Grid>
  30. <ContentPresenter VerticalAlignment="Center" />
  31. </StackPanel>
  32. <Border x:Name="Line" Grid.Column="1" BorderBrush="#E3E8EE" BorderThickness="0,1,0,0"
  33. VerticalAlignment="Center" Margin="10,0"
  34. SnapsToDevicePixels="True" UseLayoutRounding="True" />
  35. </Grid>
  36. <ControlTemplate.Triggers>
  37. <DataTrigger Value="True">
  38. <DataTrigger.Binding>
  39. <MultiBinding Converter="{StaticResource IsLastItemConverter}">
  40. <Binding RelativeSource="{RelativeSource Self}" />
  41. <Binding RelativeSource="{RelativeSource AncestorType={x:Type ZUI:StepBar}}" Path="Items.Count"/>
  42. </MultiBinding>
  43. </DataTrigger.Binding>
  44. <Setter TargetName="Line" Property="Visibility" Value="Collapsed" />
  45. </DataTrigger>
  46. <DataTrigger Value="False">
  47. <DataTrigger.Binding>
  48. <MultiBinding Converter="{StaticResource IsLastItemConverter}">
  49. <Binding RelativeSource="{RelativeSource Self}" />
  50. <Binding RelativeSource="{RelativeSource AncestorType={x:Type ZUI:StepBar}}" Path="Items.Count"/>
  51. </MultiBinding>
  52. </DataTrigger.Binding>
  53. <Setter TargetName="Line" Property="Visibility" Value="Visible" />
  54. </DataTrigger>
  55. <DataTrigger Value="Less">
  56. <DataTrigger.Binding>
  57. <MultiBinding Converter="{StaticResource IsProgressedConverter}">
  58. <Binding RelativeSource="{RelativeSource Self}" />
  59. <Binding RelativeSource="{RelativeSource AncestorType={x:Type ZUI:StepBar}}" Path="Progress"/>
  60. </MultiBinding>
  61. </DataTrigger.Binding>
  62. <Setter TargetName="Bd" Property="Background" Value="#003399FF" />
  63. <Setter TargetName="Bd" Property="BorderBrush" Value="#3399FF" />
  64. <Setter TargetName="Number" Property="Visibility" Value="Collapsed" />
  65. <Setter TargetName="Line" Property="BorderBrush" Value="#3399FF" />
  66. <Setter TargetName="path" Property="Visibility" Value="Visible" />
  67. <Setter Property="Foreground" Value="#999999" />
  68. </DataTrigger>
  69. <DataTrigger Value="Equal">
  70. <DataTrigger.Binding>
  71. <MultiBinding Converter="{StaticResource IsProgressedConverter}">
  72. <Binding RelativeSource="{RelativeSource Self}" />
  73. <Binding RelativeSource="{RelativeSource AncestorType={x:Type ZUI:StepBar}}" Path="Progress"/>
  74. </MultiBinding>
  75. </DataTrigger.Binding>
  76. <Setter TargetName="Bd" Property="Background" Value="#3399FF" />
  77. <Setter TargetName="Bd" Property="BorderBrush" Value="#3399FF" />
  78. <Setter TargetName="Number" Property="Visibility" Value="Visible" />
  79. <Setter TargetName="Number" Property="Foreground" Value="#FFFFFF" />
  80. <Setter TargetName="path" Property="Visibility" Value="Collapsed" />
  81. <Setter Property="Foreground" Value="#666666" />
  82. </DataTrigger>
  83. <DataTrigger Value="Large">
  84. <DataTrigger.Binding>
  85. <MultiBinding Converter="{StaticResource IsProgressedConverter}">
  86. <Binding RelativeSource="{RelativeSource Self}" />
  87. <Binding RelativeSource="{RelativeSource AncestorType={x:Type ZUI:StepBar}}" Path="Progress"/>
  88. </MultiBinding>
  89. </DataTrigger.Binding>
  90. <Setter Property="Foreground" Value="#999999" />
  91. </DataTrigger>
  92. </ControlTemplate.Triggers>
  93. </ControlTemplate>
  94. </Setter.Value>
  95. </Setter>
  96. </Style>
  97. <Style TargetType="{x:Type ZUI:StepBar}">
  98. <Setter Property="Template">
  99. <Setter.Value>
  100. <ControlTemplate TargetType="{x:Type ZUI:StepBar}">
  101. <Border>
  102. <ItemsPresenter />
  103. </Border>
  104. </ControlTemplate>
  105. </Setter.Value>
  106. </Setter>
  107. <Setter Property="ItemsPanel">
  108. <Setter.Value>
  109. <ItemsPanelTemplate>
  110. <UniformGrid IsItemsHost="True" Rows="1" />
  111. </ItemsPanelTemplate>
  112. </Setter.Value>
  113. </Setter>
  114. </Style>
  115. </ResourceDictionary>
这里有个地方需要注意一下:我在  IsLastItemConverter  转换器中同时传入了Items.Count,但是转换器中又没用到这个参数,是因为什么呢?因为在StepBar可能会在中途Add或者Remove一个Item,这样就可以动态的设置倒数第二个本来已经隐藏的横线为Visible,而不至于Add一个Item后,后面几个Item右边的横线都不显示。

2.3、新增两个转换器:IsProgressedConverter(进度转换器)和IsLastItemConverter(是否是最后一个Item)
IsProgressedConverter
 
  
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. namespace ZdfFlatUI.Converters
  9. {
  10. public class IsProgressedConverter : IMultiValueConverter
  11. {
  12. public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  13. {
  14. if ((values[0] is ContentControl && values[1] is int) == false)
  15. {
  16. return EnumCompare.None;
  17. }
  18. ContentControl contentControl = values[0] as ContentControl;
  19. int progress = (int)values[1];
  20. ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(contentControl);
  21. if(itemsControl == null)
  22. {
  23. return EnumCompare.None;
  24. }
  25. int index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentControl);
  26. if(index < progress)
  27. {
  28. return EnumCompare.Less;
  29. }
  30. else if(index == progress)
  31. {
  32. return EnumCompare.Equal;
  33. }
  34. return EnumCompare.Large;
  35. }
  36. public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
  37. {
  38. throw new NotSupportedException();
  39. }
  40. }
  41. }
IsLastItemConverter
 
  
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. namespace ZdfFlatUI.Converters
  9. {
  10. /// <summary>
  11. /// true则隐藏,false则显示
  12. /// </summary>
  13. public class IsLastItemConverter : IMultiValueConverter
  14. {
  15. #region IValueConverter 成员
  16. public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  17. {
  18. ContentControl contentPresenter = value[0] as ContentControl;
  19. ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(contentPresenter);
  20. bool flag = false;
  21. if(itemsControl != null)
  22. {
  23. int index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentPresenter);
  24. flag = (index == (itemsControl.Items.Count - 1));
  25. }
  26. return flag;
  27. }
  28. public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
  29. {
  30. return null;
  31. }
  32. #endregion
  33. }
  34. }
2.4、在StepBar和StepBarItem分别新增Progress、Number依赖属性,具体的看代码,就不解释了
StepBar
 
  
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. namespace ZdfFlatUI
  10. {
  11. public class StepBar : ItemsControl
  12. {
  13. #region Private属性
  14. #endregion
  15. #region 依赖属性定义
  16. public int Progress
  17. {
  18. get { return (int)GetValue(ProgressProperty); }
  19. set { SetValue(ProgressProperty, value); }
  20. }
  21. public static readonly DependencyProperty ProgressProperty =
  22. DependencyProperty.Register("Progress", typeof(int), typeof(StepBar), new PropertyMetadata(0, OnProgressChangedCallback, OnProgressCoerceValueCallback));
  23. private static object OnProgressCoerceValueCallback(DependencyObject d, object baseValue)
  24. {
  25. //不让Progress超出边界
  26. StepBar stepBar = d as StepBar;
  27. int newValue = Convert.ToInt32(baseValue);
  28. if (newValue < 0)
  29. {
  30. return 0;
  31. }
  32. else if(newValue >= stepBar.Items.Count)
  33. {
  34. return stepBar.Items.Count - 1;
  35. }
  36. return newValue;
  37. }
  38. private static void OnProgressChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  39. {
  40. }
  41. #endregion
  42. #region 依赖属性set get
  43. #endregion
  44. #region Constructors
  45. static StepBar()
  46. {
  47. DefaultStyleKeyProperty.OverrideMetadata(typeof(StepBar), new FrameworkPropertyMetadata(typeof(StepBar)));
  48. }
  49. #endregion
  50. #region Override方法
  51. protected override DependencyObject GetContainerForItemOverride()
  52. {
  53. return new StepBarItem();
  54. }
  55. protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
  56. {
  57. //设置Item的显示数字
  58. StepBarItem stepBarItem = element as StepBarItem;
  59. ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(stepBarItem);
  60. int index = itemsControl.ItemContainerGenerator.IndexFromContainer(stepBarItem);
  61. stepBarItem.Number = Convert.ToString(++index);
  62. base.PrepareContainerForItemOverride(element, item);
  63. }
  64. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
  65. {
  66. base.OnItemsChanged(e);
  67. //ItemsControl数量变化时,重新设置各个Item的显示的数字
  68. for (int i = 0; i < this.Items.Count; i++)
  69. {
  70. StepBarItem stepBarItem = this.ItemContainerGenerator.ContainerFromIndex(i) as StepBarItem;
  71. if(stepBarItem != null)
  72. {
  73. int temp = i;
  74. stepBarItem.Number = Convert.ToString(++temp);
  75. }
  76. }
  77. //进度重新回到第一个
  78. this.Progress = 0;
  79. }
  80. #endregion
  81. #region Private方法
  82. #endregion
  83. }
  84. }
StepBarItem
 
  
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. namespace ZdfFlatUI
  8. {
  9. public class StepBarItem : ContentControl
  10. {
  11. #region Private属性
  12. #endregion
  13. #region 依赖属性定义
  14. public string Number
  15. {
  16. get { return (string)GetValue(NumberProperty); }
  17. set { SetValue(NumberProperty, value); }
  18. }
  19. public static readonly DependencyProperty NumberProperty =
  20. DependencyProperty.Register("Number", typeof(string), typeof(StepBarItem));
  21. #endregion
  22. #region 依赖属性set get
  23. #endregion
  24. #region Constructors
  25. static StepBarItem()
  26. {
  27. DefaultStyleKeyProperty.OverrideMetadata(typeof(StepBarItem), new FrameworkPropertyMetadata(typeof(StepBarItem)));
  28. }
  29. #endregion
  30. #region Override方法
  31. #endregion
  32. #region Private方法
  33. #endregion
  34. }
  35. }

3、控件的具体使用

 
  
  1. <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
  2. <TextBlock x:Name="text" Text="{Binding ., StringFormat=当前正在进行 {0} 步}" Foreground="#957180" FontSize="14" Margin="0,0,0,20" />
  3. <WPF.UI:StepBar x:Name="stepBar1" Width="900" Margin="0,20,0,0" />
  4. <StackPanel Orientation="Horizontal">
  5. <WPF.UI:FlatButton Width="70" Height="28" CornerRadius="3" Click="FlatButton_Click1" Content="Previous" Margin="10,20" />
  6. <WPF.UI:FlatButton Width="70" Height="28" CornerRadius="3" Click="FlatButton_Click" Content="Next" Margin="10,10" />
  7. <WPF.UI:FlatButton Width="70" Height="28" CornerRadius="3" Click="btn_AddItem" Content="Add" Margin="10,10" />
  8. <WPF.UI:FlatButton Width="70" Height="28" CornerRadius="3" Click="btn_RemoveItem" Content="Remove" Margin="10,10" />
  9. </StackPanel>
  10. </StackPanel>
 
   
  1. public partial class UCTStepBarTest : UserControl
  2. {
  3. private int _Step;
  4. public int Step
  5. {
  6. get
  7. {
  8. int temp = this.stepBar.Progress;
  9. return ++temp;
  10. }
  11. }
  12. ObservableCollection<string> list = new ObservableCollection<string>();
  13. public UCTStepBarTest()
  14. {
  15. InitializeComponent();
  16. list.Add("已完成");
  17. list.Add("进行中");
  18. list.Add("已完成");
  19. list.Add("进行中");
  20. this.stepBar1.ItemsSource = list;
  21. this.text.DataContext = Step;
  22. }
  23. private void FlatButton_Click(object sender, RoutedEventArgs e)
  24. {
  25. this.stepBar1.Progress++;
  26. this.text.DataContext = Step;
  27. }
  28. private void FlatButton_Click1(object sender, RoutedEventArgs e)
  29. {
  30. this.stepBar1.Progress--;
  31. this.text.DataContext = Step;
  32. }
  33. private void btn_AddItem(object sender, RoutedEventArgs e)
  34. {
  35. list.Add("进行中");
  36. }
  37. private void btn_RemoveItem(object sender, RoutedEventArgs e)
  38. {
  39. list.RemoveAt(0);
  40. this.text.DataContext = Step;
  41. }
  42. }

最终的效果图如下:
img_bfb26eecd982f7eaff63f50c9c33e57c.gif
 最后仍然是没有达到一开始的那个效果,我设想的是文字部分应该是可以使用DataTemplate模板自定义的,但是文字详细描述那部分和横线的部分不太好布局,目前想不到该怎么去布局,思维受限了,先搁置等到后面看能不能找到合适的解决方法。





















目录
相关文章
|
3月前
|
C#
WPF中动画教程(DoubleAnimation的基本使用)
WPF中动画教程(DoubleAnimation的基本使用)
68 0
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
3月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
|
3月前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
228 0
|
3月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
103 0
|
3月前
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
62 0
|
3月前
|
C# Windows
WPF中如何使用HandyCotrol控件库
WPF中如何使用HandyCotrol控件库
183 1
|
3月前
|
开发框架 前端开发 JavaScript
WPF应用开发之控件动态内容展示
WPF应用开发之控件动态内容展示
|
3月前
|
前端开发 C#
wpfui:一个开源免费具有现代化设计趋势的WPF控件库
wpfui:一个开源免费具有现代化设计趋势的WPF控件库
132 0
|
3月前
|
存储 C# 数据格式
WPF动画教程(PointAnimationUsingPath的使用)
WPF动画教程(PointAnimationUsingPath的使用)
44 0