Merge pull request #114 from unoplatform/dev/agzi/I86-Chip

fix: Move Chip / ChipGroup to Toolkit
This commit is contained in:
Agnès ZITTE 2021-11-16 15:02:44 -05:00 коммит произвёл GitHub
Родитель 199e6917ca f58e83f8b6
Коммит a62d637b8e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 2688 добавлений и 2 удалений

79
docs/controls/Chip.md Normal file
Просмотреть файл

@ -0,0 +1,79 @@
# Chip
## Summary
`Chip` is a compact `ToggleButton` can be used for selection, filters or for a list of action to trigger. `ChipGroup` can be used to display a list of `Chip`.
## Features
### Chip
| Properties | Type | Description | Supported |
|--------------------|--------------|------------------------------------------------------------|-----------------|
| IsCheckable | bool | Whether the chip can be checked. note: When used inside a ChipGroup, this property will be overwritten by ChipGroup's SelectionMode. | All platforms |
| Icon | object | Icon to display on the chip. | All platforms |
| IconTemplate | DataTemplate | Template to display as the chip icon. | All platforms |
| CanRemove | bool | Whether there's a remove icon on the chip. | All platforms |
| RemoveCommand | TODO | TODO | Not implemented |
| RemoveEvent | TODO | TODO | Not implemented |
### ChipGroup
| Properties | Type | Description | Supported |
|--------------------|-------------------|---------------------------------------------------------------|-----------------|
| SelectionMode | ChipSelectionMode | Gets or sets the selection behavior. (None, Single, Multiple) | All platforms |
| SelectedItem | object | Current selected item. (SelectionMode = Single) | All platforms |
| SelectedItems | IList | Current selected items. (SelectionMode = Multiple) | All platforms |
| IconTemplate | DataTemplate | IconTemplate to use for each `Chip`. | All platforms |
| CanRemove | bool | Whether we display a remove icon for each `Chip` | All platforms |
| RemoveCommand | TODO | TODO | Not implemented |
| RemoveEvent | TODO | TODO | Not implemented |
## Usage
### Chip
```xml
<!-- Filled Input Material chip -->
<toolkit:Chip Content="Chip"
CanRemove="True"
Style="{StaticResource MaterialFilledInputChipStyle}"/>
<!-- Filled Input Material chip chip with icon-->
<toolkit:Chip Content="Chip"
Style="{StaticResource MaterialFilledInputChipStyle}">
<toolkit:Chip.Icon>
<!-- Icon -->
</toolkit:Chip.Icon>
</toolkit:Chip>
```
### ChipGroup
```xml
<!-- Filled Input Material ChipGroup with static items -->
<toolkit:ChipGroup Style="{StaticResource MaterialFilledInputChipGroupStyle}">
<toolkit:Chip Content="Chip" />
<toolkit:Chip Content="Chip"
IsChecked="True" />
<toolkit:Chip Content="Chip" />
</toolkit:ChipGroup>
<!-- Filled Choice Material ChipGroup with dynamic items -->
<toolkit:ChipGroup ItemsSource="{Binding Items}"
Style="{StaticResource MaterialFilledChoiceChipGroupStyle}">
<!-- Outlined Input ChipGroup with custom thumbnail template -->
<toolkit:ChipGroup ItemsSource="{Binding Items}"
Style="{StaticResource MaterialOutlinedInputChipGroupStyle}">
<toolkit:ChipGroup.IconTemplate>
<DataTemplate>
<!-- IconTemplate -->
</DataTemplate>
</toolkit:ChipGroup.IconTemplate>
</toolkit:ChipGroup>
```

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.3 KiB

Просмотреть файл

@ -0,0 +1,528 @@
<Page x:Class="Uno.Toolkit.Samples.Content.Controls.ChipSamplePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converters="using:Uno.Toolkit.Samples.Converters"
xmlns:toolkit="using:Uno.Toolkit.UI.Controls"
xmlns:android="http://uno.ui/android"
xmlns:ios="http://uno.ui/ios"
xmlns:sample="using:Uno.Toolkit.Samples"
mc:Ignorable="d android ios">
<Page.Resources>
<converters:FromNullToValueConverter x:Key="SingleSelectionToValueConverter"
NotNullValue="Selected Item: #"
NullValue="No selection" />
<converters:FromNullToValueConverter x:Key="MultipleSelectionToValueConverter"
NotNullValue="Selected Items:"
NullValue="No selection" />
<Style x:Key="HorizontalScrollViewerStyle"
TargetType="ScrollViewer">
<Setter Property="HorizontalScrollMode"
Value="Auto" />
<Setter Property="HorizontalScrollBarVisibility"
Value="Auto" />
<Setter Property="VerticalScrollMode"
Value="Disabled" />
<Setter Property="VerticalScrollBarVisibility"
Value="Hidden" />
<Setter Property="Margin"
Value="0,10" />
</Style>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<sample:SamplePageLayout>
<sample:SamplePageLayout.MaterialTemplate>
<DataTemplate>
<StackPanel>
<!-- MaterialFilledInputChipStyle -->
<TextBlock Text="Input - Filled"
Margin="0,20,0,0"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialFilledInputChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialFilledInputChipStyle}"
IsEnabled="False">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Selected"
IsChecked="True"
Style="{StaticResource MaterialFilledInputChipStyle}" />
</StackPanel>
</ScrollViewer>
<!-- MaterialOutlinedInputChipStyle -->
<TextBlock Text="Input - Outlined"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialOutlinedInputChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialOutlinedInputChipStyle}"
IsEnabled="False">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Selected"
IsChecked="True"
Style="{StaticResource MaterialOutlinedInputChipStyle}" />
</StackPanel>
</ScrollViewer>
<!-- MaterialFilledChoiceChipStyle -->
<TextBlock Text="Choice - Filled"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialFilledChoiceChipStyle}" />
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialFilledChoiceChipStyle}"
IsEnabled="False" />
<toolkit:Chip Content="Selected"
IsChecked="True"
Style="{StaticResource MaterialFilledChoiceChipStyle}" />
</StackPanel>
</ScrollViewer>
<!-- MaterialOutlinedChoiceChipStyle -->
<TextBlock Text="Choice - Outlined"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialOutlinedChoiceChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialOutlinedChoiceChipStyle}"
IsEnabled="False">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Selected"
IsChecked="True"
Style="{StaticResource MaterialOutlinedChoiceChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
</StackPanel>
</ScrollViewer>
<!-- MaterialFilledFilterChipStyle -->
<TextBlock Text="Filter - Filled"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialFilledFilterChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialFilledFilterChipStyle}"
IsEnabled="False">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Selected"
IsChecked="True"
Style="{StaticResource MaterialFilledFilterChipStyle}" />
</StackPanel>
</ScrollViewer>
<!-- MaterialOutlinedFilterChipStyle -->
<TextBlock Text="Filter - Outlined"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialOutlinedFilterChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialOutlinedFilterChipStyle}"
IsEnabled="False">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Selected"
IsChecked="True"
Style="{StaticResource MaterialOutlinedFilterChipStyle}" />
</StackPanel>
</ScrollViewer>
<!-- MaterialFilledActionChipStyle -->
<TextBlock Text="Action - Filled"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialFilledActionChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialFilledActionChipStyle}"
IsEnabled="False">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
</StackPanel>
</ScrollViewer>
<!-- MaterialOutlinedActionChipStyle -->
<TextBlock Text="Action - Outlined"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<StackPanel Orientation="Horizontal"
Spacing="8">
<toolkit:Chip Content="Enabled"
Style="{StaticResource MaterialOutlinedActionChipStyle}">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
<toolkit:Chip Content="Disabled"
Style="{StaticResource MaterialOutlinedActionChipStyle}"
IsEnabled="False">
<toolkit:Chip.Icon>
<Image Source="ms-appx:///Assets/Avatar.png" />
</toolkit:Chip.Icon>
</toolkit:Chip>
</StackPanel>
</ScrollViewer>
<!-- ### ChipGroup -->
<TextBlock Text="ChipGroup"
Margin="0,36,0,20"
Style="{StaticResource MaterialHeadline6}" />
<!-- ChipGroup Input -->
<TextBlock Text="Input"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.MutableTestCollection}"
ItemRemoved="RemoveChipItem"
Style="{StaticResource MaterialFilledInputChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.IconTemplate>
<DataTemplate>
<Image Source="{Binding Image}" />
</DataTemplate>
</toolkit:ChipGroup.IconTemplate>
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.MutableTestCollection}"
ItemRemoved="RemoveChipItem"
Style="{StaticResource MaterialOutlinedInputChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.IconTemplate>
<DataTemplate>
<Image Source="{Binding Image}" />
</DataTemplate>
</toolkit:ChipGroup.IconTemplate>
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<Button Content="Reset chip items"
Click="ResetChipItems"
Margin="0,10,0,20" />
<!-- ChipGroup Choice -->
<TextBlock Text="Choice"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.TestCollection}"
Style="{StaticResource MaterialFilledChoiceChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.TestCollection}"
Style="{StaticResource MaterialOutlinedChoiceChipGroupStyle}"
SelectionMode="Single"
Margin="8,0,8,8">
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<!-- ChipGroup Filter -->
<TextBlock Text="Filter"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.TestCollection}"
Style="{StaticResource MaterialFilledFilterChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.TestCollection}"
Style="{StaticResource MaterialOutlinedFilterChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.IconTemplate>
<DataTemplate>
<Border Background="{StaticResource MaterialSecondaryBrush}" />
</DataTemplate>
</toolkit:ChipGroup.IconTemplate>
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<!-- ChipGroup Action -->
<TextBlock Text="Action"
Style="{StaticResource MaterialSubtitle1}" />
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.TestCollection}"
Style="{StaticResource MaterialFilledActionChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.IconTemplate>
<DataTemplate>
<Image Source="{Binding Image}" />
</DataTemplate>
</toolkit:ChipGroup.IconTemplate>
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup ItemsSource="{Binding Data.TestCollection}"
Style="{StaticResource MaterialOutlinedActionChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.IconTemplate>
<DataTemplate>
<Image Source="{Binding Image}" />
</DataTemplate>
</toolkit:ChipGroup.IconTemplate>
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<!-- ChipGroup Selection -->
<TextBlock Text="Single Selection with Choice chips"
Style="{StaticResource MaterialSubtitle1}" />
<TextBlock Style="{StaticResource MaterialSubtitle2}">
<Run Text="{Binding ElementName=SingleSelectionChipGroup, Path=SelectedItem, Mode=TwoWay, Converter={StaticResource SingleSelectionToValueConverter}}" /><Run Text="{Binding ElementName=SingleSelectionChipGroup, Path=SelectedItem.Index, Mode=TwoWay}" />
</TextBlock>
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup x:Name="SingleSelectionChipGroup"
ItemsSource="{Binding Data.TestArray}"
SelectedItem="{Binding Data.TestItem}"
Style="{StaticResource MaterialFilledChoiceChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<TextBlock Text="Single Selection with Enum and Choice chips"
Style="{StaticResource MaterialSubtitle1}" />
<TextBlock Style="{StaticResource MaterialSubtitle2}">
<Run Text="{Binding ElementName=SingleEnumSelectionChipGroup, Path=SelectedItem, Mode=TwoWay, Converter={StaticResource SingleSelectionToValueConverter}}" /><Run Text="{Binding ElementName=SingleEnumSelectionChipGroup, Path=SelectedItem, Mode=TwoWay}" />
</TextBlock>
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup x:Name="SingleEnumSelectionChipGroup"
ItemsSource="{Binding Data.TestEnumArray}"
SelectedItem="{Binding Data.TestEnumItem}"
Style="{StaticResource MaterialFilledChoiceChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}"
Text="{Binding}" />
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
<TextBlock Text="Multiple selection with Filter chips"
Style="{StaticResource MaterialSubtitle1}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ElementName=MultipleSelectionChipGroup, Path=SelectedItems, Mode=TwoWay, Converter={StaticResource MultipleSelectionToValueConverter}}"
Style="{StaticResource MaterialSubtitle2}"
VerticalAlignment="Center"
Margin="0,0,4,0" />
<ItemsControl ItemsSource="{Binding ElementName=MultipleSelectionChipGroup, Path=SelectedItems, Mode=TwoWay}"
VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="0,0,2,0"
Style="{StaticResource MaterialSubtitle2}">
<Run Text="#" /><Run Text="{Binding Index}" /><Run Text="," />
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}">
<toolkit:ChipGroup x:Name="MultipleSelectionChipGroup"
ItemsSource="{Binding Data.TestArray}"
SelectedItems="{Binding Data.TestSelectedItems}"
Style="{StaticResource MaterialFilledFilterChipGroupStyle}"
Margin="8,0,8,8">
<toolkit:ChipGroup.ItemTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource MaterialBody1}">
<Run Text="Item #" /><Run Text="{Binding Index}" />
</TextBlock>
</DataTemplate>
</toolkit:ChipGroup.ItemTemplate>
</toolkit:ChipGroup>
</ScrollViewer>
</StackPanel>
</DataTemplate>
</sample:SamplePageLayout.MaterialTemplate>
</sample:SamplePageLayout>
</Grid>
</Page>

Просмотреть файл

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Uno.Toolkit.Samples.Entities.Data;
using Uno.Toolkit.Samples.Entities;
using Uno.Toolkit.UI.Controls;
#if IS_WINUI
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
#else
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
#endif
namespace Uno.Toolkit.Samples.Content.Controls
{
[SamplePage(SampleCategory.Controls, "Chip", SourceSdk.UnoMaterial, DataType = typeof(TestCollections))]
public sealed partial class ChipSamplePage : Page
{
public ChipSamplePage()
{
this.InitializeComponent();
}
private void RemoveChipItem(object sender, ChipItemEventArgs e)
{
if (DataContext is Sample sample)
{
if (sample.Data is TestCollections test)
{
test.RemoveChipItem(e.Item as TestCollections.SelectableData);
}
}
}
private void ResetChipItems(object sender, RoutedEventArgs e)
{
if (DataContext is Sample sample)
{
if (sample.Data is TestCollections test)
{
test.ResetChipItems();
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;
#if IS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
#endif
namespace Uno.Toolkit.Samples.Converters
{
public class FromNullToValueConverter : IValueConverter
{
public object NullValue { get; set; }
public object NotNullValue { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null || value == DependencyProperty.UnsetValue)
{
return NullValue;
}
return NotNullValue;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotSupportedException();
}
}
}

Просмотреть файл

@ -11,6 +11,11 @@ namespace Uno.Toolkit.Samples.Entities.Data
{
public ObservableCollection<SelectableData> MutableTestCollection { get; } = new ObservableCollection<SelectableData>(CreateItems());
public ObservableCollection<SelectableData> TestCollection { get; } = new ObservableCollection<SelectableData>(CreateItems());
public static IEnumerable<SelectableData> TestArray { get; } = CreateItems();
public static IEnumerable<SelectableData> TestSelectedItems { get; } = TestArray.Take(3).ToArray();
public static SelectableData TestItem { get; } = TestArray.First();
public static IEnumerable<DayOfWeek> TestEnumArray { get; } = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday };
public static DayOfWeek TestEnumItem { get; } = DayOfWeek.Monday;
private static IEnumerable<SelectableData> CreateItems()
{
@ -18,7 +23,7 @@ namespace Uno.Toolkit.Samples.Entities.Data
.Select(x => new SelectableData
{
Index = x,
Image = new Uri("ms-appx:///Assets/Cards/Avatar.png"),
Image = new Uri("ms-appx:///Assets/Avatar.png"),
})
.ToArray();
}

Просмотреть файл

@ -22,6 +22,7 @@ namespace Uno.Toolkit.Samples.Entities
Description = attribute.Description;
DocumentationLink = attribute.DocumentationLink;
Data = CreateData(attribute.DataType);
Source = attribute.Source;
SortOrder = attribute.SortOrder;
ViewType = viewType;

Просмотреть файл

@ -7,10 +7,11 @@ namespace Uno.Toolkit.Samples.Entities
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class SamplePageAttribute : Attribute
{
public SamplePageAttribute(SampleCategory category, string title)
public SamplePageAttribute(SampleCategory category, string title, SourceSdk source = SourceSdk.WinUI)
{
Category = category;
Title = title;
Source = source;
}
/// <summary>
@ -26,6 +27,8 @@ namespace Uno.Toolkit.Samples.Entities
public Type DataType { get; set; }
public SourceSdk Source { get; }
/// <summary>
/// Sort order with the same <see cref="Category"/>.
/// </summary>

Просмотреть файл

@ -19,6 +19,9 @@
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)App.xaml.Navigation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Content\Controls\ChipSamplePage.xaml.cs">
<DependentUpon>ChipSamplePage.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Controls\ModalDialog.xaml.cs">
<DependentUpon>ModalDialog.xaml</DependentUpon>
</Compile>
@ -67,6 +70,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Controls\SamplePageLayout.Properties.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\EnumDescriptionConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\FromBoolToValueConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\FromNullToValueConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\FromStringToValueConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Entities\Data\TestCollections.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Entities\Design.cs" />
@ -90,6 +94,10 @@
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\en\Resources.resw" />
</ItemGroup>
<ItemGroup>
<Page Include="$(MSBuildThisFileDirectory)Content\Controls\ChipSamplePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Content\Controls\DividerSamplePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -205,6 +213,11 @@
<Content Include="$(MSBuildThisFileDirectory)Assets\AppleIcon_Small.scale-200.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\AppleIcon_Small.scale-300.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\AppleIcon_Small.scale-400.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\Avatar.scale-100.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\Avatar.scale-150.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\Avatar.scale-200.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\Avatar.scale-300.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\Avatar.scale-400.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\BackButton-Dark.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\BackButton.png" />
<Content Include="$(MSBuildThisFileDirectory)Assets\CloseIcon.scale-100.png" />

Просмотреть файл

@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
#if IS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
#endif
namespace Uno.Toolkit.UI.Controls
{
[TemplatePart(Name = RemoveButtonName, Type = typeof(Button))]
public partial class Chip : ToggleButton
{
private const string RemoveButtonName = "PART_RemoveButton";
public event ChipRemovingEventHandler Removing;
public event RoutedEventHandler Removed;
/// <summary>
/// Fires when a ToggleButton is checked or unchecked, except when set with <see cref="SetIsCheckedSilently"/>.
/// </summary>
internal event RoutedEventHandler IsCheckedChanged;
#region DependencyProperty: IsCheckable = true
public static DependencyProperty IsCheckableProperty { get; } = DependencyProperty.Register(
nameof(IsCheckable),
typeof(bool),
typeof(Chip),
new PropertyMetadata(true, (s, e) => (s as Chip)?.OnIsCheckableChanged(e)));
/// <summary>
/// Gets or sets whether this should behave like a ToggleButton or a Button
/// </summary>
public bool IsCheckable
{
get => (bool)GetValue(IsCheckableProperty);
set => SetValue(IsCheckableProperty, value);
}
#endregion
#region DependencyProperty: Icon
public object Icon
{
get { return (IconElement)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(object), typeof(Chip), new PropertyMetadata(null));
#endregion
#region DependencyProperty: IconTemplate
public DataTemplate IconTemplate
{
get { return (DataTemplate)GetValue(IconTemplateProperty); }
set { SetValue(IconTemplateProperty, value); }
}
public static readonly DependencyProperty IconTemplateProperty =
DependencyProperty.Register("IconTemplate", typeof(DataTemplate), typeof(Chip), new PropertyMetadata(null));
#endregion
#region DependencyProperty: CanRemove = false
public bool CanRemove
{
get { return (bool)GetValue(CanRemoveProperty); }
set { SetValue(CanRemoveProperty, value); }
}
public static readonly DependencyProperty CanRemoveProperty =
DependencyProperty.Register("CanRemove", typeof(bool), typeof(Chip), new PropertyMetadata(false));
#endregion
#region DependencyProperty: RemovedCommand
public static DependencyProperty RemovedCommandProperty { get; } = DependencyProperty.Register(
nameof(RemovedCommand),
typeof(ICommand),
typeof(Chip),
new PropertyMetadata(default));
public ICommand RemovedCommand
{
get => (ICommand)GetValue(RemovedCommandProperty);
set => SetValue(RemovedCommandProperty, value);
}
#endregion
#region DependencyProperty: RemovedCommandParameter
public static DependencyProperty RemovedCommandParameterProperty { get; } = DependencyProperty.Register(
nameof(RemovedCommandParameter),
typeof(object),
typeof(Chip),
new PropertyMetadata(default));
public object RemovedCommandParameter
{
get => (object)GetValue(RemovedCommandParameterProperty);
set => SetValue(RemovedCommandParameterProperty, value);
}
#endregion
private bool _isMuted = false;
public Chip()
{
Checked += RaiseIsCheckedChanged;
Unchecked += RaiseIsCheckedChanged;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (GetTemplateChild(RemoveButtonName) is Button removeButton)
{
removeButton.Click += RaiseRemoveButtonClicked;
}
}
private void OnIsCheckableChanged(DependencyPropertyChangedEventArgs e)
{
if (!IsCheckable)
{
IsChecked = false;
}
}
private void RaiseIsCheckedChanged(object sender, RoutedEventArgs e)
{
if (!_isMuted)
{
IsCheckedChanged?.Invoke(sender, e);
}
}
private void RaiseRemoveButtonClicked(object sender, RoutedEventArgs e)
{
// note: sender is the RemoveButton, do not pass it as the event sender
// as ChipGroup expect the sender to be an instance of Chip
if (CanRemove)
{
var removingArgs = new ChipRemovingEventArgs();
Removing?.Invoke(this, removingArgs);
if (!removingArgs.Cancel)
{
Removed?.Invoke(this, e);
var param = RemovedCommandParameter;
if (RemovedCommand is ICommand command && command.CanExecute(param))
{
command.Execute(param);
}
}
}
}
internal void SetIsCheckedSilently(bool? value)
{
try
{
_isMuted = true;
IsChecked = value;
}
finally
{
_isMuted = false;
}
}
protected override void OnToggle()
{
if (!IsCheckable) return;
base.OnToggle();
}
}
}

Просмотреть файл

@ -0,0 +1,111 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
#if IS_WINUI
using Microsoft.UI.Xaml;
#else
using Windows.UI.Xaml;
#endif
namespace Uno.Toolkit.UI.Controls
{
public partial class ChipGroup
{
#region DependencyProperty: SelectedItem
public static DependencyProperty SelectedItemProperty { get; } = DependencyProperty.Register(
nameof(SelectedItem),
typeof(object),
typeof(ChipGroup),
new PropertyMetadata(default, (s, e) => (s as ChipGroup)?.OnSelectedItemChanged(e)));
public object SelectedItem
{
get => (object)GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
#endregion
#region DependencyProperty: SelectedItems
public static DependencyProperty SelectedItemsProperty { get; } = DependencyProperty.Register(
nameof(SelectedItems),
typeof(IList),
typeof(ChipGroup),
new PropertyMetadata(default, (s, e) => (s as ChipGroup)?.OnSelectedItemsChanged(e)));
public IList SelectedItems
{
get => (IList)GetValue(SelectedItemsProperty);
set => SetValue(SelectedItemsProperty, value);
}
#endregion
#region DependencyProperty: SelectionMemberPath
public static DependencyProperty SelectionMemberPathProperty { get; } = DependencyProperty.Register(
nameof(SelectionMemberPath),
typeof(string),
typeof(ChipGroup),
new PropertyMetadata(default, (s, e) => (s as ChipGroup)?.OnSelectionMemberPathChanged(e)));
/// <summary>
/// Gets or sets the name or path of the property that indicates selection state for each data item.
/// </summary>
public string SelectionMemberPath
{
get => (string)GetValue(SelectionMemberPathProperty);
set => SetValue(SelectionMemberPathProperty, value);
}
#endregion
#region DependencyProperty: SelectionMode = ChipSelectionMode.Single
public static DependencyProperty SelectionModeProperty { get; } = DependencyProperty.Register(
nameof(SelectionMode),
typeof(ChipSelectionMode),
typeof(ChipGroup),
new PropertyMetadata(ChipSelectionMode.Single, (s, e) => (s as ChipGroup)?.OnSelectionModeChanged(e)));
public ChipSelectionMode SelectionMode
{
get => (ChipSelectionMode)GetValue(SelectionModeProperty);
set => SetValue(SelectionModeProperty, value);
}
#endregion
#region DependencyProperty: IconTemplate
public DataTemplate IconTemplate
{
get { return (DataTemplate)GetValue(IconTemplateProperty); }
set { SetValue(IconTemplateProperty, value); }
}
public static readonly DependencyProperty IconTemplateProperty =
DependencyProperty.Register("IconTemplate", typeof(DataTemplate), typeof(ChipGroup), new PropertyMetadata(null, (s, e) => (s as ChipGroup)?.ApplyIconTemplate()));
#endregion
#region DependencyProperty: CanRemove = false
// TODO : Implement Remove Command/Event
public bool CanRemove
{
get { return (bool)GetValue(CanRemoveProperty); }
set { SetValue(CanRemoveProperty, value); }
}
public static readonly DependencyProperty CanRemoveProperty =
DependencyProperty.Register("CanRemove", typeof(bool), typeof(ChipGroup), new PropertyMetadata(false, (s, e) => (s as ChipGroup)?.ApplyCanRemoveProperty()));
#endregion
}
}

Просмотреть файл

@ -0,0 +1,436 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Uno.Extensions.Specialized;
#if IS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
#endif
namespace Uno.Toolkit.UI.Controls
{
public partial class ChipGroup : ItemsControl
{
public event ChipItemEventHandler ItemClick;
public event ChipItemEventHandler ItemChecked;
public event ChipItemEventHandler ItemUnchecked;
public event ChipItemRemovingEventHandler ItemRemoving;
public event ChipItemEventHandler ItemRemoved;
private bool _isLoaded = false;
private bool _isSynchronizingSelection = false;
private bool _isUpdatingSelection = false;
public ChipGroup()
{
RegisterPropertyChangedCallback(ItemsSourceProperty, (s, e) => (s as ChipGroup)?.OnItemsSourceChanged());
this.Loaded += OnLoaded;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
_isLoaded = true;
SynchronizeInitialSelection();
EnforceSelectionMode();
ApplyIconTemplate();
}
private void OnSelectionMemberPathChanged(DependencyPropertyChangedEventArgs e)
{
var binding = SelectionMemberPath != null
? new Binding { Path = new PropertyPath(SelectionMemberPath), Mode = BindingMode.TwoWay }
: null;
foreach (var container in GetItemContainers())
{
if (binding != null)
{
container.SetBinding(Chip.IsCheckedProperty, binding);
}
container.ClearValue(Chip.IsCheckedProperty);
}
}
private void ApplyIconTemplate()
{
if (IconTemplate != null)
{
foreach (var container in GetItemContainers())
{
container.Icon = container.Content;
container.IconTemplate = IconTemplate;
}
}
}
private void ApplyCanRemoveProperty()
{
foreach (var container in GetItemContainers())
{
container.CanRemove = CanRemove;
}
}
private void OnSelectionModeChanged(DependencyPropertyChangedEventArgs e)
{
EnforceSelectionMode();
}
private void OnItemIsCheckedChanged(object sender, RoutedEventArgs e)
{
if (_isSynchronizingSelection) return;
if (sender is Chip container)
{
UpdateSelection(new[] { container });
}
}
protected override void OnItemsChanged(object e)
{
base.OnItemsChanged(e);
SynchronizeInitialSelection();
EnforceSelectionMode();
}
protected void OnItemsSourceChanged()
{
SynchronizeInitialSelection();
EnforceSelectionMode();
}
private void OnSelectedItemChanged(DependencyPropertyChangedEventArgs e)
{
if (_isSynchronizingSelection || _isUpdatingSelection) return;
if (!IsReady && e.NewValue != null)
{
return;
}
if (SelectionMode == ChipSelectionMode.Single && FindContainer(SelectedItem) is Chip container)
{
container.SetIsCheckedSilently(true);
UpdateSelection(new[] { container });
}
else
{
UpdateSelection(null);
}
}
private void OnSelectedItemsChanged(DependencyPropertyChangedEventArgs e)
{
if (_isSynchronizingSelection || _isUpdatingSelection) return;
if (!IsReady && e.NewValue != null)
{
return;
}
if (SelectionMode == ChipSelectionMode.Multiple)
{
var selectedContainers = SelectedItems
?.Cast<object>()
.Select(x => FindContainer(x))
.Where(x => x != null)
.ToArray();
foreach (var container in selectedContainers ?? Enumerable.Empty<Chip>())
{
container.SetIsCheckedSilently(true);
}
UpdateSelection(selectedContainers, forceClearOthersSelection: true);
}
else
{
UpdateSelection(null, forceClearOthersSelection: true);
}
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is Chip;
protected override DependencyObject GetContainerForItemOverride() => new Chip();
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
if (element is Chip container)
{
if (SelectionMemberPath != null)
{
container.SetBinding(Chip.IsCheckedProperty, new Binding { Path = new PropertyPath(SelectionMemberPath), Mode = BindingMode.TwoWay });
}
if (IconTemplate != null)
{
container.Icon = container.Content;
container.IconTemplate = IconTemplate;
}
container.IsChecked = IsItemSelected(item);
container.CanRemove = CanRemove;
container.IsCheckedChanged += OnItemIsCheckedChanged;
container.Click += OnItemClick;
container.Checked += OnItemChecked;
container.Unchecked += OnItemUnchecked;
container.Removing += OnItemRemoving;
container.Removed += OnItemRemoved;
}
}
private bool IsItemSelected(object item)
{
// It's important to use Equals and not == because of boxing.
return Equals(item, SelectedItem) || (SelectedItems?.Contains(item) ?? false);
}
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
base.ClearContainerForItemOverride(element, item);
if (element is Chip container)
{
container.ClearValue(Chip.IsCheckedProperty);
container.Icon = null;
container.IconTemplate = null;
container.CanRemove = false;
container.IsCheckedChanged -= OnItemIsCheckedChanged;
container.Click -= OnItemClick;
container.Checked -= OnItemChecked;
container.Unchecked -= OnItemUnchecked;
container.Removing -= OnItemRemoving;
container.Removed -= OnItemRemoved;
}
}
private void OnItemClick(object sender, RoutedEventArgs e) => RaiseItemEvent(ItemClick, sender);
private void OnItemChecked(object sender, RoutedEventArgs e) => RaiseItemEvent(ItemChecked, sender);
private void OnItemUnchecked(object sender, RoutedEventArgs e) => RaiseItemEvent(ItemUnchecked, sender);
private void OnItemRemoving(object sender, ChipRemovingEventArgs e)
{
if (sender is Chip container)
{
var args = new ChipItemRemovingEventArgs(ItemFromContainer(container));
args.Cancel = e.Cancel;
ItemRemoving?.Invoke(this, new ChipItemRemovingEventArgs(ItemFromContainer(container)));
e.Cancel = args.Cancel;
}
}
private void OnItemRemoved(object sender, RoutedEventArgs e)
{
if (sender is Chip container)
{
// there isn't much that can be done here if the item is generated from an ItemsSource
// in such case, the removal should be handled from the source provider (view-model or code-behind)
// remove the item only if it was added via xaml or .Add(item)
if (ItemsSource == null &&
Items?.IndexOf(container) is int index && index != -1)
{
Items.RemoveAt(index);
}
RaiseItemEvent(ItemRemoved, sender);
}
}
private void RaiseItemEvent(ChipItemEventHandler handler, object originalSender)
{
if (originalSender is Chip container)
{
handler?.Invoke(this, new ChipItemEventArgs(ItemFromContainer(container)));
}
}
private void SynchronizeInitialSelection()
{
if (!IsReady)
{
return;
}
if (SelectedItem != null)
{
OnSelectedItemChanged(null);
}
if (SelectedItems != null)
{
OnSelectedItemsChanged(null);
}
}
private void EnforceSelectionMode()
{
if (!IsReady) return;
if (SelectionMode == ChipSelectionMode.None)
{
UpdateItemsIsCheckable();
UpdateSelection(default);
}
else if (SelectionMode == ChipSelectionMode.Single)
{
UpdateItemsIsCheckable();
// either one is selected or none are selected
var selectedContainers = GetItemContainers().Where(x => x.IsChecked ?? false).ToArray();
if (selectedContainers.Length > 1)
{
foreach (var container in selectedContainers)
{
container.SetIsCheckedSilently(false);
}
UpdateSelection(default);
}
else
{
UpdateSelection(selectedContainers);
}
}
else if (SelectionMode == ChipSelectionMode.Multiple)
{
UpdateItemsIsCheckable();
UpdateSelection(default);
}
void UpdateItemsIsCheckable()
{
foreach (var container in GetItemContainers())
{
container.IsCheckable = SelectionMode != ChipSelectionMode.None;
}
}
}
private void UpdateSelection(Chip[] newlySelectedContainers, bool forceClearOthersSelection = false)
{
if (!IsReady) return;
if (_isSynchronizingSelection) return;
try
{
_isSynchronizingSelection = true;
var selectedItems = new List<object>();
foreach (var container in GetItemContainers())
{
if (!container.IsChecked ?? false)
{
continue;
}
if (ShouldClearSelection())
{
container.SetIsCheckedSilently(false);
}
else
{
selectedItems.Add(ItemFromContainer(container));
}
bool ShouldClearSelection()
{
switch (SelectionMode)
{
case ChipSelectionMode.None:
// uncheck all
return true;
case ChipSelectionMode.Single:
// uncheck every other items
return newlySelectedContainers?.Contains(container) != true;
case ChipSelectionMode.Multiple:
// uncheck other items if SelectedItem or SelectedItems got updated
return forceClearOthersSelection && newlySelectedContainers?.Contains(container) != true;
default: throw new ArgumentOutOfRangeException(nameof(SelectionMode));
}
}
}
// update selection properties
SelectedItem = SelectionMode == ChipSelectionMode.Single && selectedItems?.Count == 1
? selectedItems[0]
: null;
SelectedItems = SelectionMode == ChipSelectionMode.Multiple && selectedItems?.Count >= 1
? selectedItems
: null;
}
finally
{
_isSynchronizingSelection = false;
}
}
private bool IsReady => _isLoaded && HasItems && HasContainers;
private bool HasItems => GetItems().Any();
private bool HasContainers => GetItemContainers().Any();
/// <summary>
/// Get the items.
/// </summary>
/// <remarks>The item itself maybe its own container, as in the case of <see cref="Chip"> added as child to <see cref="ItemsControl.Items"/>.</remarks>
private IEnumerable GetItems() =>
ItemsSource as IEnumerable ??
(ItemsSource as CollectionViewSource)?.View ??
Items ??
Enumerable.Empty<object>();
private Chip FindContainer(object item)
{
if (item == null) return null;
// For some obscure reason, ContainerFromItem returns null when item is an enum.
// Note however that it works fine for other value types such as int.
// Because of this, we retrieve the container using the index instead.
if (item is Enum)
{
var index = GetItems().IndexOf(item);
if (index != -1)
{
return ContainerFromIndex(index) as Chip;
}
}
return
item as Chip ??
ContainerFromItem(item) as Chip;
}
/// <summary>
/// Get the item containers.
/// </summary>
/// <remarks>An empty enumerable will returned if the <see cref="ItemsControl.ItemsPanelRoot"/> and the containers have not been materialized.</remarks>
private IEnumerable<Chip> GetItemContainers() =>
ItemsPanelRoot?.Children.OfType<Chip>() ??
Enumerable.Empty<Chip>();
}
}

Просмотреть файл

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Uno.Toolkit.UI.Controls
{
public enum ChipSelectionMode
{
None, Single, Multiple,
}
}

Просмотреть файл

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Uno.Toolkit.UI.Controls
{
#region Chip event handlers
public delegate void ChipRemovingEventHandler(object sender, ChipRemovingEventArgs e);
public sealed class ChipRemovingEventArgs : EventArgs
{
public bool Cancel { get; set; }
}
public delegate void ChipRemovedEventHandler(object sender, EventArgs e);
#endregion
#region ChipGroup event handlers
public delegate void ChipItemEventHandler(object sender, ChipItemEventArgs e);
public class ChipItemEventArgs : EventArgs
{
internal ChipItemEventArgs(object item) => Item = item;
public object Item { get; }
}
public delegate void ChipItemRemovingEventHandler(object sender, ChipItemEventArgs e);
public class ChipItemRemovingEventArgs : ChipItemEventArgs
{
public ChipItemRemovingEventArgs(object item) : base(item) { }
public bool Cancel { get; set; }
}
#endregion
}

Просмотреть файл

@ -31,6 +31,8 @@ namespace Uno.Toolkit.UI.Material
this.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"ms-appx:///{PackageName}/Styles/Controls/TopTabBar.Mobile.xaml") });
#else
this.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"ms-appx:///{PackageName}/Styles/Controls/BottomTabBar.xaml") });
this.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"ms-appx:///{PackageName}/Styles/Controls/Chip.xaml") });
this.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"ms-appx:///{PackageName}/Styles/Controls/ChipGroup.xaml") });
this.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"ms-appx:///{PackageName}/Styles/Controls/TopTabBar.xaml") });
#endif
this.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri($"ms-appx:///{PackageName}/Styles/Controls/Divider.xaml") });

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,142 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkitLib="using:Uno.Toolkit.UI.Controls"
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:android="http://uno.ui/android"
xmlns:ios="http://uno.ui/ios"
xmlns:wasm="http://uno.ui/wasm"
xmlns:macos="http://uno.ui/macos"
xmlns:toolkit="using:Uno.UI.Toolkit"
mc:Ignorable="android ios wasm macos">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Chip.xaml" />
</ResourceDictionary.MergedDictionaries>
<ItemsPanelTemplate x:Key="MaterialHorizontalChipGroupItemsPanel">
<StackPanel Orientation="Horizontal"
Spacing="8" />
</ItemsPanelTemplate>
<Style x:Key="BaseMaterialChipGroupStyle"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemsPanel"
Value="{StaticResource MaterialHorizontalChipGroupItemsPanel}" />
</Style>
<!-- Begin Input Chip Group Style -->
<Style x:Key="MaterialFilledInputChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialFilledInputChipStyle}" />
<Setter Property="CanRemove"
Value="True" />
<Setter Property="SelectionMode"
Value="Multiple" />
</Style>
<Style x:Key="MaterialOutlinedInputChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialOutlinedInputChipStyle}" />
<Setter Property="CanRemove"
Value="True" />
<Setter Property="SelectionMode"
Value="Multiple" />
</Style>
<!-- End Input Chip Group Style -->
<!-- Begin Choice Chip Group Style -->
<Style x:Key="MaterialFilledChoiceChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialFilledChoiceChipStyle}" />
<Setter Property="CanRemove"
Value="False" />
<Setter Property="SelectionMode"
Value="Single" />
</Style>
<Style x:Key="MaterialOutlinedChoiceChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialOutlinedChoiceChipStyle}" />
<Setter Property="CanRemove"
Value="False" />
<Setter Property="SelectionMode"
Value="Single" />
</Style>
<!-- End Choice Chip Group Style -->
<!-- Begin Filter Chip Group Style -->
<Style x:Key="MaterialFilledFilterChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialFilledFilterChipStyle}" />
<Setter Property="CanRemove"
Value="False" />
<Setter Property="SelectionMode"
Value="Multiple" />
</Style>
<Style x:Key="MaterialOutlinedFilterChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialOutlinedFilterChipStyle}" />
<Setter Property="CanRemove"
Value="False" />
<Setter Property="SelectionMode"
Value="Multiple" />
</Style>
<!-- End Filter Chip Group Style -->
<!-- Begin Action Chip Group Style -->
<Style x:Key="MaterialFilledActionChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialFilledActionChipStyle}" />
<Setter Property="CanRemove"
Value="False" />
<Setter Property="SelectionMode"
Value="None" />
</Style>
<Style x:Key="MaterialOutlinedActionChipGroupStyle"
BasedOn="{StaticResource BaseMaterialChipGroupStyle}"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialOutlinedActionChipStyle}" />
<Setter Property="CanRemove"
Value="False" />
<Setter Property="SelectionMode"
Value="None" />
</Style>
<!-- End Action Chip Group Style -->
<!--
ItemsWrapGrid is not working inside an ItemsControl.
Issue (WASM) https://github.com/unoplatform/uno/issues/468
(All platforms) https://github.com/unoplatform/uno/issues/4023
-->
<!--<ItemsPanelTemplate x:Key="MaterialWrapChipGroupItemsPanel">
<ItemsWrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
<Style x:Key="MaterialWrapChipGroupStyle"
TargetType="toolkitLib:ChipGroup">
<Setter Property="ItemsPanel"
Value="{StaticResource MaterialWrapChipGroupItemsPanel}" />
<Setter Property="ItemContainerStyle"
Value="{StaticResource MaterialChipStyle}" />
</Style>-->
</ResourceDictionary>