Skip to content

Commit

Permalink
Add StepBar
Browse files Browse the repository at this point in the history
  • Loading branch information
ghost1372 committed Jan 9, 2025
1 parent 6e54266 commit a1bbb6d
Show file tree
Hide file tree
Showing 14 changed files with 1,452 additions and 3 deletions.
88 changes: 88 additions & 0 deletions dev/DevWinUI.Controls/Controls/StepBar/StepBar.Property.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
namespace DevWinUI;
public partial class StepBar
{
public StepStatus Status
{
get => (StepStatus)GetValue(StatusProperty);
set => SetValue(StatusProperty, value);
}

public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register(nameof(Status), typeof(StepStatus), typeof(StepBar), new PropertyMetadata(StepStatus.Info, OnStepStatusChanged));

private static void OnStepStatusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (StepBar)d;
if (ctl != null)
{
ctl.UpdateItems();
}
}

public int StepIndex
{
get => (int)GetValue(StepIndexProperty);
set
{
if (ItemsCount > 0)
{
int clampedValue = Math.Max(0, Math.Min(ItemsCount - 1, value));
SetValue(StepIndexProperty, clampedValue);
}
else
{
SetValue(StepIndexProperty, value);
}
}
}

public static readonly DependencyProperty StepIndexProperty =
DependencyProperty.Register(nameof(StepIndex), typeof(int), typeof(StepBar), new PropertyMetadata(0, OnStepIndexChanged));

private static void OnStepIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (StepBar)d;
if (ctl != null)
{
ctl.OnStepIndexChanged((int)e.NewValue);
}
}

public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(StepBar), new PropertyMetadata(Orientation.Horizontal, OnOrientationChanged));

private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (StepBar)d;
if (ctl != null)
{
ctl.UpdateItemsPanel();
ctl.UpdateProgressBarSize();
ctl.OnStepIndexChanged(ctl.StepIndex);
ctl.UpdateItems();
}
}

public StepBarHeaderDisplayMode HeaderDisplayMode
{
get { return (StepBarHeaderDisplayMode)GetValue(HeaderDisplayModeProperty); }
set { SetValue(HeaderDisplayModeProperty, value); }
}

public static readonly DependencyProperty HeaderDisplayModeProperty =
DependencyProperty.Register(nameof(HeaderDisplayMode), typeof(StepBarHeaderDisplayMode), typeof(StepBar), new PropertyMetadata(StepBarHeaderDisplayMode.Bottom, OnHeaderDisplayModeChanged));

private static void OnHeaderDisplayModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (StepBar)d;
if (ctl != null)
{
ctl.UpdateHeaderDisplayMode();
}
}
}
272 changes: 272 additions & 0 deletions dev/DevWinUI.Controls/Controls/StepBar/StepBar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
using Microsoft.UI.Xaml.Markup;

namespace DevWinUI;

[ContentProperty(Name = nameof(Items))]
[TemplatePart(Name = ElementProgressBar, Type = typeof(ProgressBar))]
public partial class StepBar : ItemsControl
{
private const string ElementProgressBar = "PART_ProgressBar";
private const string ElementProgressBarBorder = "PART_ProgressBarBorder";
private const string ElementRootGridVertical = "PART_RootGridVertical";

private ProgressBar progressBar;
private Border progressBarBorder;
private Grid rootGridVertical;
private int ItemsCount => Items.Count;
public ControlTemplate? HorizontalTemplate { get; set; }
public ControlTemplate? VerticalTemplate { get; set; }
public Style? HorizontalItemContainerStyle { get; set; }
public Style? VerticalItemContainerStyle { get; set; }

private int _oriStepIndex = -1;
private void UpdateTemplate()
{
Template = Orientation switch
{
Orientation.Horizontal => HorizontalTemplate,
Orientation.Vertical => VerticalTemplate,
_ => Template
};

ItemContainerStyle = Orientation switch
{
Orientation.Horizontal => HorizontalItemContainerStyle,
Orientation.Vertical => VerticalItemContainerStyle,
_ => ItemContainerStyle
};
}
public StepBar()
{
if (Application.Current.Resources["StepBarHorizontalControlTemplate"] is ControlTemplate horizontalTemplate)
HorizontalTemplate = horizontalTemplate;

if (Application.Current.Resources["StepBarVerticalControlTemplate"] is ControlTemplate verticalTemplate)
VerticalTemplate = verticalTemplate;

if (Application.Current.Resources["StepBarItemHorizontalStyle"] is Style horizontalItemContainerStyle)
HorizontalItemContainerStyle = horizontalItemContainerStyle;

if (Application.Current.Resources["StepBarItemVerticalStyle"] is Style verticalItemContainerStyle)
VerticalItemContainerStyle = verticalItemContainerStyle;
}

protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
progressBar = GetTemplateChild(ElementProgressBar) as ProgressBar;
progressBarBorder = GetTemplateChild(ElementProgressBarBorder) as Border;
rootGridVertical = GetTemplateChild(ElementRootGridVertical) as Grid;

SetProgressBarMaximumValue();
UpdateItemsPanel();
UpdateHeaderDisplayMode();
UpdateProgressBarVisualStates();
SizeChanged -= OnSizeChanged;
SizeChanged += OnSizeChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateItems();
}
protected override void OnItemsChanged(object e)
{
base.OnItemsChanged(e);
UpdateItems();
}

private void UpdateItemsPanel()
{
UpdateTemplate();

if (Orientation == Orientation.Horizontal)
{
ItemsPanel = (ItemsPanelTemplate)XamlReader.Load("<ItemsPanelTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><UniformGrid xmlns='using:DevWinUI' Rows='1'/></ItemsPanelTemplate>");
}
else
{
ItemsPanel = (ItemsPanelTemplate)XamlReader.Load("<ItemsPanelTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><UniformGrid xmlns='using:DevWinUI' Columns='1'/></ItemsPanelTemplate>");
}
}

private void UpdateHeaderDisplayMode()
{
if (Orientation == Orientation.Horizontal)
{
if (progressBar == null)
{
return;
}
switch (HeaderDisplayMode)
{
case StepBarHeaderDisplayMode.Top:
progressBar.VerticalAlignment = VerticalAlignment.Bottom;
progressBar.HorizontalAlignment = HorizontalAlignment.Center;
progressBar.Margin = new Thickness(0, 0, 0, 20);
break;
case StepBarHeaderDisplayMode.Left:
case StepBarHeaderDisplayMode.Right:
case StepBarHeaderDisplayMode.Bottom:
progressBar.VerticalAlignment = VerticalAlignment.Top;
progressBar.HorizontalAlignment = HorizontalAlignment.Center;
progressBar.Margin = new Thickness(0, 20, 0, 0);
break;
}
}
else
{
if (rootGridVertical == null)
{
return;
}
switch (HeaderDisplayMode)
{
case StepBarHeaderDisplayMode.Top:
case StepBarHeaderDisplayMode.Bottom:
case StepBarHeaderDisplayMode.Right:
rootGridVertical.FlowDirection = FlowDirection.LeftToRight;
break;
case StepBarHeaderDisplayMode.Left:
rootGridVertical.FlowDirection = FlowDirection.RightToLeft;
break;
}
}
UpdateItems();
}

private void SetProgressBarMaximumValue()
{
if (progressBar != null)
{
progressBar.Maximum = ItemsCount - 1;
}
}

private void UpdateProgressBarSize()
{
int colOrRowCount = ItemsCount;

if (Orientation == Orientation.Horizontal)
{
if (progressBar == null || colOrRowCount <= 0)
{
return;
}
progressBar.Width = (colOrRowCount - 1) * (ActualWidth / colOrRowCount);
}
else
{
if (progressBarBorder == null || colOrRowCount <= 0)
{
return;
}
progressBarBorder.Height = (colOrRowCount - 1) * (ActualHeight / colOrRowCount);
}
}

private void UpdateItems()
{
int count = ItemsCount;
if (count <= 0)
{
return;
}

SetProgressBarMaximumValue();
SetProgressBarValueWithAnimation(StepIndex);

for (int i = 0; i < count; i++)
{
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem stepBarItem)
{
stepBarItem.Index = i + 1;
stepBarItem.Orientation = this.Orientation;
stepBarItem.HeaderDisplayMode = this.HeaderDisplayMode;
}
}

if (_oriStepIndex > 0)
{
StepIndex = _oriStepIndex;
_oriStepIndex = -1;
}
else
{
OnStepIndexChanged(StepIndex);
}

UpdateProgressBarSize();
}

private void OnStepIndexChanged(int stepIndex)
{
if (progressBar == null)
{
return;
}

if (stepIndex < 0)
{
SetProgressBarValueWithAnimation(0);
return;
}

for (int i = 0; i < stepIndex; i++)
{
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem stepItemFinished)
{
UpdateProgressBarVisualStates();
stepItemFinished.ProgressState = StepProgressState.Complete;
stepItemFinished.Status = Status;
}
}

for (int i = stepIndex + 1; i < ItemsCount; i++)
{
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem stepItemFinished)
{
UpdateProgressBarVisualStates();
stepItemFinished.ProgressState = StepProgressState.Waiting;
stepItemFinished.Status = Status;
}
}

if (ItemContainerGenerator.ContainerFromIndex(stepIndex) is StepBarItem stepItemSelected)
{
UpdateProgressBarVisualStates();
stepItemSelected.ProgressState = StepProgressState.UnderWay;
stepItemSelected.Status = Status;
}
SetProgressBarValueWithAnimation(StepIndex);
}

private void SetProgressBarValueWithAnimation(double toValue, int duration = 200)
{
if (progressBar == null)
{
return;
}
var horizontalAnimation = new DoubleAnimation
{
From = progressBar.Value,
To = toValue,
Duration = new Duration(TimeSpan.FromMilliseconds(duration)),
EnableDependentAnimation = true
};

Storyboard.SetTarget(horizontalAnimation, progressBar);
Storyboard.SetTargetProperty(horizontalAnimation, nameof(ProgressBar.Value));

var horizontalStoryboard = new Storyboard();
horizontalStoryboard.Children.Add(horizontalAnimation);
horizontalStoryboard.Begin();
}

private void UpdateProgressBarVisualStates()
{
VisualStateManager.GoToState(this, Status.ToString(), true);
}

public void Next() => StepIndex++;
public void Prev() => StepIndex--;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace DevWinUI;
public enum StepBarHeaderDisplayMode
{
Top,
Bottom,
Left,
Right
}
Loading

0 comments on commit a1bbb6d

Please sign in to comment.