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
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
< 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
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
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
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
namespace WPFDevelopers.Controls { /// <summary> ///状态值 /// </summary> public enum Status { /// <summary> /// 等待中 /// </summary> Waiting, /// <summary> /// 正在进行中 /// </summary> InProgress, /// <summary> /// 完成 /// </summary> Complete } } |
5) StepExample.xaml
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
< 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
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
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步骤控件的资料请关注服务器之家其它相关文章!
原文链接:https://mp.weixin.qq.com/s/CSZWAHVnuxz_J5ghwpYXKg