From 7fcb7729ab1eb1252c33bca4e190a4e2cfff426f Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Mon, 15 May 2023 12:06:28 +0200 Subject: [PATCH 1/6] Init --- components/MetadataControl/OpenSolution.bat | 3 + .../samples/Dependencies.props | 31 +++ .../samples/MetadataControl.md | 60 +++++ .../samples/MetadataControlSample.cs | 240 ++++++++++++++++++ .../samples/MetadataControlSample.xaml | 31 +++ .../src/AdditionalAssemblyInfo.cs | 13 + ...lkit.WinUI.Controls.MetadataControl.csproj | 13 + .../MetadataControl/src/Dependencies.props | 31 +++ .../MetadataControl/src/MetadataControl.cs | 213 ++++++++++++++++ .../MetadataControl/src/MetadataControl.xaml | 22 ++ .../MetadataControl/src/MetadataItem.cs | 36 +++ .../MetadataControl/src/MultiTarget.props | 9 + .../MetadataControl/src/Themes/Generic.xaml | 9 + .../tests/MetadataControl.Tests.projitems | 11 + .../tests/MetadataControl.Tests.shproj | 13 + 15 files changed, 735 insertions(+) create mode 100644 components/MetadataControl/OpenSolution.bat create mode 100644 components/MetadataControl/samples/Dependencies.props create mode 100644 components/MetadataControl/samples/MetadataControl.md create mode 100644 components/MetadataControl/samples/MetadataControlSample.cs create mode 100644 components/MetadataControl/samples/MetadataControlSample.xaml create mode 100644 components/MetadataControl/src/AdditionalAssemblyInfo.cs create mode 100644 components/MetadataControl/src/CommunityToolkit.WinUI.Controls.MetadataControl.csproj create mode 100644 components/MetadataControl/src/Dependencies.props create mode 100644 components/MetadataControl/src/MetadataControl.cs create mode 100644 components/MetadataControl/src/MetadataControl.xaml create mode 100644 components/MetadataControl/src/MetadataItem.cs create mode 100644 components/MetadataControl/src/MultiTarget.props create mode 100644 components/MetadataControl/src/Themes/Generic.xaml create mode 100644 components/MetadataControl/tests/MetadataControl.Tests.projitems create mode 100644 components/MetadataControl/tests/MetadataControl.Tests.shproj diff --git a/components/MetadataControl/OpenSolution.bat b/components/MetadataControl/OpenSolution.bat new file mode 100644 index 00000000..814a56d4 --- /dev/null +++ b/components/MetadataControl/OpenSolution.bat @@ -0,0 +1,3 @@ +@ECHO OFF + +powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %* \ No newline at end of file diff --git a/components/MetadataControl/samples/Dependencies.props b/components/MetadataControl/samples/Dependencies.props new file mode 100644 index 00000000..e622e1df --- /dev/null +++ b/components/MetadataControl/samples/Dependencies.props @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/MetadataControl/samples/MetadataControl.md b/components/MetadataControl/samples/MetadataControl.md new file mode 100644 index 00000000..841fbfc8 --- /dev/null +++ b/components/MetadataControl/samples/MetadataControl.md @@ -0,0 +1,60 @@ +--- +title: MetadataControl +author: vgromfeld +description: The MetadataControl control displays a list of labels and hyper-links separated by a bullet. +keywords: MetadataControl, Control, metadata +dev_langs: + - csharp +category: Controls +subcategory: Layout +discussion-id: 0 +issue-id: 0 +--- + +# MetadataControl + +The [MetadataControl](/dotnet/api/microsoft.toolkit.uwp.ui.controls.metadatacontrol) control displays a +list of labels and hyper-links separated by a bullet. +It also generates an accessible string representing its content. + +The bullet separator can be customized using the `Separator` property. +`AccessibleSeparator` is used as a replacement for `Separator` to generate the accessible string. + +The control needs a list of [MediadataItem](https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.UI.Controls.Core/MetadataControl/MetadataItem.cs). +Each item will be displayed either as a text or as an hyper-link (if the `Command`property is set). + +The default control template is using on a `TextBlock`. The style of this `TextBlock` can be customized using the `TextBlockStyle` property. + +> [!SAMPLE MetadataControlSample] + +## Example + +Add the control in the page: + +```xaml + +``` + +Add items to control: + +```cs +metadataControl.Items = new[] +{ + new MetadataItem { Label = "Hello" }, + new MetadataItem { Label = "World", Command = myCommand }, +}; +``` + +## MediadataItem + +A `MediadataItem` contains the information about one entry which will be displayed in the `MetadataControl` + +| Property | Type | Description | +| -- | -- | -- | +| Label | String | Gets or sets the label of the item | +| AccessibleLabel | String | Gets or sets the automation name that will be set on the item. If not set, `Label` will be used. | +| Command | ICommand | Gets or sets the command associated to the item. If null, the item will be displayed as a text field. If set, the item will be displayed as an hyperlink. | +| CommandParameter | Object | Gets or sets the parameter that will be provided to the `Command`| diff --git a/components/MetadataControl/samples/MetadataControlSample.cs b/components/MetadataControl/samples/MetadataControlSample.cs new file mode 100644 index 00000000..5c320b5c --- /dev/null +++ b/components/MetadataControl/samples/MetadataControlSample.cs @@ -0,0 +1,240 @@ +// 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; +using System.Windows.Input; + +namespace MetadataControlExperiment.Samples; + +/// +/// An example sample page of a custom control inheriting from Panel. +/// +[ToolkitSampleTextOption("Separator", " • ", Title = "Separator")] +[ToolkitSampleTextOption("AccessibleSeparator", " • ", Title = "AccessibleSeparator")] + +[ToolkitSample(id: nameof(MetadataControlSample), "MetadataControl", description: $"A sample for showing how to create and use a {nameof(MetadataControl)} control.")] +public sealed partial class MetadataControlSample : Page +{ + private static readonly string[] Labels = "Lorem ipsum dolor sit amet consectetur adipiscing elit".Split(' '); + + private readonly Random _random; + private readonly ObservableCollection _units; + private readonly DelegateCommand _command; + + public MetadataControlSample() + { + this.InitializeComponent(); + _random = new Random(); + _units = new ObservableCollection(); + _command = new DelegateCommand(OnExecuteCommand); + metadataControl.Items = _units; + } + + private string GetRandomLabel() => Labels[_random!.Next(Labels.Length)]; + + private async void OnExecuteCommand(object obj) + { + var dialog = new ContentDialog + { + Title = "Command invoked", + Content = $"Command parameter: {obj}", + CloseButtonText = "OK" + }; + + await dialog.ShowAsync(); + } + + private void AddLabel_Click(object sender, RoutedEventArgs e) + { + if (_units != null) + { + _units.Add(new MetadataItem { Label = GetRandomLabel() }); + } + } + + private void AddCommand_Click(object sender, RoutedEventArgs e) + { + if (_units != null) + { + var label = GetRandomLabel(); + _units.Add(new MetadataItem + { + Label = label, + Command = _command!, + CommandParameter = label, + }); + } + } + + private void Clear_Click(object sender, RoutedEventArgs e) + { + if (_units != null) + { + _units.Clear(); + } + } +} + +public class DelegateCommand : ICommand +{ + private readonly Action commandExecuteAction; + + private readonly Func commandCanExecute; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The action to execute when called. + /// + /// + /// The function to call to determine if the command can execute the action. + /// + /// + /// Thrown if the execute action is null. + /// + public DelegateCommand(Action execute, Func? canExecute = null) + { + if (execute == null) + { + throw new ArgumentNullException(nameof(execute)); + } + + commandExecuteAction = execute; + commandCanExecute = canExecute ?? (() => true); + } + + /// + /// Occurs when changes occur that affect whether or not the command should execute. + /// + public event EventHandler? CanExecuteChanged; + + /// + /// Defines the method that determines whether the command can execute in its current state. + /// + /// + /// The parameter used by the command. + /// + /// + /// Returns a value indicating whether this command can be executed. + /// + public bool CanExecute(object? parameter = null) + { + try + { + return commandCanExecute(); + } + catch + { + return false; + } + } + + /// + /// Defines the method to be called when the command is invoked. + /// + /// + /// The parameter used by the command. + /// + public void Execute(object? parameter) + { + if (!CanExecute(parameter)) + { + return; + } + + try + { + commandExecuteAction(); + } + catch + { + Debugger.Break(); + } + } + + public void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } +} + + +public class DelegateCommand : ICommand +{ + private readonly Action commandExecuteAction; + + private readonly Func commandCanExecute; + + public DelegateCommand(Action executeAction, Func? canExecute = null) + { + if (executeAction == null) + { + throw new ArgumentNullException(nameof(executeAction)); + } + + commandExecuteAction = executeAction; + commandCanExecute = canExecute ?? (e => true); + } + + /// + /// Occurs when changes occur that affect whether or not the command should execute. + /// + public event EventHandler? CanExecuteChanged; + + /// + /// Defines the method that determines whether the command can execute in its current state. + /// + /// + /// The parameter used by the command. + /// + /// + /// Returns a value indicating whether this command can be executed. + /// + public bool CanExecute(object? parameter) + { + try + { + return commandCanExecute(ConvertParameterValue(parameter!)); + } + catch + { + return false; + } + } + + /// + /// Defines the method to be called when the command is invoked. + /// + /// + /// The parameter used by the command. + /// + public void Execute(object? parameter) + { + if (!CanExecute(parameter)) + { + return; + } + + try + { + commandExecuteAction(ConvertParameterValue(parameter!)); + } + catch + { + Debugger.Break(); + } + } + + public void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + + private static T ConvertParameterValue(object parameter) + { + parameter = parameter is T ? parameter : Convert.ChangeType(parameter, typeof(T)); + return (T)parameter; + } +} diff --git a/components/MetadataControl/samples/MetadataControlSample.xaml b/components/MetadataControl/samples/MetadataControlSample.xaml new file mode 100644 index 00000000..d0ff3699 --- /dev/null +++ b/components/MetadataControl/samples/MetadataControlSample.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + +