基于WPF实现步骤控件的示例代码
作者:驚鏵 发布时间:2021-09-12 15:16:16
标签:WPF,步骤,控件
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();
}));
}
}
效果图
来源:https://mp.weixin.qq.com/s/CSZWAHVnuxz_J5ghwpYXKg
0
投稿
猜你喜欢
- 1、每帧检查定义一个时间变量 timer,每帧将此时间减去帧间隔时间 Time.deltaTime,如果小于或者等于零,说明定时器到了,执行
- 控制json序列化/反序列化1. @JsonIgnoreProperties的用法@JsonIgnoreProperties(value =
- 前言项目中有需求在APP的Webview中长按图片可以保存。后来就去研究一下该怎么实现,顺便整理了一下。WebView基本配置mWvCont
- 一、引入先给出一个Num类的定义internal class Num{ public static int odd = 5000
- 方式一:基于现有控件进行扩展,如基于button进行扩展,UI可直接用xmal进行编辑设计,逻辑用xaml.cs进行编辑方法二:直接创建wp
- android通过google API获取天气信息public class WeatherActivity extends Activity
- 切面编程听起来可能有点陌生,不过现在越来越多的开发团队正在用上这种技术。先说熟悉的面向对象编程 OOP,通常都是用各种对象/模块来负责具体的
- 有一位程序员去相亲的时候,非常礼貌得说自己是一名程序员,并解释自己是做底层架构的,于是女方听到"底层"两个字,就一脸嫌弃
- java获取系统路径字体、得到某个目录下的所有文件名、获取当前路径package com.liuxing.test;import java.
- 在maven的pom.xml里面添加一下依赖:<properties><project.build.sourceEncod
- 前文传送门:NioEventLoop处理IO事件执行任务队列继续回到NioEventLoop的run()方法:protected void
- gravity与layout_gravity属性在android布局中,我们经常会用到“重心”-gr
- //把txt清空 &n
- 前 言🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端☕专栏简介:深入、全面、系统的介绍消息中间件🌰 文章简介
- 你好,今天我要和大家分享一些东西,举例来说这个在JavaScript中用的很多。我要讲讲回调(callbacks)。你知道什么时候用,怎么用
- 今天被数据大神说了,对接第三方接口返回的json字段我想用驼峰形式,他说我这样不专业。所以就改了,认怂。记住以后再次对接rest接口,返回的
- 目录概述代码实现完整代码概述在PC端用.NET开发一个蓝牙下载的程序。实现在PC上查找周围的蓝牙设备(主要是手机),并将PC上的文件通过蓝牙
- 最近由于项目需要,研究了下百度地图定位,他们提供的实例基本都是用 * 实现自动定位的。我想实现一种效果:当用户进入UI时,不定位,用户需要定
- 现在看我文章的多数是一些老Android了,相信每个人使用起LayoutInflater都是家常便饭,信手拈来。但即使是这样,我仍然觉得这个
- 我们先来看本地如何生成图片验证码的,再来写输出到网页的验证码如何实现。先来看最简单的—实现的功能是,将一个字符串变成图片写入到文件中实现代码