Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SwitchPresenter 2.0 and the new SwitchConverter 🦙❤️ #550

Merged
merged 7 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions components/Primitives/samples/Primitives.Samples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<Compile Update="UniformGridSample.xaml.cs">
<DependentUpon>UniformGridSample.xaml</DependentUpon>
</Compile>
<Compile Update="DockPanelSample.xaml.cs">
<DependentUpon>DockPanelSample.xaml</DependentUpon>
</Compile>
<Compile Update="ConstrainedBoxSample.xaml.cs">
<DependentUpon>ConstrainedBoxSample.xaml</DependentUpon>
</Compile>
<Compile Update="StaggeredLayoutSample.xaml.cs">
<DependentUpon>StaggeredLayoutSample.xaml</DependentUpon>
</Compile>
<Compile Update="WrapPanelSample.xaml.cs">
<DependentUpon>WrapPanelSample.xaml</DependentUpon>
</Compile>
</ItemGroup>
</Project>
12 changes: 12 additions & 0 deletions components/Primitives/samples/SwitchPresenter.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,15 @@ Or it can simply be used to clearly display different outcomes based on some sta
`SwitchPresenter` can also be used as a replacement for the deprecated `Loading` control. This provides more fine-grained control over animations and content within each state:

> [!SAMPLE SwitchPresenterLoaderSample]

We can also invert the paradigm a bit with a `SwitchPresenter` to do data transformations within XAML using a `ContentTemplate`. Imagine an alternate view of our first starting example:

> [!SAMPLE SwitchPresenterTemplateSample]

That's right! `SwitchPresenter` can be used not just for displaying different UIElements but in feeding different kinds of data into the `ContentTemplate` as well.

## SwitchConverter

A new analog to `SwitchPresenter` is the `SwitchConverter` which can be used in bindings to translate values into resources:

> [!SAMPLE SwitchConverterBrushSample]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<Page x:Class="PrimitivesExperiment.Samples.SwitchPresenter.SwitchConverterBrushSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:PrimitivesExperiment.Samples.SwitchPresenter"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">

<Page.Resources>
<!--
If you reference an enum directly in UWP, you need to use it somewhere for the XamlTypeInfo reference to be generated...
-->
<local:CheckStatus x:Key="MyChecks">Warning</local:CheckStatus>

<!-- SwitchConverter lets you easily convert general values to resources -->
<!-- Note: This is in the converters namespace -->
<converters:SwitchConverter x:Key="StatusToColorSwitchConverter"
TargetType="local:CheckStatus">
<!-- Note: These are reused from the controls namespace from SwitchPresenter -->
<controls:Case Content="{ThemeResource SystemFillColorSuccessBrush}"
Value="Success" />
<controls:Case Content="{ThemeResource SystemFillColorCautionBrush}"
Value="Warning" />
<controls:Case Content="{ThemeResource SystemFillColorCriticalBrush}"
Value="Error" />
</converters:SwitchConverter>
</Page.Resources>

<StackPanel Spacing="8">
<ComboBox x:Name="StatusPicker"
Header="Pick a status"
SelectedIndex="0">
<x:String>Success</x:String>
<x:String>Warning</x:String>
<x:String>Error</x:String>
</ComboBox>
<TextBlock Text="This is it, this is the demo:" />
<TextBlock FontWeight="SemiBold"
Foreground="{x:Bind StatusPicker.SelectedItem, Converter={StaticResource StatusToColorSwitchConverter}, Mode=OneWay}"
Text="{x:Bind StatusPicker.SelectedItem, Mode=OneWay}" />
</StackPanel>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Converters;

namespace PrimitivesExperiment.Samples.SwitchPresenter;

[ToolkitSample(id: nameof(SwitchConverterBrushSample), "SwitchConverter Brush", description: $"A sample for showing how to use a {nameof(SwitchConverter)} for swapping a brush based on an enum.")]
public sealed partial class SwitchConverterBrushSample : Page
{
public SwitchConverterBrushSample()
{
this.InitializeComponent();
}
}

public enum CheckStatus
{
Error,
Warning,
Success,
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using PrimitivesExperiment.Samples.ConstrainedBox;

namespace PrimitivesExperiment.Samples.SwitchPresenter;

[ToolkitSample(id: nameof(SwitchPresenterLayoutSample), "SwitchPresenter Layout", description: $"A sample for showing how to use a {nameof(SwitchPresenter)} for complex layouts.")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<Page x:Class="PrimitivesExperiment.Samples.SwitchPresenter.SwitchPresenterTemplateSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:PrimitivesExperiment.Samples.SwitchPresenter"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">

<StackPanel>
<ComboBox x:Name="Lookup"
Margin="0,0,0,8"
Header="Look up reservation"
SelectedIndex="0">
<x:String>Confirmation Code</x:String>
<x:String>E-ticket number</x:String>
<x:String>Mileage Plan number</x:String>
</ComboBox>
<!-- SwitchPresenter binds to a value -->
<controls:SwitchPresenter Value="{x:Bind Lookup.SelectedItem, Mode=OneWay}">
<!-- We define a common UI template for the data we want to display -->
<controls:SwitchPresenter.ContentTemplate>
<DataTemplate x:DataType="local:TemplateInformation">
<StackPanel>
<TextBox Name="CodeValidator"
ui:TextBoxExtensions.Regex="{x:Bind Regex, Mode=OneWay}"
Header="{x:Bind Header, Mode=OneWay}"
PlaceholderText="{x:Bind PlaceholderText, Mode=OneWay}" />
<TextBlock Text="Thanks for entering a valid code!"
Visibility="{Binding (ui:TextBoxExtensions.IsValid), ElementName=CodeValidator}" />
</StackPanel>
</DataTemplate>
</controls:SwitchPresenter.ContentTemplate>
<!-- And use the value to transform our data into an object we'll use as the context for our UI -->
<controls:Case IsDefault="True"
Value="Confirmation Code">
<local:TemplateInformation Header="Confirmation code"
PlaceholderText="6 letters"
Regex="^[a-zA-Z]{6}$" />
</controls:Case>
<controls:Case Value="E-ticket number">
<local:TemplateInformation Header="E-ticket number"
PlaceholderText="10 or 13 numbers"
Regex="(^\d{10}$)|(^\d{13}$)" />
</controls:Case>
<controls:Case Value="Mileage Plan number">
<local:TemplateInformation Header="Mileage Plan #"
PlaceholderText="Mileage Plan (12 digits)"
Regex="(^\d{12}$)" />
</controls:Case>
</controls:SwitchPresenter>
</StackPanel>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace PrimitivesExperiment.Samples.SwitchPresenter;

[ToolkitSample(id: nameof(SwitchPresenterTemplateSample), "SwitchPresenter Template", description: $"A sample for showing how to use a {nameof(SwitchPresenter)} with a Content Template.")]
public sealed partial class SwitchPresenterTemplateSample : Page
{
public SwitchPresenterTemplateSample()
{
this.InitializeComponent();
}
}

public partial class TemplateInformation
{
public string? Header { get; set; }

public string? Regex { get; set; }

public string? PlaceholderText { get; set; }
}

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
SelectedIndex="0" />
<controls:SwitchPresenter Padding="16"
TargetType="local:Animal"
Value="{Binding SelectedItem, ElementName=AnimalPicker}">
Value="{x:Bind AnimalPicker.SelectedItem, Mode=OneWay}">
<controls:Case Value="Bunny">
<TextBlock FontSize="32"
Text="🐇" />
</controls:Case>
<controls:Case Value="Cat">
<TextBlock FontSize="32"
Text="🐈" />
Expand All @@ -31,14 +35,22 @@
<TextBlock FontSize="32"
Text="🐕" />
</controls:Case>
<controls:Case Value="Bunny">
<controls:Case Value="Giraffe">
<TextBlock FontSize="32"
Text="🐇" />
Text="🦒" />
</controls:Case>
<controls:Case Value="Llama">
<TextBlock FontSize="32"
Text="🦙" />
</controls:Case>
<controls:Case Value="Otter">
<TextBlock FontSize="32"
Text="🦦" />
</controls:Case>
<controls:Case Value="Owl">
<TextBlock FontSize="32"
Text="🦉" />
</controls:Case>
<controls:Case Value="Parrot">
<TextBlock FontSize="32"
Text="🦜" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ public SwitchPresenterValueSample()

public enum Animal
{
Bunny,
Cat,
Dog,
Bunny,
Giraffe,
Llama,
Otter,
Owl,
Parrot,
Squirrel
}
6 changes: 3 additions & 3 deletions components/Primitives/src/SwitchPresenter/Case.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ public partial class Case : DependencyObject
/// <summary>
/// Gets or sets the Content to display when this case is active.
/// </summary>
public UIElement Content
public object Content
{
get { return (UIElement)GetValue(ContentProperty); }
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}

/// <summary>
/// Identifies the <see cref="Content"/> property.
/// </summary>
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register(nameof(Content), typeof(UIElement), typeof(Case), new PropertyMetadata(null));
DependencyProperty.Register(nameof(Content), typeof(object), typeof(Case), new PropertyMetadata(null));

/// <summary>
/// Gets or sets a value indicating whether this is the default case to display when no values match the specified value in the <see cref="SwitchPresenter"/>. There should only be a single default case provided. Do not set the <see cref="Value"/> property when setting <see cref="IsDefault"/> to <c>true</c>. Default is <c>false</c>.
Expand Down
80 changes: 80 additions & 0 deletions components/Primitives/src/SwitchPresenter/SwitchConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Controls;

namespace CommunityToolkit.WinUI.Converters;

/// <summary>
/// A helper <see cref="IValueConverter"/> which can automatically translate incoming data to a set of resulting values defined in XAML.
/// </summary>
/// <example>
/// &lt;converters:SwitchConverter x:Key=&quot;StatusToColorSwitchConverter&quot;
/// TargetType=&quot;models:CheckStatus&quot;&gt;
/// &lt;controls:Case Value=&quot;Error&quot; Content=&quot;{ThemeResource SystemFillColorCriticalBrush}&quot;/&gt;
/// &lt;controls:Case Value=&quot;Warning&quot; Content=&quot;{ThemeResource SystemFillColorCautionBrush}&quot;/&gt;
/// &lt;controls:Case Value=&quot;Success&quot; Content=&quot;{ThemeResource SystemFillColorSuccessBrush}&quot;/&gt;
/// &lt;/converters:SwitchConverter&gt;
/// ...
/// &lt;TextBlock
/// FontWeight=&quot;SemiBold&quot;
/// Foreground=&quot;{x:Bind Status, Converter={StaticResource StatusToColorSwitchConverter}}&quot;
/// Text = &quot;{x:Bind Status}&quot; /&gt;
/// </example>
[ContentProperty(Name = nameof(SwitchCases))]
public sealed partial class SwitchConverter : DependencyObject, IValueConverter
{
/// <summary>
/// Gets or sets a value representing the collection of cases to evaluate.
/// </summary>
public CaseCollection SwitchCases
{
get { return (CaseCollection)GetValue(SwitchCasesProperty); }
set { SetValue(SwitchCasesProperty, value); }
}

/// <summary>
/// Identifies the <see cref="SwitchCases"/> property.
/// </summary>
public static readonly DependencyProperty SwitchCasesProperty =
DependencyProperty.Register(nameof(SwitchCases), typeof(CaseCollection), typeof(SwitchConverter), new PropertyMetadata(null));

/// <summary>
/// Gets or sets a value indicating which type to first cast and compare provided values against.
/// </summary>
public Type TargetType
{
get { return (Type)GetValue(TargetTypeProperty); }
set { SetValue(TargetTypeProperty, value); }
}

/// <summary>
/// Identifies the <see cref="TargetType"/> property.
/// </summary>
public static readonly DependencyProperty TargetTypeProperty =
DependencyProperty.Register(nameof(TargetType), typeof(Type), typeof(SwitchConverter), new PropertyMetadata(null));

/// <summary>
/// Initializes a new instance of the <see cref="SwitchPresenter"/> class.
/// </summary>
public SwitchConverter()
{
// Note: we need to initialize this here so that XAML can automatically add cases without needing this defined around it as the content.
// We don't do this in the PropertyMetadata as then we create a static shared collection for all converters, which we don't want. We want it per instance.
// See https://learn.microsoft.com/windows/uwp/xaml-platform/custom-dependency-properties#initializing-the-collection
SwitchCases = new CaseCollection();
}

public object Convert(object value, Type targetType, object parameter, string language)
{
var result = SwitchCases.EvaluateCases(value, TargetType ?? targetType);

return result?.Content!;
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Loading
Loading