diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.Tests/CommunityToolkit.Labs.Core.SourceGenerators.Tests/ToolkitSampleMetadataTests.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.Tests/CommunityToolkit.Labs.Core.SourceGenerators.Tests/ToolkitSampleMetadataTests.cs index e18e5ac18..18b91d192 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.Tests/CommunityToolkit.Labs.Core.SourceGenerators.Tests/ToolkitSampleMetadataTests.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.Tests/CommunityToolkit.Labs.Core.SourceGenerators.Tests/ToolkitSampleMetadataTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.ComponentModel.DataAnnotations; namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests; @@ -15,7 +16,7 @@ namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests; public partial class ToolkitSampleMetadataTests { [TestMethod] - public void PaneOption_GeneratesProperty() + public void PaneOption_GeneratesWithoutDiagnostics() { var source = $@" using System.ComponentModel; @@ -24,8 +25,8 @@ public void PaneOption_GeneratesProperty() namespace MyApp {{ - [ToolkitSampleBoolOption(""Test"", ""Toggle y"", false)] - [ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")] + [ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle y"")] + [ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")] [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl @@ -46,6 +47,50 @@ public class UserControl {{ }} VerifyGeneratedDiagnostics(source, string.Empty); } + [TestMethod] + public void PaneOption_GeneratesTitleProperty() + { + var source = """ + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators; + using CommunityToolkit.Labs.Core.SourceGenerators.Attributes; + + namespace MyApp + { + [ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")] + [ToolkitSample(id: nameof(Sample), "Test Sample", description: "")] + public partial class Sample : Windows.UI.Xaml.Controls.UserControl + { + public Sample() + { + var x = this.Test; + var y = this.TextFontFamily; + } + } + } + + namespace Windows.UI.Xaml.Controls + { + public class UserControl { } + } + """; + + var result = """ + #nullable enable + namespace CommunityToolkit.Labs.Core.SourceGenerators; + + public static class ToolkitSampleRegistry + { + public static System.Collections.Generic.Dictionary Listing + { get; } = new() { + ["Sample"] = new CommunityToolkit.Labs.Core.SourceGenerators.Metadata.ToolkitSampleMetadata("Sample", "Test Sample", "", typeof(MyApp.Sample), () => new MyApp.Sample(), null, null, new CommunityToolkit.Labs.Core.SourceGenerators.Metadata.IGeneratedToolkitSampleOptionViewModel[] { new CommunityToolkit.Labs.Core.SourceGenerators.Metadata.ToolkitSampleNumericOptionMetadataViewModel(name: "TextSize", initial: 12, min: 8, max: 48, step: 2, showAsNumberBox: false, title: "FontSize") }) + }; + } + """; + + VerifyGenerateSources("MyApp.Tests", source, new[] { new ToolkitSampleMetadataGenerator() }, ignoreDiagnostics: true, ("ToolkitSampleRegistry.g.cs", result)); + } + // https://github.com/CommunityToolkit/Labs-Windows/issues/175 [TestMethod] public void PaneOption_GeneratesProperty_DuplicatePropNamesAcrossSampleClasses() @@ -57,8 +102,8 @@ public void PaneOption_GeneratesProperty_DuplicatePropNamesAcrossSampleClasses() namespace MyApp {{ - [ToolkitSampleBoolOption(""Test"", ""Toggle y"", false)] - [ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")] + [ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle y"")] + [ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")] [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl @@ -70,8 +115,8 @@ public Sample() }} }} - [ToolkitSampleBoolOption(""Test"", ""Toggle y"", false)] - [ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")] + [ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle y"")] + [ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")] [ToolkitSample(id: nameof(Sample2), ""Test Sample"", description: """")] public partial class Sample2 : Windows.UI.Xaml.Controls.UserControl @@ -101,7 +146,7 @@ public void PaneOptionOnNonSample() namespace MyApp { - [ToolkitSampleBoolOption(""BindToMe"", ""Toggle visibility"", false)] + [ToolkitSampleBoolOption(""BindToMe"", false, Title = ""Toggle visibility"")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl { } @@ -129,7 +174,7 @@ public void PaneOptionWithBadName(string name) namespace MyApp {{ [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] - [ToolkitSampleBoolOption(""{name}"", ""Toggle visibility"", false)] + [ToolkitSampleBoolOption(""{name}"", false, Title = ""Toggle visibility"")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl {{ }} @@ -153,7 +198,7 @@ public void PaneOptionWithConflictingPropertyName() namespace MyApp {{ - [ToolkitSampleBoolOption(""IsVisible"", ""Toggle x"", false)] + [ToolkitSampleBoolOption(""IsVisible"", false, Title = ""Toggle x"")] [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl {{ @@ -179,7 +224,7 @@ public void PaneOptionWithConflictingInheritedPropertyName() namespace MyApp {{ - [ToolkitSampleBoolOption(""IsVisible"", ""Toggle x"", false)] + [ToolkitSampleBoolOption(""IsVisible"", false, Title = ""Toggle x"")] [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] public partial class Sample : Base {{ @@ -209,9 +254,9 @@ public void PaneOptionWithDuplicateName() namespace MyApp {{ - [ToolkitSampleBoolOption(""test"", ""Toggle x"", false)] - [ToolkitSampleBoolOption(""test"", ""Toggle y"", false)] - [ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")] + [ToolkitSampleBoolOption(""test"", false, Title = ""Toggle x"")] + [ToolkitSampleBoolOption(""test"", false, Title = ""Toggle y"")] + [ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", Title = ""Text foreground"")] [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl @@ -237,14 +282,14 @@ public void PaneOptionWithDuplicateName_AllowedBetweenSamples() namespace MyApp {{ - [ToolkitSampleBoolOption(""test"", ""Toggle y"", false)] + [ToolkitSampleBoolOption(""test"", false, Title = ""Toggle y"")] [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl {{ }} - [ToolkitSampleBoolOption(""test"", ""Toggle y"", false)] + [ToolkitSampleBoolOption(""test"", false, Title = ""Toggle y"")] [ToolkitSample(id: nameof(Sample2), ""Test Sample"", description: """")] public partial class Sample2 : Windows.UI.Xaml.Controls.UserControl @@ -270,7 +315,7 @@ public void PaneMultipleChoiceOptionWithNoChoices() namespace MyApp {{ - [ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"")] + [ToolkitSampleMultiChoiceOption(""TextFontFamily"", Title = ""Font family"")] [ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")] public partial class Sample : Windows.UI.Xaml.Controls.UserControl @@ -296,8 +341,8 @@ public void SampleGeneratedOptionAttributeOnUnsupportedType() namespace MyApp {{ - [ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")] - [ToolkitSampleBoolOption(""Test"", ""Toggle visibility"", false)] + [ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")] + [ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle visibility"")] public partial class Sample {{ }} @@ -444,6 +489,55 @@ from assembly in AppDomain.CurrentDomain.GetAssemblies() GC.KeepAlive(sampleAttributeType); } + //// See: https://github.com/CommunityToolkit/dotnet/blob/c2053562d1a4d4829fc04b1cb86d1564c2c4a03c/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs#L103 + /// + /// Generates the requested sources + /// + /// The input source to process. + /// The generators to apply to the input syntax tree. + /// The source files to compare. + private static void VerifyGenerateSources(string assemblyName, string source, IIncrementalGenerator[] generators, bool ignoreDiagnostics = false, params (string Filename, string Text)[] results) + { + // Ensure our types are loaded + Type sampleattributeObjectType = typeof(ToolkitSampleAttribute); + + // Get all assembly references for the loaded assemblies (easy way to pull in all necessary dependencies) + IEnumerable references = + from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !assembly.IsDynamic + let reference = MetadataReference.CreateFromFile(assembly.Location) + select reference; + + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10)); + + // Create a syntax tree with the input source + CSharpCompilation compilation = CSharpCompilation.Create( + assemblyName, + new SyntaxTree[] { syntaxTree }, + references, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(generators).WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options); + + // Run all source generators on the input source code + _ = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation outputCompilation, out ImmutableArray diagnostics); + + // Ensure that no diagnostics were generated + if (!ignoreDiagnostics) + { + CollectionAssert.AreEquivalent(Array.Empty(), diagnostics); + } + + foreach ((string filename, string text) in results) + { + SyntaxTree generatedTree = outputCompilation.SyntaxTrees.Single(tree => Path.GetFileName(tree.FilePath) == filename); + + Assert.AreEqual(text, generatedTree.ToString()); + } + + GC.KeepAlive(sampleattributeObjectType); + } + // From: https://github.com/dotnet/roslyn/blob/main/src/Compilers/Test/Core/SourceGeneration/TestGenerators.cs internal class InMemoryAdditionalText : AdditionalText { diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleBoolOptionAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleBoolOptionAttribute.cs index b15f8ecc5..9cf24e474 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleBoolOptionAttribute.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleBoolOptionAttribute.cs @@ -20,19 +20,14 @@ public sealed class ToolkitSampleBoolOptionAttribute : ToolkitSampleOptionBaseAt /// The name of the generated property, which you can bind to in XAML. /// The initial value for the bound property. /// A title to display on top of this option. - public ToolkitSampleBoolOptionAttribute(string bindingName, string label, bool defaultState, string? title = null) - : base(bindingName, defaultState, title) + public ToolkitSampleBoolOptionAttribute(string bindingName, bool defaultState) + : base(bindingName, defaultState) { - Label = label; + } /// /// The source generator-friendly type name used for casting. /// internal override string TypeName { get; } = "bool"; - - /// - /// A label to display along the boolean option. - /// - public string Label { get; } } diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleMultiChoiceOptionAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleMultiChoiceOptionAttribute.cs index 5716f8b15..b5fa13ff3 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleMultiChoiceOptionAttribute.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleMultiChoiceOptionAttribute.cs @@ -16,13 +16,13 @@ namespace CommunityToolkit.Labs.Core.SourceGenerators.Attributes; public sealed class ToolkitSampleMultiChoiceOptionAttribute : ToolkitSampleOptionBaseAttribute { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the generated property, which you can bind to in XAML. /// A list of the choices to display to the user. Can be literal values, or labeled values. Use a " : " separator (single colon surrounded by at least 1 whitespace) to separate a label from a value. /// A title to display on top of this option. - public ToolkitSampleMultiChoiceOptionAttribute(string bindingName, string? title = null, params string[] choices) - : base(bindingName, null, title) + public ToolkitSampleMultiChoiceOptionAttribute(string bindingName, params string[] choices) + : base(bindingName, null) { Choices = choices.Select(x => { diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleNumericOptionAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleNumericOptionAttribute.cs new file mode 100644 index 000000000..05f283e42 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleNumericOptionAttribute.cs @@ -0,0 +1,63 @@ +// 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 CommunityToolkit.Labs.Core.SourceGenerators.Attributes; + +/// +/// Represents a boolean sample option. +/// +/// +/// Using this attribute will automatically generate an -enabled property +/// that you can bind to in XAML, and displays an options pane alonside your sample which allows the user to manipulate the property. +/// +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public sealed class ToolkitSampleNumericOptionAttribute : ToolkitSampleOptionBaseAttribute +{ + /// + /// Creates a new instance of . + /// + /// The name of the generated property, which you can bind to in XAML. + /// A list of the choices to display to the user. Can be literal values, or labeled values. Use a " : " separator (single colon surrounded by at least 1 whitespace) to separate a label from a value. + /// A title to display on top of this option. + public ToolkitSampleNumericOptionAttribute(string bindingName, double initial = 0, double min = 0, double max = 10, double step = 1, bool showAsNumberBox = false) + : base(bindingName, null) + { + Initial = initial; + Min = min; + Max = max; + Step = step; + ShowAsNumberBox = showAsNumberBox; + } + + /// + /// The default start value. + /// + public double Initial { get; } + + /// + /// The minimal value. + /// + public double Min { get; } + + /// + /// The maximum value. + /// + public double Max { get; } + + /// + /// The step value. + /// + public double Step { get; } + + /// + /// Determines if a Slider or NumberBox is shown. + /// + public bool ShowAsNumberBox { get; } + + /// + /// The source generator-friendly type name used for casting. + /// + internal override string TypeName { get; } = "double"; +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleOptionBaseAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleOptionBaseAttribute.cs index 9fc848a03..1adb446d3 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleOptionBaseAttribute.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleOptionBaseAttribute.cs @@ -15,9 +15,8 @@ public abstract class ToolkitSampleOptionBaseAttribute : Attribute /// The name of the generated property, which you can bind to in XAML. /// The initial value for the bound property. /// A title to display on top of this option. - public ToolkitSampleOptionBaseAttribute(string bindingName, object? defaultState, string? title = null) + public ToolkitSampleOptionBaseAttribute(string bindingName, object? defaultState) { - Title = title; Name = bindingName; DefaultState = defaultState; } @@ -35,7 +34,7 @@ public ToolkitSampleOptionBaseAttribute(string bindingName, object? defaultState /// /// A title to display on top of the option. /// - public string? Title { get; } + public string? Title { get; set; } /// /// The source generator-friendly type name used for casting. diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleTextOptionAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleTextOptionAttribute.cs new file mode 100644 index 000000000..09b17cc65 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/ToolkitSampleTextOptionAttribute.cs @@ -0,0 +1,38 @@ +// 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 CommunityToolkit.Labs.Core.SourceGenerators.Attributes; + +/// +/// Represents a boolean sample option that the user can manipulate and the XAML can bind to. +/// +/// +/// Using this attribute will automatically generate an -enabled property +/// that you can bind to in XAML, and displays an options pane alonside your sample which allows the user to manipulate the property. +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public sealed class ToolkitSampleTextOptionAttribute : ToolkitSampleOptionBaseAttribute +{ + /// + /// Creates a new instance of . + /// + /// The name of the generated property, which you can bind to in XAML. + /// The initial value for the bound property. + /// A title to display on top of this option. + public ToolkitSampleTextOptionAttribute(string bindingName, string? placeholderText = null) + : base(bindingName, placeholderText) + { + PlaceholderText = placeholderText; + } + + /// + /// The source generator-friendly type name used for casting. + /// + internal override string TypeName { get; } = "string"; + + /// + /// A label to display along the boolean option. + /// + public string? PlaceholderText { get; } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/GeneratorExtensions.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/GeneratorExtensions.cs index 0cee25009..bab1c6cfb 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators/GeneratorExtensions.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/GeneratorExtensions.cs @@ -141,4 +141,30 @@ public static bool HasFullyQualifiedName(this ISymbol symbol, string name) return parameterTypedConstant.Value; } + + //// From: https://github.com/CommunityToolkit/dotnet/blob/e6e09406745e2262f7946bcf8089a8f0ec4e074a/src/CommunityToolkit.Mvvm.SourceGenerators/Extensions/AttributeDataExtensions.cs#L67 + /// + /// Tries to get a given named argument value from an instance, if present. + /// + /// The type of argument to check. + /// The target instance to check. + /// The name of the argument to check. + /// The resulting argument value, if present. + /// Whether or not contains an argument named with a valid value. + public static bool TryGetNamedArgument(this AttributeData attributeData, string name, out T? value) + { + foreach (KeyValuePair properties in attributeData.NamedArguments) + { + if (properties.Key == name) + { + value = (T?)properties.Value.Value; + + return true; + } + } + + value = default; + + return false; + } } diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleBoolOptionMetadataViewModel.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleBoolOptionMetadataViewModel.cs index 259a81297..1200b4e15 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleBoolOptionMetadataViewModel.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleBoolOptionMetadataViewModel.cs @@ -15,18 +15,16 @@ namespace CommunityToolkit.Labs.Core.SourceGenerators.Metadata; /// public class ToolkitSampleBoolOptionMetadataViewModel : IGeneratedToolkitSampleOptionViewModel { - private string _label; private string? _title; private object _value; /// /// Creates a new instance of . /// - public ToolkitSampleBoolOptionMetadataViewModel(string id, string label, bool defaultState, string? title = null) + public ToolkitSampleBoolOptionMetadataViewModel(string name, bool defaultState, string? title = null) { - Name = id; + Name = name; _title = title; - _label = label; _value = defaultState; } @@ -68,19 +66,6 @@ public object? Value } } - /// - /// A label to display along the boolean option. - /// - public string Label - { - get => _label; - set - { - _label = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Label))); - } - } - /// /// A title to display on top of the boolean option. /// diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleNumericOptionMetadataViewModel.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleNumericOptionMetadataViewModel.cs new file mode 100644 index 000000000..825849d3d --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleNumericOptionMetadataViewModel.cs @@ -0,0 +1,101 @@ +// 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.Labs.Core.SourceGenerators.Attributes; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.Metadata; + +/// +/// An INPC-enabled metadata container for data defined in an . +/// +public class ToolkitSampleNumericOptionMetadataViewModel : IGeneratedToolkitSampleOptionViewModel +{ + private string? _title; + private object _value; + + /// + /// Creates a new instance of . + /// + public ToolkitSampleNumericOptionMetadataViewModel(string name, double initial = 0, double min = 0, double max = 10, double step = 1, bool showAsNumberBox = false, string? title = null) + { + Name = name; + _title = title; + _value = initial; + Max = max; + Min = min; + Step = step; + ShowAsNumberBox = showAsNumberBox; + } + + /// + public event PropertyChangedEventHandler? PropertyChanged; + + /// + /// A unique identifier for this option. + /// + /// + /// Used by the sample system to match up to the original and the control that declared it. + /// + public string Name { get; } + + /// + /// The available options presented to the user. + /// + /// + /// The initial double value. + /// + public double Initial + { + get => (double)_value; + set + { + _value = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name)); + } + } + + /// + public object? Value + { + get => Initial; + set + { + Initial = (double)(value ?? 0.0); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name)); + } + } + + /// + /// The Minimum value of the slider. + /// + public double Min { get; } + + /// + /// The Maximum value of the slider. + /// + public double Max { get; } + + /// + /// The StepFrequency value of the slider. + /// + public double Step { get; } + + /// + /// Determines if a Slider or NumberBox is shown. + /// + public bool ShowAsNumberBox { get; } + + /// + /// A label to display along the slider. + /// + public string? Title + { + get => _title; + set + { + _title = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title))); + } + } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleTextOptionMetadataViewModel.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleTextOptionMetadataViewModel.cs new file mode 100644 index 000000000..4af4d94fb --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitSampleTextOptionMetadataViewModel.cs @@ -0,0 +1,81 @@ +// 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.Labs.Core.SourceGenerators.Attributes; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.Metadata; + +/// +/// An INPC-enabled metadata container for data defined in an . +/// +/// +/// Instances of these are generated by the and +/// provided to the app alongside the sample registration. +/// +public class ToolkitSampleTextOptionMetadataViewModel : IGeneratedToolkitSampleOptionViewModel +{ + private string? _title; + private object _value; + + /// + /// Creates a new instance of . + /// + public ToolkitSampleTextOptionMetadataViewModel(string name, string placeholderText = "", string? title = null) + { + Name = name; + _title = title; + _value = placeholderText; + } + + /// + public event PropertyChangedEventHandler? PropertyChanged; + + /// + /// A unique identifier for this option. + /// + /// + /// Used by the sample system to match up to the original and the control that declared it. + /// + public string Name { get; } + + /// + /// The current boolean value. + /// + /// + /// Provided to accomodate binding to a property that is a non-nullable . + /// + public string PlaceholderText + { + get => (string)_value; + set + { + _value = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name)); + } + } + + /// + public object? Value + { + get => PlaceholderText; + set + { + PlaceholderText = (string)(value ?? string.Empty); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name)); + } + } + + /// + /// A title to display on top of the boolean option. + /// + public string? Title + { + get => _title; + set + { + _title = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title))); + } + } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleMetadataGenerator.Sample.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleMetadataGenerator.Sample.cs index ea17bcb25..4fcf1873b 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleMetadataGenerator.Sample.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleMetadataGenerator.Sample.cs @@ -58,13 +58,33 @@ void Execute(IncrementalValuesProvider types, bool skipDiagnostics = fa var generatedPaneOptions = allAttributeData .Select(static (x, _) => { - if (x.Item2.TryReconstructAs() is ToolkitSampleBoolOptionAttribute boolOptionAttribute) - return (x.Item1, (ToolkitSampleOptionBaseAttribute)boolOptionAttribute); - - if (x.Item2.TryReconstructAs() is ToolkitSampleMultiChoiceOptionAttribute multiChoiceOptionAttribute) - return (x.Item1, (ToolkitSampleOptionBaseAttribute)multiChoiceOptionAttribute); + (ISymbol Symbol, ToolkitSampleOptionBaseAttribute Attribute) item = default; - return default; + // Try and get base attribute of whatever sample attribute types we support. + if (x.Item2.TryReconstructAs() is ToolkitSampleBoolOptionAttribute boolOptionAttribute) + { + item = (x.Item1, boolOptionAttribute); + } + else if (x.Item2.TryReconstructAs() is ToolkitSampleMultiChoiceOptionAttribute multiChoiceOptionAttribute) + { + item = (x.Item1, multiChoiceOptionAttribute); + } + else if (x.Item2.TryReconstructAs() is ToolkitSampleNumericOptionAttribute numericOptionAttribute) + { + item = (x.Item1, numericOptionAttribute); + } + else if (x.Item2.TryReconstructAs() is ToolkitSampleTextOptionAttribute textOptionAttribute) + { + item = (x.Item1, textOptionAttribute); + } + + // Add extra property data, like Title back to Attribute + if (item.Attribute != null && x.Item2.TryGetNamedArgument(nameof(ToolkitSampleOptionBaseAttribute.Title), out string? title) && !string.IsNullOrWhiteSpace(title)) + { + item.Attribute.Title = title; + } + + return item; }) .Where(static x => x != default) .Collect(); @@ -101,6 +121,7 @@ void Execute(IncrementalValuesProvider types, bool skipDiagnostics = fa var currentAssembly = data.Right; var isExecutingInSampleProject = currentAssembly?.EndsWith(".Samples") ?? false; + var isExecutingInTestProject = currentAssembly?.EndsWith(".Tests") ?? false; // Reconstruct sample metadata from attributes var sampleMetadata = toolkitSampleAttributeData @@ -126,7 +147,9 @@ void Execute(IncrementalValuesProvider types, bool skipDiagnostics = fa ReportDocumentDiagnostics(ctx, sampleMetadata, markdownFileData, toolkitSampleAttributeData, docFrontMatter); } - if (!isExecutingInSampleProject && !skipRegistry) + // For tests we need one pass to do diagnostics and registry as we're in a contrived environment that'll have both our scenarios. Though we check if we have anything to write, as we will hit both executes. + if ((!isExecutingInSampleProject && !skipRegistry) || + (isExecutingInTestProject && (docFrontMatter.Any() || sampleMetadata.Any()))) { CreateDocumentRegistry(ctx, docFrontMatter); CreateSampleRegistry(ctx, sampleMetadata); @@ -281,11 +304,19 @@ private static IEnumerable BuildNewGeneratedSampleOptionMetadataSource(T { if (item is ToolkitSampleMultiChoiceOptionAttribute multiChoiceAttr) { - yield return $@"new {typeof(ToolkitSampleMultiChoiceOptionMetadataViewModel).FullName}(name: ""{item.Name}"", options: new[] {{ {string.Join(",", multiChoiceAttr.Choices.Select(x => $@"new {typeof(MultiChoiceOption).FullName}(""{x.Label}"", ""{x.Value}"")").ToArray())} }}, title: ""{item.Title}"")"; + yield return $@"new {typeof(ToolkitSampleMultiChoiceOptionMetadataViewModel).FullName}(name: ""{multiChoiceAttr.Name}"", options: new[] {{ {string.Join(",", multiChoiceAttr.Choices.Select(x => $@"new {typeof(MultiChoiceOption).FullName}(""{x.Label}"", ""{x.Value}"")").ToArray())} }}, title: ""{multiChoiceAttr.Title}"")"; } else if (item is ToolkitSampleBoolOptionAttribute boolAttribute) { - yield return $@"new {typeof(ToolkitSampleBoolOptionMetadataViewModel).FullName}(id: ""{boolAttribute.Name}"", label: ""{boolAttribute.Label}"", defaultState: {boolAttribute.DefaultState?.ToString().ToLower()}, title: ""{boolAttribute.Title}"")"; + yield return $@"new {typeof(ToolkitSampleBoolOptionMetadataViewModel).FullName}(name: ""{boolAttribute.Name}"", defaultState: {boolAttribute.DefaultState?.ToString().ToLower()}, title: ""{boolAttribute.Title}"")"; + } + else if (item is ToolkitSampleNumericOptionAttribute numericAttribute) + { + yield return $@"new {typeof(ToolkitSampleNumericOptionMetadataViewModel).FullName}(name: ""{numericAttribute.Name}"", initial: {numericAttribute.Initial}, min: {numericAttribute.Min}, max: {numericAttribute.Max}, step: {numericAttribute.Step}, showAsNumberBox: {numericAttribute.ShowAsNumberBox.ToString().ToLower()}, title: ""{numericAttribute.Title}"")"; + } + else if (item is ToolkitSampleTextOptionAttribute textAttribute) + { + yield return $@"new {typeof(ToolkitSampleTextOptionMetadataViewModel).FullName}(name: ""{textAttribute.Name}"", placeholderText: ""{textAttribute.PlaceholderText}"", title: ""{textAttribute.Title}"")"; } else { diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleOptionGenerator.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleOptionGenerator.cs index 005c55cae..944dc90b4 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleOptionGenerator.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleOptionGenerator.cs @@ -41,6 +41,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context) if (x.Item2.TryReconstructAs() is ToolkitSampleMultiChoiceOptionAttribute multiChoiceOptionAttribute) return (Attribute: (ToolkitSampleOptionBaseAttribute)multiChoiceOptionAttribute, ContainingClassSymbol: x.Item1, Type: typeof(ToolkitSampleMultiChoiceOptionMetadataViewModel)); + if(x.Item2.TryReconstructAs() is ToolkitSampleNumericOptionAttribute numericOptionAttribute) + return (Attribute: (ToolkitSampleOptionBaseAttribute)numericOptionAttribute, ContainingClassSymbol: x.Item1, Type: typeof(ToolkitSampleNumericOptionMetadataViewModel)); + + if (x.Item2.TryReconstructAs() is ToolkitSampleTextOptionAttribute textOptionAttribute) + return (Attribute: (ToolkitSampleOptionBaseAttribute)textOptionAttribute, ContainingClassSymbol: x.Item1, Type: typeof(ToolkitSampleTextOptionMetadataViewModel)); + return default; }) .Where(x => x != default); diff --git a/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionTemplateSelector.cs b/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionTemplateSelector.cs index 9c6661005..ea28dc64a 100644 --- a/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionTemplateSelector.cs +++ b/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionTemplateSelector.cs @@ -15,12 +15,21 @@ internal class GeneratedSampleOptionTemplateSelector : DataTemplateSelector public DataTemplate? MultiChoiceOptionTemplate { get; set; } + public DataTemplate? SliderOptionTemplate { get; set; } + + public DataTemplate? NumberBoxOptionTemplate { get; set; } + + public DataTemplate? TextOptionTemplate { get; set; } + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { return item switch { ToolkitSampleBoolOptionMetadataViewModel => BoolOptionTemplate ?? base.SelectTemplateCore(item, container), ToolkitSampleMultiChoiceOptionMetadataViewModel => MultiChoiceOptionTemplate ?? base.SelectTemplateCore(item, container), + ToolkitSampleNumericOptionMetadataViewModel { ShowAsNumberBox: true } => NumberBoxOptionTemplate ?? base.SelectTemplateCore(item, container), + ToolkitSampleNumericOptionMetadataViewModel { ShowAsNumberBox: false } => SliderOptionTemplate ?? base.SelectTemplateCore(item, container), + ToolkitSampleTextOptionMetadataViewModel => TextOptionTemplate ?? base.SelectTemplateCore(item, container), _ => base.SelectTemplateCore(item, container), }; } diff --git a/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionsRenderer.xaml b/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionsRenderer.xaml index 2bedb1045..f2fbb25a5 100644 --- a/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionsRenderer.xaml +++ b/common/CommunityToolkit.Labs.Shared/Renderers/GeneratedSampleOptionsRenderer.xaml @@ -1,4 +1,4 @@ - + - - - - + - - - - + + + + + + + + + + + + + + + + + ItemsSource="{x:Bind SampleOptions, Mode=OneWay}"> + + + + + + diff --git a/labs/SettingsControls/samples/SettingsControls.Samples/ClickableSettingsCardSample.xaml.cs b/labs/SettingsControls/samples/SettingsControls.Samples/ClickableSettingsCardSample.xaml.cs index be04f25a3..2fe777917 100644 --- a/labs/SettingsControls/samples/SettingsControls.Samples/ClickableSettingsCardSample.xaml.cs +++ b/labs/SettingsControls/samples/SettingsControls.Samples/ClickableSettingsCardSample.xaml.cs @@ -2,17 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.ComponentModel; + namespace SettingsControlsExperiment.Samples; -[ToolkitSampleBoolOption("IsCardEnabled", "Is Enabled", true)] -// Single values without a colon are used for both label and value. -// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -//[ToolkitSampleMultiChoiceOption("TextSize", title: "Text size", "Small : 12", "Normal : 16", "Big : 32")] -//[ToolkitSampleMultiChoiceOption("TextFontFamily", title: "Font family", "Segoe UI", "Arial", "Consolas")] -//[ToolkitSampleMultiChoiceOption("TextForeground", title: "Text foreground", -// "Teal : #0ddc8c", -// "Sand : #e7a676", -// "Dull green : #5d7577")] +[ToolkitSampleBoolOption("IsCardEnabled", true, Title = "Is Enabled")] [ToolkitSample(id: nameof(ClickableSettingsCardSample), "ClickableSettingsCardSample", description: "A sample for showing how SettingsCard can be static or clickable.")] public sealed partial class ClickableSettingsCardSample : Page diff --git a/labs/SettingsControls/samples/SettingsControls.Samples/SettingsCardSample.xaml.cs b/labs/SettingsControls/samples/SettingsControls.Samples/SettingsCardSample.xaml.cs index 3e4f3480b..596e18b53 100644 --- a/labs/SettingsControls/samples/SettingsControls.Samples/SettingsCardSample.xaml.cs +++ b/labs/SettingsControls/samples/SettingsControls.Samples/SettingsCardSample.xaml.cs @@ -4,7 +4,7 @@ namespace SettingsControlsExperiment.Samples; -[ToolkitSampleBoolOption("IsCardEnabled", "Is Enabled", true)] +[ToolkitSampleBoolOption("IsCardEnabled", true, Title = "Is Enabled")] // Single values without a colon are used for both label and value. // To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). //[ToolkitSampleMultiChoiceOption("TextSize", title: "Text size", "Small : 12", "Normal : 16", "Big : 32")] diff --git a/labs/SettingsControls/samples/SettingsControls.Samples/SettingsExpanderSample.xaml.cs b/labs/SettingsControls/samples/SettingsControls.Samples/SettingsExpanderSample.xaml.cs index a210999a7..7c4455185 100644 --- a/labs/SettingsControls/samples/SettingsControls.Samples/SettingsExpanderSample.xaml.cs +++ b/labs/SettingsControls/samples/SettingsControls.Samples/SettingsExpanderSample.xaml.cs @@ -4,8 +4,8 @@ namespace SettingsControlsExperiment.Samples; -[ToolkitSampleBoolOption("IsCardEnabled", "Is Enabled", true)] -[ToolkitSampleBoolOption("IsCardExpanded", "Is Expanded", false)] +[ToolkitSampleBoolOption("IsCardEnabled", true, Title = "Is Enabled")] +[ToolkitSampleBoolOption("IsCardExpanded", false, Title = "Is Expanded")] // Single values without a colon are used for both label and value. // To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). //[ToolkitSampleMultiChoiceOption("TextSize", title: "Text size", "Small : 12", "Normal : 16", "Big : 32")] diff --git a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml index 8cbf02f9c..c744fd697 100644 --- a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml +++ b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml @@ -9,6 +9,8 @@ mc:Ignorable="d"> + diff --git a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml.cs b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml.cs index e5fd23e2e..26484e6b3 100644 --- a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml.cs +++ b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateCustomSample.xaml.cs @@ -7,7 +7,8 @@ namespace ProjectTemplateExperiment.Samples; /// /// An example sample page of a custom control inheriting from Panel. /// -[ToolkitSampleMultiChoiceOption("LayoutOrientation", title: "Orientation", "Horizontal", "Vertical")] +[ToolkitSampleTextOption("TitleText", "This is a title", Title = "Input the text")] +[ToolkitSampleMultiChoiceOption("LayoutOrientation", "Horizontal", "Vertical", Title = "Orientation")] [ToolkitSample(id: nameof(ProjectTemplateCustomSample), "Custom control", description: $"A sample for showing how to create and use a {nameof(ProjectTemplate)} custom control.")] public sealed partial class ProjectTemplateCustomSample : Page diff --git a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedSample.xaml.cs b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedSample.xaml.cs index cbfb9b2a3..1cce04293 100644 --- a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedSample.xaml.cs +++ b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedSample.xaml.cs @@ -4,15 +4,12 @@ namespace ProjectTemplateExperiment.Samples; -[ToolkitSampleBoolOption("IsTextVisible", "IsVisible", true)] +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] // Single values without a colon are used for both label and value. // To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleMultiChoiceOption("TextSize", title: "Text size", "Small : 12", "Normal : 16", "Big : 32")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", title: "Font family", "Segoe UI", "Arial", "Consolas")] -[ToolkitSampleMultiChoiceOption("TextForeground", title: "Text foreground", - "Teal : #0ddc8c", - "Sand : #e7a676", - "Dull green : #5d7577")] +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] [ToolkitSample(id: nameof(ProjectTemplateTemplatedSample), "Templated control", description: "A sample for showing how to create and use a templated control.")] public sealed partial class ProjectTemplateTemplatedSample : Page diff --git a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedStyleCustomSample.xaml.cs b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedStyleCustomSample.xaml.cs index 26142fad1..fefbad5de 100644 --- a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedStyleCustomSample.xaml.cs +++ b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateTemplatedStyleCustomSample.xaml.cs @@ -4,15 +4,12 @@ namespace ProjectTemplateExperiment.Samples; -[ToolkitSampleBoolOption("IsTextVisible", "IsVisible", true)] +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] // Single values without a colon are used for both label and value. // To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleMultiChoiceOption("TextSize", title: "Text size", "Small : 12", "Normal : 16", "Big : 32")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", title: "Font family", "Segoe UI", "Arial", "Consolas")] -[ToolkitSampleMultiChoiceOption("TextForeground", title: "Text foreground", - "Teal : #0ddc8c", - "Sand : #e7a676", - "Dull green : #5d7577")] +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, true, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] [ToolkitSample(id: nameof(ProjectTemplateTemplatedStyleCustomSample), "Templated control (restyled)", description: "A sample for showing how to create a use and templated control with a custom style.")] public sealed partial class ProjectTemplateTemplatedStyleCustomSample : Page diff --git a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedSample.xaml.cs b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedSample.xaml.cs index f72d260a9..bbeaa0b1f 100644 --- a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedSample.xaml.cs +++ b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedSample.xaml.cs @@ -4,15 +4,12 @@ namespace ProjectTemplateExperiment.Samples; -[ToolkitSampleBoolOption("IsTextVisible", "IsVisible", true)] +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] // Single values without a colon are used for both label and value. // To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleMultiChoiceOption("TextSize", title: "Text size", "Small : 12", "Normal : 16", "Big : 32")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", title: "Font family", "Segoe UI", "Arial", "Consolas")] -[ToolkitSampleMultiChoiceOption("TextForeground", title: "Text foreground", - "Teal : #0ddc8c", - "Sand : #e7a676", - "Dull green : #5d7577")] +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] [ToolkitSample(id: nameof(ProjectTemplateXbindBackedSample), "Backed templated control", description: "A sample for showing how to create and use a templated control with a backed resource dictionary.")] public sealed partial class ProjectTemplateXbindBackedSample : Page diff --git a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedStyleCustomSample.xaml.cs b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedStyleCustomSample.xaml.cs index deadf29a7..ac6f7e56a 100644 --- a/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedStyleCustomSample.xaml.cs +++ b/template/lab/samples/ProjectTemplate.Samples/ProjectTemplateXbindBackedStyleCustomSample.xaml.cs @@ -4,15 +4,12 @@ namespace ProjectTemplateExperiment.Samples; -[ToolkitSampleBoolOption("IsTextVisible", "IsVisible", true)] +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] // Single values without a colon are used for both label and value. // To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleMultiChoiceOption("TextSize", title: "Text size", "Small : 12", "Normal : 16", "Big : 32")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", title: "Font family", "Segoe UI", "Arial", "Consolas")] -[ToolkitSampleMultiChoiceOption("TextForeground", title: "Text foreground", - "Teal : #0ddc8c", - "Sand : #e7a676", - "Dull green : #5d7577")] +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, true, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] [ToolkitSample(id: nameof(ProjectTemplateXbindBackedStyleCustomSample), "Backed templated control (restyled)", description: "A sample for showing how to create and use a templated control with a backed resource dictionary and a custom style.")] public sealed partial class ProjectTemplateXbindBackedStyleCustomSample : Page