Split Control
Learn creating a Split Control for a split-flap display using Windows App SDK with this Tutorial
Split Control shows how to create a split-flap or Flap display using Windows App SDK.
Step 1
Follow Setup and Start on how to get Setup and Install what you need for Visual Studio 2022 and Windows App SDK.
Step 2
Then in Visual Studio within Solution Explorer for the Solution, right click on the Project shown below the Solution and then select Add then New Item…
Step 3
Then in Add New Item from the C# Items list, select WinUI and then select User Control (WinUI 3) from the list next to this, then type in the name of Flap.xaml and then Click on Add.
Step 4
Step 5
In the XAML for Flap.xaml there will be some XAML for a Grid
, above Grid
, type the following XAML:
<UserControl.Resources>
<Style x:Key="SplitLabel" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="75"/>
</Style>
<Style x:Key="GridStyle" TargetType="Grid">
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="1,1,1,1"/>
</Style>
<LinearGradientBrush x:Key="BackgroundBrush"
EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF202020" Offset="1"/>
<GradientStop Color="#FF404040"/>
</LinearGradientBrush>
<!-- Storyboard -->
</UserControl.Resources>
This is the start of the Resources which will control the Style of the User Control.
Step 6
While still in the the XAML for Flap.xaml below <!-- Storyboard -->
, type in the following XAML:
<Storyboard x:Name="FlipAnimation">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="BlockFlip"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<EasingDoubleKeyFrame Value="1" KeyTime="0">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseOut" Bounces="1" Bounciness="6"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
<EasingDoubleKeyFrame Value="-1" KeyTime="00:00:00.250">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseOut" Bounces="1" Bounciness="6"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlockFlipTop"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="00:00:00.125">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlockFlipBottom"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="00:00:00.125">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
This Resources is the Storyboard which contains the animations for the User Control.
Step 7
While still in the the XAML for Flap.xaml below <Grid>
, type in the following XAML:
<Grid Height="80" Width="50">
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
<Grid x:Name="BlockTop" Grid.Row="0" Style="{StaticResource GridStyle}"
Background="{StaticResource BackgroundBrush}">
<TextBlock x:Name="TextBlockTop" Style="{StaticResource SplitLabel}"
HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-2,0,0"/>
</Grid>
<Grid x:Name="BlockBottom" Grid.Row="1" Style="{StaticResource GridStyle}">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF202020"/>
<GradientStop Color="#FF404040" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<TextBlock x:Name="TextBlockBottom" Style="{StaticResource SplitLabel}"
HorizontalAlignment="Center" VerticalAlignment="Bottom"
RenderTransformOrigin="0.5,0.5" Margin="0,0,0,-4"/>
</Grid>
<Grid x:Name="BlockFlip" Style="{StaticResource GridStyle}"
Background="{StaticResource BackgroundBrush}" RenderTransformOrigin="0.5,1">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
<TextBlock x:Name="TextBlockFlipTop" Style="{StaticResource SplitLabel}"
HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-2,0,0"/>
<TextBlock x:Name="TextBlockFlipBottom" Style="{StaticResource SplitLabel}"
HorizontalAlignment="Center" VerticalAlignment="Bottom"
Visibility="Collapsed" RenderTransformOrigin="0.5,0.5" Margin="0,0,0,-4">
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="-1"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform Y="40"/>
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
</Grid>
</Grid>
This Grid
will represent the Flap of the Split Control with the parts to make the top and bottom
parts along with the part which will flip over as part of the User Control.
Step 8
Step 9
You will now be in the View for the Code of Flap.xaml.cs type the following Code
below the end of the Constructor of public Flap() { ... }
:
private string _value;
private string _from;
public string Value
{
get { return _value; }
set
{
_value = value;
if (_from != null)
{
if (_from != value)
{
TextBlockTop.Text = TextBlockFlipBottom.Text = value;
TextBlockFlipTop.Text = _from;
FlipAnimation.Begin();
FlipAnimation.Completed -= (s, e) => { };
FlipAnimation.Completed += (s, e) =>
TextBlockBottom.Text = _from;
}
}
if (_from == null)
{
TextBlockFlipTop.Text = TextBlockBottom.Text = value;
}
_from = value;
}
}
The class
for Flap
represents the User Control for the Flap and includes a
Property
of Value
which contains triggers for the animation of the Storyboard
when set.
Step 10
Then in Visual Studio within Solution Explorer for the Solution, right click on the Project shown below the Solution and then select Add then New Item…
Step 11
Then in Add New Item from the C# Items list, select Code and then select Code File from the list next to this, then type in the name of Split.cs and then Click on Add.
Step 12
Step 13
You will now be in the View for the Code of Split.cs, within this type the following Code:
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Linq;
namespace SplitControl;
public enum Sources
{
Value, Time, Date, TimeDate
}
public class Split : StackPanel
{
// Constants, Members, Dependency Property & Property
// Set Element & Add Element Methods
// Add Layout Method & Value Property
// Constructor
}
There are using
statements for the User Control, a namespace
for SplitControl
with an enum
for the Sources
of the Split Control along with a class
of Split
that will represent the User Control.
Step 14
While still in the namespace
of SplitControl
in the class
of Split
after the Comment
of // Constants, Members, Dependency Property & Property
type the following Constants, Members,
Dependency Property and Property:
private const char space = ' ';
private const string time = "HH mm ss";
private const string date = "dd MM yyyy";
private const string date_time = "HH mm ss dd MM yyyy";
private const string invalid_source = "Invalid argument";
private string _value;
private int _count;
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register(nameof(Source), typeof(Sources),
typeof(Split), new PropertyMetadata(Sources.Time));
public Sources Source
{
get { return (Sources)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
The Constants include formats for the time and date that can be displayed with the Split Control along with
Members for the Split Control and a Dependency Property and Property for the Source
to be used.
Step 15
While still in the namespace
of SplitControl
in the class
of Split
after the Comment
of // Set Element & Add Element Methods
type the following Methods:
private void SetElement(string name, char glyph)
{
var element = Children.Cast<FrameworkElement>()
.FirstOrDefault(f => (string)f.Tag == name);
if (element is Flap flap)
{
flap.Value = glyph.ToString();
}
}
private void AddElement(string name)
{
FrameworkElement element = name == null
? new Canvas
{
Width = 5
}
: new Flap()
{
Tag = name
};
Children.Add(element);
}
The Method of SetElement
will be used to set a Flap
to a particular Value
and AddElement
will be used to either add a Flap
or a Canvas
for a Space.
Step 16
While still in the namespace
of SplitControl
in the class
of Split
after the Comment
of // Add Layout Method & Value Property
type the following Method and Property:
private void AddLayout()
{
var array = _value.ToCharArray();
var length = array.Length;
var list = Enumerable.Range(0, length);
if (_count != length)
{
Children.Clear();
foreach (int item in list)
{
AddElement((array[item] == space)
? null : item.ToString());
}
_count = length;
}
foreach (int item in list)
{
SetElement(item.ToString(), array[item]);
}
}
public string Value
{
get { return _value; }
set { _value = value; AddLayout(); }
}
The Method of AddLayout
creates the look-and-feel for the User Control and the
Property of Value
will setup the display of the Split Control using the Method of AddLayout
.
Step 17
While still in the namespace
of SplitControl
in the class
of Split
after the Comment
of // Constructor
type the following Constructor:
public Split()
{
Orientation = Orientation.Horizontal;
var timer = new DispatcherTimer()
{
Interval = TimeSpan.FromMilliseconds(250)
};
timer.Tick += (object s, object args) =>
{
if (Source != Sources.Value)
{
var format = Source switch
{
Sources.Time => time,
Sources.Date => date,
Sources.TimeDate => date_time,
_ => throw new ArgumentException(invalid_source)
};
Value = DateTime.Now.ToString(format);
}
};
timer.Start();
}
The Constructor will setup a DispatcherTimer
to be used to display the Value
of the Split Control.
Step 18
Step 19
In the XAML for MainWindow.xaml there will be some XAML for a StackPanel
, this should be Removed:
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
</StackPanel>
Step 20
While still in the XAML for MainWindow.xaml above </Window>
, type in the following XAML:
<Viewbox>
<local:Split Padding="50" Source="Time"/>
</Viewbox>
This XAML contains a ViewBox and the User Control of Split
with the Source
set to Time
.
Step 21
Step 22
In the Code for MainWindow.xaml.cs
there be a Method of myButton_Click(...)
this should be Removed by removing the following:
private void myButton_Click(object sender, RoutedEventArgs e)
{
myButton.Content = "Clicked";
}
Step 23
Step 24
Once running you will see the Split Control displaying the current Time.