From 3acdee433c1c5c25016af70f5e27cffddd222ad7 Mon Sep 17 00:00:00 2001
From: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Thu, 31 Oct 2024 22:47:53 -0700
Subject: [PATCH 1/7] Make the Content of Case for SwitchPresenter an object
instead of an UIElement
---
components/Primitives/src/SwitchPresenter/Case.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/components/Primitives/src/SwitchPresenter/Case.cs b/components/Primitives/src/SwitchPresenter/Case.cs
index d3551f1f..0e95634a 100644
--- a/components/Primitives/src/SwitchPresenter/Case.cs
+++ b/components/Primitives/src/SwitchPresenter/Case.cs
@@ -13,9 +13,9 @@ public partial class Case : DependencyObject
///
/// Gets or sets the Content to display when this case is active.
///
- public UIElement Content
+ public object Content
{
- get { return (UIElement)GetValue(ContentProperty); }
+ get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
@@ -23,7 +23,7 @@ public UIElement Content
/// Identifies the property.
///
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));
///
/// Gets or sets a value indicating whether this is the default case to display when no values match the specified value in the . There should only be a single default case provided. Do not set the property when setting to true. Default is false.
From 72820825b8420e34bb7442f0d856f5a826d12ec4 Mon Sep 17 00:00:00 2001
From: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Thu, 31 Oct 2024 22:49:01 -0700
Subject: [PATCH 2/7] Add more animals
Though this sample is still broken with NAOT on WinUI 3... :( Tried a couple of the suggested workarounds, but they did not work on my machine, see #516
---
.../SwitchPresenterValueSample.xaml | 16 ++++++++++++++--
.../SwitchPresenterValueSample.xaml.cs | 5 ++++-
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml b/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml
index 807d3d57..0070d6d7 100644
--- a/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml
+++ b/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml
@@ -23,6 +23,10 @@
+
+
+
@@ -31,14 +35,22 @@
-
+
+ Text="🦒" />
+
+
+
+
+
+
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml.cs b/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml.cs
index 0fb2f77e..f08a62de 100644
--- a/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml.cs
+++ b/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml.cs
@@ -15,10 +15,13 @@ public SwitchPresenterValueSample()
public enum Animal
{
+ Bunny,
Cat,
Dog,
- Bunny,
+ Giraffe,
Llama,
+ Otter,
+ Owl,
Parrot,
Squirrel
}
From 7254f27554eff32d01d5cd155fa148119128b8d1 Mon Sep 17 00:00:00 2001
From: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Thu, 31 Oct 2024 23:31:43 -0700
Subject: [PATCH 3/7] Add Sample to showcase new power unlocked in
SwitchPresenter v2 with object Content for a Data Transformation with
ContentTemplate
Tested on UWP, WinUI 3, and Uno WASM
---
.../samples/Primitives.Samples.csproj | 18 -------
.../Primitives/samples/SwitchPresenter.md | 6 +++
.../SwitchPresenterLayoutSample.xaml.cs | 2 -
.../SwitchPresenterTemplateSample.xaml | 54 +++++++++++++++++++
.../SwitchPresenterTemplateSample.xaml.cs | 24 +++++++++
.../SwitchPresenterValueSample.xaml | 2 +-
6 files changed, 85 insertions(+), 21 deletions(-)
create mode 100644 components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml
create mode 100644 components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml.cs
diff --git a/components/Primitives/samples/Primitives.Samples.csproj b/components/Primitives/samples/Primitives.Samples.csproj
index 3927b93a..63350207 100644
--- a/components/Primitives/samples/Primitives.Samples.csproj
+++ b/components/Primitives/samples/Primitives.Samples.csproj
@@ -63,22 +63,4 @@
PreserveNewest
-
-
-
- UniformGridSample.xaml
-
-
- DockPanelSample.xaml
-
-
- ConstrainedBoxSample.xaml
-
-
- StaggeredLayoutSample.xaml
-
-
- WrapPanelSample.xaml
-
-
diff --git a/components/Primitives/samples/SwitchPresenter.md b/components/Primitives/samples/SwitchPresenter.md
index 471de5a6..5c166607 100644
--- a/components/Primitives/samples/SwitchPresenter.md
+++ b/components/Primitives/samples/SwitchPresenter.md
@@ -29,3 +29,9 @@ 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.
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs b/components/Primitives/samples/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs
index e5edd4e6..e6067234 100644
--- a/components/Primitives/samples/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs
+++ b/components/Primitives/samples/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs
@@ -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.")]
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml b/components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml
new file mode 100644
index 00000000..bd1df35c
--- /dev/null
+++ b/components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+ Confirmation Code
+ E-ticket number
+ Mileage Plan number
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml.cs b/components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml.cs
new file mode 100644
index 00000000..5858e90d
--- /dev/null
+++ b/components/Primitives/samples/SwitchPresenter/SwitchPresenterTemplateSample.xaml.cs
@@ -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; }
+}
+
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml b/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml
index 0070d6d7..9a8b2cf4 100644
--- a/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml
+++ b/components/Primitives/samples/SwitchPresenter/SwitchPresenterValueSample.xaml
@@ -22,7 +22,7 @@
SelectedIndex="0" />
+ Value="{x:Bind AnimalPicker.SelectedItem, Mode=OneWay}">
From 4d3d5c33c33cbb233c903d77df98bf6427b4d920 Mon Sep 17 00:00:00 2001
From: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Fri, 1 Nov 2024 00:41:37 -0700
Subject: [PATCH 4/7] =?UTF-8?q?Introducing=20SwitchConverter!=20?=
=?UTF-8?q?=F0=9F=A6=99=E2=9D=A4=EF=B8=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Migrate core logic from SwitchPresenter and generlize to share between both SwitchPresenter and SwitchConverter
Added small demo of SwitchConverter to SwitchPresenter docs
Tested on UWP, WinUI 3, and Uno Platform
---
.../Primitives/samples/SwitchPresenter.md | 6 +
.../SwitchConverterBrushSample.xaml | 45 ++++++
.../SwitchConverterBrushSample.xaml.cs | 23 +++
.../src/SwitchPresenter/SwitchConverter.cs | 80 +++++++++++
.../src/SwitchPresenter/SwitchHelpers.cs | 135 ++++++++++++++++++
.../src/SwitchPresenter/SwitchPresenter.cs | 125 +---------------
6 files changed, 295 insertions(+), 119 deletions(-)
create mode 100644 components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml
create mode 100644 components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml.cs
create mode 100644 components/Primitives/src/SwitchPresenter/SwitchConverter.cs
create mode 100644 components/Primitives/src/SwitchPresenter/SwitchHelpers.cs
diff --git a/components/Primitives/samples/SwitchPresenter.md b/components/Primitives/samples/SwitchPresenter.md
index 5c166607..2c3590bc 100644
--- a/components/Primitives/samples/SwitchPresenter.md
+++ b/components/Primitives/samples/SwitchPresenter.md
@@ -35,3 +35,9 @@ We can also invert the paradigm a bit with a `SwitchPresenter` to do data transf
> [!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]
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml b/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml
new file mode 100644
index 00000000..f5f3e9b7
--- /dev/null
+++ b/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+ Warning
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Error
+ Warning
+ Success
+
+
+
+
+
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml.cs b/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml.cs
new file mode 100644
index 00000000..c8f259a0
--- /dev/null
+++ b/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml.cs
@@ -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,
+}
diff --git a/components/Primitives/src/SwitchPresenter/SwitchConverter.cs b/components/Primitives/src/SwitchPresenter/SwitchConverter.cs
new file mode 100644
index 00000000..7ce8bad2
--- /dev/null
+++ b/components/Primitives/src/SwitchPresenter/SwitchConverter.cs
@@ -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;
+
+///
+/// A helper which can automatically translate incoming data to a set of resulting values defined in XAML.
+///
+///
+/// <converters:SwitchConverter x:Key="StatusToColorSwitchConverter"
+/// TargetType="models:CheckStatus">
+/// <controls:Case Value="Error" Content="{ThemeResource SystemFillColorCriticalBrush}"/>
+/// <controls:Case Value="Warning" Content="{ThemeResource SystemFillColorCautionBrush}"/>
+/// <controls:Case Value="Success" Content="{ThemeResource SystemFillColorSuccessBrush}"/>
+/// </converters:SwitchConverter>
+/// ...
+/// <TextBlock
+/// FontWeight="SemiBold"
+/// Foreground="{x:Bind Status, Converter={StaticResource StatusToColorSwitchConverter}}"
+/// Text = "{x:Bind Status}" />
+///
+[ContentProperty(Name = nameof(SwitchCases))]
+public sealed partial class SwitchConverter : DependencyObject, IValueConverter
+{
+ ///
+ /// Gets or sets a value representing the collection of cases to evaluate.
+ ///
+ public CaseCollection SwitchCases
+ {
+ get { return (CaseCollection)GetValue(SwitchCasesProperty); }
+ set { SetValue(SwitchCasesProperty, value); }
+ }
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty SwitchCasesProperty =
+ DependencyProperty.Register(nameof(SwitchCases), typeof(CaseCollection), typeof(SwitchConverter), new PropertyMetadata(null));
+
+ ///
+ /// Gets or sets a value indicating which type to first cast and compare provided values against.
+ ///
+ public Type TargetType
+ {
+ get { return (Type)GetValue(TargetTypeProperty); }
+ set { SetValue(TargetTypeProperty, value); }
+ }
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty TargetTypeProperty =
+ DependencyProperty.Register(nameof(TargetType), typeof(Type), typeof(SwitchConverter), new PropertyMetadata(null));
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ 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();
+ }
+}
diff --git a/components/Primitives/src/SwitchPresenter/SwitchHelpers.cs b/components/Primitives/src/SwitchPresenter/SwitchHelpers.cs
new file mode 100644
index 00000000..c3698931
--- /dev/null
+++ b/components/Primitives/src/SwitchPresenter/SwitchHelpers.cs
@@ -0,0 +1,135 @@
+// 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 CommunityToolkit.WinUI.Controls;
+
+///
+/// Internal helpers for use between and .
+/// The logic here is the main code which looks across a to match a specific with a given value while converting types based on the property. This will handle values as well as values compatible with the method.
+///
+internal static partial class SwitchHelpers
+{
+ ///
+ /// Extension method for a set of cases to find the matching case given its value and type.
+ ///
+ /// The collection of s in a
+ /// The value of the to find
+ /// The desired type of the result for automatic conversion
+ /// The discovered value, the default value, or null
+ internal static Case? EvaluateCases(this CaseCollection switchCases, object value, Type targetType)
+ {
+ if (switchCases == null ||
+ switchCases.Count == 0)
+ {
+ // If we have no cases, then we can't match anything.
+ return null;
+ }
+
+ Case? xdefault = null;
+ Case? newcase = null;
+
+ foreach (Case xcase in switchCases)
+ {
+ if (xcase.IsDefault)
+ {
+ // If there are multiple default cases provided, this will override and just grab the last one, the developer will have to fix this in their XAML. We call this out in the case comments.
+ xdefault = xcase;
+ continue;
+ }
+
+ if (CompareValues(value, xcase.Value, targetType))
+ {
+ newcase = xcase;
+ break;
+ }
+ }
+
+ if (newcase == null && xdefault != null)
+ {
+ // Inject default if we found one without matching anything
+ newcase = xdefault;
+ }
+
+ return newcase;
+ }
+
+ ///
+ /// Compares two values using the TargetType.
+ ///
+ /// Our main value in our SwitchPresenter.
+ /// The value from the case to compare to.
+ /// true if the two values are equal
+ internal static bool CompareValues(object compare, object value, Type targetType)
+ {
+ if (compare == null || value == null)
+ {
+ return compare == value;
+ }
+
+ if (targetType == null ||
+ (targetType == compare.GetType() &&
+ targetType == value.GetType()))
+ {
+ // Default direct object comparison or we're all the proper type
+ return compare.Equals(value);
+ }
+ else if (compare.GetType() == targetType)
+ {
+ // If we have a TargetType and the first value is the right type
+ // Then our 2nd value isn't, so convert to string and coerce.
+ var valueBase2 = ConvertValue(targetType, value);
+
+ return compare.Equals(valueBase2);
+ }
+
+ // Neither of our two values matches the type so
+ // we'll convert both to a String and try and coerce it to the proper type.
+ var compareBase = ConvertValue(targetType, compare);
+
+ var valueBase = ConvertValue(targetType, value);
+
+ return compareBase.Equals(valueBase);
+ }
+
+ ///
+ /// Helper method to convert a value from a source type to a target type.
+ ///
+ /// The target type
+ /// The value to convert
+ /// The converted value
+ internal static object ConvertValue(Type targetType, object value)
+ {
+ if (targetType.IsInstanceOfType(value))
+ {
+ return value;
+ }
+ else if (targetType.IsEnum && value is string str)
+ {
+#if HAS_UNO
+ if (Enum.IsDefined(targetType, str))
+ {
+ return Enum.Parse(targetType, str);
+ }
+#else
+ if (Enum.TryParse(targetType, str, out object? result))
+ {
+ return result!;
+ }
+#endif
+
+ static object ThrowExceptionForKeyNotFound()
+ {
+ throw new InvalidOperationException("The requested enum value was not present in the provided type.");
+ }
+
+ return ThrowExceptionForKeyNotFound();
+ }
+ else
+ {
+ return XamlBindingHelper.ConvertValue(targetType, value);
+ }
+ }
+}
diff --git a/components/Primitives/src/SwitchPresenter/SwitchPresenter.cs b/components/Primitives/src/SwitchPresenter/SwitchPresenter.cs
index 870da7dc..9ebc6b6e 100644
--- a/components/Primitives/src/SwitchPresenter/SwitchPresenter.cs
+++ b/components/Primitives/src/SwitchPresenter/SwitchPresenter.cs
@@ -115,20 +115,7 @@ protected override void OnApplyTemplate()
private void EvaluateCases()
{
- if (SwitchCases == null ||
- SwitchCases.Count == 0)
- {
- // If we have no cases, then we can't match anything.
- if (CurrentCase != null)
- {
- // Only bother clearing our actual content if we had something before.
- Content = null;
- CurrentCase = null;
- }
-
- return;
- }
- else if (CurrentCase?.Value != null &&
+ if (CurrentCase?.Value != null &&
CurrentCase.Value.Equals(Value))
{
// If the current case we're on already matches our current value,
@@ -136,114 +123,14 @@ private void EvaluateCases()
return;
}
- Case? xdefault = null;
- Case? newcase = null;
-
- foreach (Case xcase in SwitchCases)
- {
- if (xcase.IsDefault)
- {
- // If there are multiple default cases provided, this will override and just grab the last one, the developer will have to fix this in their XAML. We call this out in the case comments.
- xdefault = xcase;
- continue;
- }
-
- if (CompareValues(Value, xcase.Value))
- {
- newcase = xcase;
- break;
- }
- }
-
- if (newcase == null && xdefault != null)
- {
- // Inject default if we found one without matching anything
- newcase = xdefault;
- }
+ var result = SwitchCases.EvaluateCases(Value, TargetType);
- // Only bother changing things around if we actually have a new case.
- if (newcase != CurrentCase)
+ // Only bother changing things around if we actually have a new case. (this should handle prior null case as well)
+ if (result != CurrentCase)
{
// If we don't have any cases or default, setting these to null is what we want to be blank again.
- Content = newcase?.Content;
- CurrentCase = newcase;
- }
- }
-
- ///
- /// Compares two values using the TargetType.
- ///
- /// Our main value in our SwitchPresenter.
- /// The value from the case to compare to.
- /// true if the two values are equal
- private bool CompareValues(object compare, object value)
- {
- if (compare == null || value == null)
- {
- return compare == value;
- }
-
- if (TargetType == null ||
- (TargetType == compare.GetType() &&
- TargetType == value.GetType()))
- {
- // Default direct object comparison or we're all the proper type
- return compare.Equals(value);
- }
- else if (compare.GetType() == TargetType)
- {
- // If we have a TargetType and the first value is the right type
- // Then our 2nd value isn't, so convert to string and coerce.
- var valueBase2 = ConvertValue(TargetType, value);
-
- return compare.Equals(valueBase2);
- }
-
- // Neither of our two values matches the type so
- // we'll convert both to a String and try and coerce it to the proper type.
- var compareBase = ConvertValue(TargetType, compare);
-
- var valueBase = ConvertValue(TargetType, value);
-
- return compareBase.Equals(valueBase);
- }
-
- ///
- /// Helper method to convert a value from a source type to a target type.
- ///
- /// The target type
- /// The value to convert
- /// The converted value
- internal static object ConvertValue(Type targetType, object value)
- {
- if (targetType.IsInstanceOfType(value))
- {
- return value;
- }
- else if (targetType.IsEnum && value is string str)
- {
-#if HAS_UNO
- if (Enum.IsDefined(targetType, str))
- {
- return Enum.Parse(targetType, str);
- }
-#else
- if (Enum.TryParse(targetType, str, out object? result))
- {
- return result!;
- }
-#endif
-
- static object ThrowExceptionForKeyNotFound()
- {
- throw new InvalidOperationException("The requested enum value was not present in the provided type.");
- }
-
- return ThrowExceptionForKeyNotFound();
- }
- else
- {
- return XamlBindingHelper.ConvertValue(targetType, value);
+ Content = result?.Content;
+ CurrentCase = result;
}
}
}
From 006ba8b542c9e20c05ecc40619f26b55f38ad8cc Mon Sep 17 00:00:00 2001
From: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Thu, 7 Nov 2024 14:55:03 -0800
Subject: [PATCH 5/7] Re-order Success/Warning/Error options for
SwitchConverter example based on PR feedback
---
.../SwitchPresenter/SwitchConverterBrushSample.xaml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml b/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml
index f5f3e9b7..400ebcb0 100644
--- a/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml
+++ b/components/Primitives/samples/SwitchPresenter/SwitchConverterBrushSample.xaml
@@ -20,12 +20,12 @@
-
-
+
+
@@ -33,9 +33,9 @@
- Error
- Warning
Success
+ Warning
+ Error
Date: Thu, 7 Nov 2024 17:34:34 -0800
Subject: [PATCH 6/7] [Test] Add a basic SwitchPresenter test case
Validates the value changes, the case, and the content
---
.../tests/Primitives.Tests.projitems | 10 ++++
.../SwitchPresenterLayoutSample.xaml | 54 +++++++++++++++++++
.../SwitchPresenterLayoutSample.xaml.cs | 13 +++++
.../SwitchPresenter/SwitchPresenterTests.cs | 51 ++++++++++++++++++
4 files changed, 128 insertions(+)
create mode 100644 components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml
create mode 100644 components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs
create mode 100644 components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs
diff --git a/components/Primitives/tests/Primitives.Tests.projitems b/components/Primitives/tests/Primitives.Tests.projitems
index 344de9e5..2e2d6d12 100644
--- a/components/Primitives/tests/Primitives.Tests.projitems
+++ b/components/Primitives/tests/Primitives.Tests.projitems
@@ -9,6 +9,10 @@
PrimitivesExperiment.Tests
+
+ %(Filename)
+
+
@@ -33,4 +37,10 @@
MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
\ No newline at end of file
diff --git a/components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml b/components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml
new file mode 100644
index 00000000..6465b41b
--- /dev/null
+++ b/components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+ Select an option
+ Confirmation Code
+ E-ticket number
+ Mileage Plan number
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs b/components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs
new file mode 100644
index 00000000..d6cbcb1b
--- /dev/null
+++ b/components/Primitives/tests/SwitchPresenter/SwitchPresenterLayoutSample.xaml.cs
@@ -0,0 +1,13 @@
+// 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.Tests;
+
+public sealed partial class SwitchPresenterLayoutSample : Page
+{
+ public SwitchPresenterLayoutSample()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs b/components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs
new file mode 100644
index 00000000..bc7eb43f
--- /dev/null
+++ b/components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs
@@ -0,0 +1,51 @@
+using CommunityToolkit.Tests;
+using CommunityToolkit.Tooling.TestGen;
+using CommunityToolkit.WinUI.Controls;
+
+namespace PrimitivesExperiment.Tests;
+
+[TestClass]
+public partial class SwitchPresenterTests : VisualUITestBase
+{
+ [UIThreadTestMethod]
+ public async Task SwitchPresenterLayoutTest(SwitchPresenterLayoutSample page)
+ {
+ var spresenter = page.FindDescendant();
+ var combobox = page.FindDescendant();
+
+ Assert.IsNotNull(spresenter, "Couldn't find SwitchPresenter");
+ Assert.IsNotNull(combobox, "Couldn't find ComboBox");
+
+ var dcase = spresenter.SwitchCases.OfType().FirstOrDefault(@case => @case.IsDefault);
+
+ Assert.IsNotNull(dcase, "Couldn't find default case");
+
+ // Are we in our initial case?
+ Assert.AreEqual("Select an option", spresenter.Value, "SwitchPresenter not expected starting value");
+ Assert.AreEqual(dcase, spresenter.CurrentCase, "SwitchPresenter not at current default case");
+
+ // Can we find our matching textbox?
+ var tbox = spresenter.FindDescendant();
+ Assert.IsNotNull(tbox, "Couldn't find inner textbox");
+ Assert.AreEqual("Please select a way to lookup your reservation above...", tbox.Text, "Unexpected content for SwitchPresenter default case");
+
+ // Update combobox
+ combobox.SelectedIndex = 1;
+
+ // Wait for update
+ await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { });
+
+ // Are we in the new case?
+ Assert.AreEqual("Confirmation Code", spresenter.Value);
+
+ var ccase = spresenter.SwitchCases.OfType().FirstOrDefault(@case => @case.Value?.ToString() == "Confirmation Code");
+
+ Assert.IsNotNull(ccase, "Couldn't find expected case");
+
+ Assert.AreEqual(ccase, spresenter.CurrentCase, "SwitchPresenter didn't change cases");
+
+ var txtbox = spresenter.FindDescendant();
+ Assert.IsNotNull(txtbox, "Couldn't find new textbox");
+ Assert.AreEqual("Confirmation code", txtbox.Header, "Textbox header not expected value");
+ }
+}
From f47c4f6d4be38cb2c05c80558b57d518e5165475 Mon Sep 17 00:00:00 2001
From: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Fri, 8 Nov 2024 09:07:25 -0800
Subject: [PATCH 7/7] [Test] Add SwitchConverter tests to the primitives
component
Fix test header (failure in CI from last commit)
---
.../tests/Primitives.Tests.projitems | 7 ++
.../SwitchConverterBrushSample.xaml | 53 +++++++++++
.../SwitchConverterBrushSample.xaml.cs | 20 ++++
.../SwitchPresenter/SwitchPresenterTests.cs | 91 +++++++++++++++++++
4 files changed, 171 insertions(+)
create mode 100644 components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml
create mode 100644 components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml.cs
diff --git a/components/Primitives/tests/Primitives.Tests.projitems b/components/Primitives/tests/Primitives.Tests.projitems
index 2e2d6d12..efdf851b 100644
--- a/components/Primitives/tests/Primitives.Tests.projitems
+++ b/components/Primitives/tests/Primitives.Tests.projitems
@@ -9,6 +9,9 @@
PrimitivesExperiment.Tests
+
+ %(Filename)
+
%(Filename)
@@ -38,6 +41,10 @@
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml b/components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml
new file mode 100644
index 00000000..9d6820e3
--- /dev/null
+++ b/components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml
@@ -0,0 +1,53 @@
+
+
+
+
+ Warning
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Success
+ Warning
+ Error
+
+
+
+
diff --git a/components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml.cs b/components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml.cs
new file mode 100644
index 00000000..d5319277
--- /dev/null
+++ b/components/Primitives/tests/SwitchPresenter/SwitchConverterBrushSample.xaml.cs
@@ -0,0 +1,20 @@
+// 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.Tests;
+
+public sealed partial class SwitchConverterBrushSample : Page
+{
+ public SwitchConverterBrushSample()
+ {
+ this.InitializeComponent();
+ }
+}
+
+public enum CheckStatus
+{
+ Error,
+ Warning,
+ Success,
+}
diff --git a/components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs b/components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs
index bc7eb43f..63b5862a 100644
--- a/components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs
+++ b/components/Primitives/tests/SwitchPresenter/SwitchPresenterTests.cs
@@ -1,6 +1,11 @@
+// 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.Tests;
using CommunityToolkit.Tooling.TestGen;
using CommunityToolkit.WinUI.Controls;
+using CommunityToolkit.WinUI.Converters;
namespace PrimitivesExperiment.Tests;
@@ -48,4 +53,90 @@ public async Task SwitchPresenterLayoutTest(SwitchPresenterLayoutSample page)
Assert.IsNotNull(txtbox, "Couldn't find new textbox");
Assert.AreEqual("Confirmation code", txtbox.Header, "Textbox header not expected value");
}
+
+ [UIThreadTestMethod]
+ public async Task SwitchConverterBrushTest(SwitchConverterBrushSample page)
+ {
+ var combobox = page.FindDescendant();
+ var textblock = page.FindDescendant("ResultBlock") as TextBlock;
+
+ Assert.IsNotNull(combobox, "Couldn't find ComboBox");
+ Assert.IsNotNull(textblock, "Couldn't find SwitchPresenter");
+
+ // Are we in our initial case?
+ Assert.AreEqual(0, combobox.SelectedIndex, "ComboBox not initialized");
+ Assert.AreEqual("Success", combobox.SelectedItem, "Not expected starting value in ComboBox");
+
+ // Check the TextBlock's brush
+ Assert.AreEqual(((SolidColorBrush)page.Resources["SystemFillColorSuccessBrush"]).Color, ((SolidColorBrush)textblock.Foreground).Color, "TextBlock not in initial success state");
+
+ // Update combobox
+ combobox.SelectedIndex = 1;
+
+ // Wait for update
+ await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { });
+
+ // Are we in the new case?
+ Assert.AreEqual("Warning", combobox.SelectedItem, "ComboBox didn't change");
+
+ // Check the TextBlock's brush
+ Assert.AreEqual(((SolidColorBrush)page.Resources["SystemFillColorCautionBrush"]).Color, ((SolidColorBrush)textblock.Foreground).Color, "TextBlock not in new state");
+
+ // Update combobox
+ combobox.SelectedIndex = 2;
+
+ // Wait for update
+ await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { });
+
+ // Are we in the new case?
+ Assert.AreEqual("Error", combobox.SelectedItem, "ComboBox didn't change 2");
+
+ // Check the TextBlock's brush
+ Assert.AreEqual(((SolidColorBrush)page.Resources["SystemFillColorCriticalBrush"]).Color, ((SolidColorBrush)textblock.Foreground).Color, "TextBlock not in final state");
+ }
+
+ [UIThreadTestMethod]
+ public void SwitchConverterDirectTest()
+ {
+ // Multiply by 10
+ SwitchConverter sconverter = new()
+ {
+ SwitchCases = new CaseCollection {
+ new Case()
+ {
+ Content = 10,
+ Value = 1,
+ },
+ new Case()
+ {
+ Content = 50,
+ Value = 5,
+ IsDefault = true
+ },
+ new Case()
+ {
+ Content = 30,
+ Value = 3,
+ },
+ new Case()
+ {
+ Content = 20,
+ Value = 2,
+ },
+ },
+ TargetType = typeof(int)
+ };
+
+ Assert.IsNotNull(sconverter);
+ Assert.AreEqual(4, sconverter.SwitchCases.Count, "Not 4 cases");
+
+ var @default = sconverter.Convert(100, typeof(int), string.Empty, string.Empty);
+
+ Assert.AreEqual(50, @default, "Unexpected default return");
+
+ Assert.AreEqual(10, sconverter.Convert(1, typeof(int), string.Empty, string.Empty), "Unexpected result with 1");
+ Assert.AreEqual(20, sconverter.Convert(2, typeof(int), string.Empty, string.Empty), "Unexpected result with 2");
+ Assert.AreEqual(30, sconverter.Convert(3, typeof(int), string.Empty, string.Empty), "Unexpected result with 3");
+ Assert.AreEqual(50, sconverter.Convert(5, typeof(int), string.Empty, string.Empty), "Unexpected result with 5");
+ }
}