Merge branch 'main' into winui

# Conflicts:
#	CommunityToolkit.WinUI.SampleApp/SamplePages/KeyDownTriggerBehavior/KeyDownTriggerBehaviorXaml.bind
#	CommunityToolkit.WinUI.SampleApp/SamplePages/samples.json
#	CommunityToolkit.WinUI.UI.Animations/CommunityToolkit.WinUI.UI.Animations.csproj
#	CommunityToolkit.WinUI.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs
#	CommunityToolkit.WinUI.UI.Animations/Properties/SkipLocalsInitAttribute.cs
#	CommunityToolkit.WinUI.UI.Behaviors/Keyboard/KeyDownTriggerBehavior.cs
#	CommunityToolkit.WinUI.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs
#	CommunityToolkit.WinUI.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs
#	CommunityToolkit.WinUI.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs
#	CommunityToolkit.WinUI.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.cs
#	Microsoft.Toolkit.Uwp.DeveloperTools/VisualStudioToolsManifest.xml
#	Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj
#	Microsoft.Toolkit.Uwp.UI.Controls.Core/VisualStudioToolsManifest.xml
#	Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/VisualStudioToolsManifest.xml
#	Microsoft.Toolkit.Uwp.UI.Controls.Input/VisualStudioToolsManifest.xml
#	Microsoft.Toolkit.Uwp.UI.Controls.Layout/VisualStudioToolsManifest.xml
#	Microsoft.Toolkit.Uwp.UI.Controls.Markdown/VisualStudioToolsManifest.xml
#	Microsoft.Toolkit.Uwp.UI.Controls.Media/VisualStudioToolsManifest.xml
#	Microsoft.Toolkit.Uwp.UI.Controls.Primitives/VisualStudioToolsManifest.xml
#	UnitTests/UnitTests.NetCore/UnitTests.NetCore.csproj
#	Windows Community Toolkit.sln
This commit is contained in:
Alexandre Zollinger Chohfi 2021-08-18 11:21:10 -07:00
Родитель 4e4e628d7b 5171b99c95
Коммит 9ee95a387b
51 изменённых файлов: 1625 добавлений и 206 удалений

41
.github/ISSUE_TEMPLATE/bug_report.md поставляемый
Просмотреть файл

@ -4,23 +4,26 @@ about: Create a report to help us fix something that isn't working as expected
title: ''
labels: "bug :bug:"
assignees: ''
---
<!-- 🚨 PLEASE DO NOT SKIP ANY INSTRUCTIONS AND INFORMATION MENTIONED BELOW AS THEY ARE ALL REQUIRED AND ESSENTIAL TO INVESTIGATE THE ISSUE. ISSUES WITH MISSING INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION
...
IF NOT CERTAIN ABOUT THE ISSUE AND REQUIRE MORE CLARITY THEN PLEASE POST ON "QUESTIONS & HELP" CATEGORY OF THE DISCUSSIONS PLATFORM [https://github.com/CommunityToolkit/WindowsCommunityToolkit/discussions/categories/questions-help] WHERE YOU CAN DISCUSS AND ENAGAGE WITH THE COMMUNITY TO GAIN FURTHER CLAIRITY REGARDING THE ISSUE 🚨 -->
IF NOT CERTAIN ABOUT THE ISSUE AND REQUIRE MORE CLARITY THEN PLEASE POST ON "QUESTIONS & HELP" CATEGORY OF THE DISCUSSIONS PLATFORM [https://github.com/CommunityToolkit/WindowsCommunityToolkit/discussions/categories/questions-help] WHERE YOU CAN DISCUSS AND ENGAGE WITH THE COMMUNITY TO GAIN FURTHER CLARITY REGARDING THE ISSUE 🚨 -->
## Describe the bug
A clear and concise description of what the bug is.
- [ ] Is this bug a regression in the toolkit? If so, what toolkit version did you last see it work:
## Steps to Reproduce
- [ ] Can this be reproduced in the Sample App? (Either in a sample as-is or with new XAML pasted in the editor.) If so, please provide custom XAML or steps to reproduce. If not, let us know why it can't be reproduced (e.g. more complex setup, environment, dependencies, etc...) <!-- Being able to reproduce the problem in the sample app, really stream-lines the whole process in being able to discover, resolve, and validate bug fixes. -->
- [ ] Can this be reproduced in the Sample App? (Either in a sample as-is or with new XAML pasted in the editor.) If so, please provide custom XAML or steps to reproduce. If not, let us know why it can't be reproduced (e.g. more complex setup, environment, dependencies, etc...)
<!-- Being able to reproduce the problem in the sample app, really stream-lines the whole process in being able to discover, resolve, and validate bug fixes. -->
Steps to reproduce the behavior:
1. Given the following environment (Sample App w/ XAML, Project with Isolated setup, etc...)
2. Go to '...'
3. Click on '....'
@ -30,46 +33,52 @@ Steps to reproduce the behavior:
<!-- Provide as many code-snippets or XAML snippets where appropriate. -->
## Expected behavior
A clear and concise description of what you expected to happen.
<!-- A clear and concise description of what you expected to happen. -->
## Screenshots
If applicable, add screenshots to help explain your problem.
<!-- If applicable, add screenshots to help explain your problem. -->
## Environment
<!-- Check one or more of the following options with "x" -->
```
NuGet Package(s):
<!-- Check one or more of the following options with "x" and replace the {build_number} and {minor_version} with the actual values -->
NuGet Package(s): <!-- Which NuGet Packages have you used -->
Package Version(s):
Windows 10 Build Number:
- [ ] Fall Creators Update (16299)
- [ ] April 2018 Update (17134)
- [ ] October 2018 Update (17763)
- [ ] May 2019 Update (18362)
- [ ] May 2020 Update (19041)
- [ ] Insider Build (build number: )
- [ ] Insider Build ({build_number})
App min and target version:
- [ ] Fall Creators Update (16299)
- [ ] April 2018 Update (17134)
- [ ] October 2018 Update (17763)
- [ ] May 2019 Update (18362)
- [ ] May 2020 Update (19041)
- [ ] Insider Build (xxxxx)
- [ ] Insider Build ({build_number})
Device form factor:
- [ ] Desktop
- [ ] Xbox
- [ ] Surface Hub
- [ ] IoT
Visual Studio
- [ ] 2017 (version: )
- [ ] 2019 (version: )
- [ ] 2019 Preview (version: )
Visual Studio version:
```
- [ ] 2017 (15.{minor_version})
- [ ] 2019 (16.{minor_version})
- [ ] 2022 (17.{minor_version})
## Additional context
Add any other context about the problem here.
<!-- Add any other context about the problem here. -->

1
.github/ISSUE_TEMPLATE/documentation.md поставляемый
Просмотреть файл

@ -4,7 +4,6 @@ about: I have a documentation suggestion or question
title: "[Docs]"
labels: documentation
assignees: ''
---
<!--

10
.github/ISSUE_TEMPLATE/feature_request.md поставляемый
Просмотреть файл

@ -4,27 +4,27 @@ about: I have a new idea or improvement for the toolkit
title: "[Feature]"
labels: "feature request :mailbox_with_mail:"
assignees: ''
---
<!-- 🚨 PLEASE PROVIDE DETAILED INFORMATION AND DO NOT SKIP ANY INSTRUCTIONS AND INFORMATION MENTIONED BELOW AS THEY ARE ALL REQUIRED AND ESSENTIAL TO HELP US UNDERSTAND THE FEATURE.
...
IF NOT CERTAIN ABOUT THE FEATURE AND REQUIRE MORE CLARITY THEN PLEASE POST ON "IDEAS" CATEGORY OF THE DISCUSSIONS PLATFORM [https://github.com/CommunityToolkit/WindowsCommunityToolkit/discussions/categories/ideas] WHERE YOU CAN DISCUSS AND ENAGAGE WITH THE COMMUNITY TO GAIN FURTHER CLAIRITY REGARDING THE FEATURE 🚨 -->
IF NOT CERTAIN ABOUT THE FEATURE AND REQUIRE MORE CLARITY THEN PLEASE POST ON "IDEAS" CATEGORY OF THE DISCUSSIONS PLATFORM [https://github.com/CommunityToolkit/WindowsCommunityToolkit/discussions/categories/ideas] WHERE YOU CAN DISCUSS AND ENGAGE WITH THE COMMUNITY TO GAIN FURTHER CLARITY REGARDING THE FEATURE 🚨 -->
## Describe the problem this feature would solve
<!-- Please describe or link to any existing issues or discussions.
A clear and concise description of what the problem is, starting with the user story.
Provide examples of the restrictions in the current environment that hinders the work your users or you want to perform. What are the ways this new feature will help transform and deliver those results?
For example, I am currently using the InfiniteCanvas control which lacks the TabbedCommandBar control feature. I am looking to improve user experience therefore i would like to use that in my project to provide ease of accessibility and a user-friendly interface. This new feature will provide quick access to the toolbar, enhance space utilization, etc [...] -->
## Describe the solution
<!-- A clear and concise description of what you want to happen. Define how do you think it will help the community and adds value to the toolkit? -->
## Describe alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
## Additional context & Screenshots
<!-- Add any other context or screenshots about the feature request here.-->

1
.github/ISSUE_TEMPLATE/question.md поставляемый
Просмотреть файл

@ -4,7 +4,6 @@ about: I have a question about how to use something in the toolkit.
title: "[Question]"
labels: "question :grey_question:"
assignees: ''
---
<!--

1
.github/ISSUE_TEMPLATE/win32_controls.md поставляемый
Просмотреть файл

@ -4,7 +4,6 @@ about: I have an issue with a Toolkit WPF or WinForms control
title: "[Win32]"
labels:
assignees: ''
---
<!--

28
.github/PULL_REQUEST_TEMPLATE.md поставляемый
Просмотреть файл

@ -1,17 +1,19 @@
<!-- 🚨 Please Do Not skip any instructions and information mentioned below as they are all required and essential to evaluate and test the PR. By fulfilling all the required information you will be able to reduce the volume of questions and most likely help merge the PR faster 🚨 -->
<!-- 👉 It is imperative to resolve ONE ISSUE PER PR and avoid making multiple changes unless the changes interrelate with each other -->
<!-- 👉 It is imperative to resolve ONE ISSUE PER PR and avoid making multiple changes unless the changes interrelate with each other -->
<!-- 📝 Please always keep the "☑️ Allow edits by maintainers" button checked in the Pull Request Template as it increases collaboration with the Toolkit maintainers by permitting commits to your PR branch (only) created from your fork. This can let us quickly make fixes for minor typos or forgotten StyleCop issues during review without needing to wait on you doing extra work. Let us help you help us! 🎉 -->
## Fixes
## Fixes #
<!-- Add the relevant issue number after the "#" mentioned above (for ex: "## Fixes #1234") which will automatically close the issue once the PR is merged. -->
<!-- Add the relevant issue number after the word "Fixes" mentioned above (for ex: "## Fixes #1234") which will automatically close the issue once the PR is merged. -->
<!-- Add a brief overview here of the feature/bug & fix. -->
## PR Type
What kind of change does this PR introduce?
<!-- Please uncomment one or more options below that apply to this PR. -->
<!-- - Bugfix -->
@ -23,30 +25,30 @@ What kind of change does this PR introduce?
<!-- - Sample app changes -->
<!-- - Other... Please describe: -->
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
## What is the new behavior?
<!-- Describe how was this issue resolved or changed? -->
<!-- Describe how was this issue resolved or changed? -->
## PR Checklist
Please check if your PR fulfills the following requirements:
Please check if your PR fulfills the following requirements: <!-- and remove the ones that are not applicable to the current PR -->
- [ ] Tested code with current [supported SDKs](../readme.md#supported)
- [ ] Pull Request has been submitted to the documentation repository [instructions](..\contributing.md#docs). Link: <!-- docs PR link -->
- [ ] Tested code with current [supported SDKs](../#supported)
- [ ] Pull Request has been submitted to the documentation repository [instructions](../blob/main/Contributing.md#docs). Link: <!-- docs PR link -->
- [ ] Sample in sample app has been added / updated (for bug fixes / features)
- [ ] Icon has been created (if new sample) following the [Thumbnail Style Guide and templates](https://github.com/CommunityToolkit/WindowsCommunityToolkit-design-assets)
- [ ] Icon has been created (if new sample) following the [Thumbnail Style Guide and templates](https://github.com/CommunityToolkit/WindowsCommunityToolkit-design-assets)
- [ ] New major technical changes in the toolkit have or will be added to the [Wiki](https://github.com/CommunityToolkit/WindowsCommunityToolkit/wiki) e.g. build changes, source generators, testing infrastructure, sample creation changes, etc...
- [ ] Tests for the changes have been added (for bug fixes / features) (if applicable)
- [ ] Header has been added to all new source files (run *build/UpdateHeaders.bat*)
- [ ] Header has been added to all new source files (run _build/UpdateHeaders.bat_)
- [ ] Contains **NO** breaking changes
<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below.
Please note that breaking changes are likely to be rejected within minor release cycles or held until major versions. -->
Please note that breaking changes are likely to be rejected within minor release cycles or held until major versions. -->
## Other information
<!-- Please add any other information that might be helpful to reviewers. -->

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

@ -68,9 +68,9 @@ members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
<https://www.contributor-covenant.org/faq>

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

@ -9,7 +9,7 @@ using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.Toolkit.Mvvm.SourceGenerators
namespace CommunityToolkit.Mvvm.SourceGenerators
{
/// <summary>
/// A source generator for necessary nullability attributes.
@ -34,14 +34,18 @@ namespace Microsoft.Toolkit.Mvvm.SourceGenerators
/// </summary>
private void AddSourceCodeIfTypeIsNotPresent(GeneratorExecutionContext context, string typeFullName)
{
if (context.Compilation.GetTypeByMetadataName(typeFullName) is not null)
// Check that the target attributes are not available in the consuming project. To ensure that
// this works fine both in .NET (Core) and .NET Standard implementations, we also need to check
// that the target types are declared as public (we assume that in this case those types are from the BCL).
// This avoids issues on .NET Standard with Roslyn also seeing internal types from referenced assemblies.
if (context.Compilation.GetTypeByMetadataName(typeFullName) is { DeclaredAccessibility: Accessibility.Public })
{
return;
}
string
typeName = typeFullName.Split('.').Last(),
filename = $"Microsoft.Toolkit.Mvvm.SourceGenerators.EmbeddedResources.{typeName}.cs";
filename = $"CommunityToolkit.Mvvm.SourceGenerators.EmbeddedResources.{typeName}.cs";
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename);
StreamReader reader = new(stream);

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

@ -1,3 +1,3 @@
# Windows Community Toolkit - WPF and Windows Forms
# Windows Community Toolkit WPF and Windows Forms
The source has moved to a new repository: https://github.com/CommunityToolkit/Microsoft.Toolkit.Win32
The source has moved to a new repository: <https://github.com/CommunityToolkit/Microsoft.Toolkit.Win32>

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

@ -135,9 +135,9 @@ namespace CommunityToolkit.WinUI.Connectivity
public byte? SignalStrength { get; private set; }
/// <summary>
/// Gets signal strength for the current Internet Connection Profile.
/// Gets the network names associated with the current Internet Connection Profile.
/// </summary>
/// <returns>value of <see cref="NetworkConnectivityLevel"/></returns>
/// <returns>value of <see cref="IReadOnlyList{String}"/></returns>
public IReadOnlyList<string> NetworkNames
{
get
@ -146,4 +146,4 @@ namespace CommunityToolkit.WinUI.Connectivity
}
}
}
}
}

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

@ -1,6 +1,8 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.DeveloperTools.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.DeveloperTools.AlignmentGrid" />
<Item Type="CommunityToolkit.WinUI.DeveloperTools.FocusTracker" />
</ToolboxItems>

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

@ -0,0 +1,59 @@
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ani="using:CommunityToolkit.WinUI.UI.Animations"
xmlns:behaviors="using:CommunityToolkit.WinUI.UI.Behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel
VerticalAlignment="Center">
<TextBox
PlaceholderText="Set the focus to this TextBox and press enter to trigger the animation"
Width="500">
<interactivity:Interaction.Behaviors>
<behaviors:KeyDownTriggerBehavior
Key="Enter">
<behaviors:StartAnimationAction Animation="{Binding ElementName=MoveAnimation}" />
</behaviors:KeyDownTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<Button Background="Gray" Margin="0,40,0,0" Width="200" Height="200" HorizontalAlignment="Center" VerticalAlignment="Center">
<ani:Explicit.Animations>
<ani:AnimationSet x:Name="MoveAnimation" IsSequential="True">
<ani:TranslationAnimation Duration="0:0:3" To="0,32,0" From="0,0,0" />
<ani:StartAnimationActivity Delay="0:0:3" Animation="{Binding ElementName=FadeOutAnimation}"/>
<ani:StartAnimationActivity Delay="0:0:3" Animation="{Binding ElementName=FadeInAnimation}"/>
<ani:TranslationAnimation Duration="0:0:1" To="0,0,0" From="0,32,0" />
</ani:AnimationSet>
</ani:Explicit.Animations>
<Image Source="ms-appx:///Assets/ToolkitLogo.png" Height="100" Width="100">
<ani:Explicit.Animations>
<ani:AnimationSet x:Name="FadeOutAnimation">
<ani:OpacityAnimation From="1"
To="0"
Duration="0:0:1"
Delay="0"
EasingType="Linear"
EasingMode="EaseOut"/>
</ani:AnimationSet>
<ani:AnimationSet x:Name="FadeInAnimation">
<ani:OpacityAnimation From="0"
To="1"
Duration="0:0:1"
Delay="0"
EasingType="Linear"
EasingMode="EaseOut"/>
</ani:AnimationSet>
</ani:Explicit.Animations>
</Image>
</Button>
</StackPanel>
</Page>

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

@ -376,11 +376,11 @@ To add an image, it is almost like a link. You just need to add a \! before.
So inline image syntax looks like this:
>\!\[Helpers Image](https\://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/Microsoft.Toolkit.Uwp.SampleApp/Assets/Helpers.png)
>\!\[Helpers Image](https\://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/CommunityToolkit.WinUI.SampleApp/Assets/Helpers.png)
which renders in:
![Helpers Image](https://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/Microsoft.Toolkit.Uwp.SampleApp/Assets/Helpers.png)
![Helpers Image](https://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/CommunityToolkit.WinUI.SampleApp/Assets/Helpers.png)
Rendering Images is now supported through prefix. use property **UriPrefix**

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

@ -30,8 +30,11 @@
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock FontSize="32" Text="Select Actions"
Margin="0,0,0,4"/>
<TextBlock FontSize="32" Margin="0,0,0,4">
<Run Text="Select up to" />
<Run Text="{Binding MaximumTokens, ElementName=TokenBox, Mode=OneWay}" />
<Run Text="actions" />
</TextBlock>
<controls:TokenizingTextBox
x:Name="TokenBox"
PlaceholderText="Add Actions"
@ -39,7 +42,8 @@
MaxHeight="104"
HorizontalAlignment="Stretch"
TextMemberPath="Text"
TokenDelimiter=",">
TokenDelimiter=","
MaximumTokens="3">
<controls:TokenizingTextBox.SuggestedItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">

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

@ -32,6 +32,7 @@
<triggers:UserHandPreferenceStateTrigger x:Key="UserHandPreferenceStateTrigger" />
<triggers:UserInteractionModeStateTrigger x:Key="UserInteractionModeStateTrigger" />
<behaviors:StartAnimationAction x:Key="StartAnimationAction" />
<behaviors:KeyDownTriggerBehavior x:Key="KeyDownTriggerBehavior" />
<behaviors:AutoSelectBehavior x:Key="AutoSelectBehavior" />
<controls:ColorPicker x:Key="ColorPicker" />
<controls:ColorPickerButton x:Key="ColorPickerButton" />

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

@ -479,7 +479,7 @@
"Name": "ConstrainedBox",
"Subcategory": "Layout",
"About": "The ConstrainedBox is a FrameworkElement which can allow a developer to constrain the scale, multiple of, or aspect ratio of its content.",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/ConstrainedBox",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.WinUI.UI.Controls.Primitives/ConstrainedBox",
"XamlCodeFile": "/SamplePages/Primitives/ConstrainedBox.bind",
"Icon": "/SamplePages/Primitives/ConstrainedBox.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/ConstrainedBox.md"
@ -784,7 +784,7 @@
"Type": "ThemeListenerPage",
"Subcategory": "Systems",
"About": "The ThemeListener allows you to keep track of changes to the System Theme.",
"CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/CommunityToolkit.WinUI.UI/Helpers/ThemeListener.cs",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/CommunityToolkit.WinUI.UI/Helpers/ThemeListener.cs",
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/ThemeListener.md"
},
@ -838,12 +838,21 @@
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/AutoSelectBehavior.md"
},
{
"Name": "KeyDownTriggerBehavior",
"Subcategory": "Systems",
"About": "Behavior to listen to a key press on a control and executes actions",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/CommunityToolkit.WinUI.UI.Behaviors/Keyboard/KeyDownTriggerBehavior.cs",
"XamlCodeFile": "/SamplePages/KeyDownTriggerBehavior/KeyDownTriggerBehaviorXaml.bind",
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/KeyDownTriggerBehavior.md"
},
{
"Name": "Win2d Path Mini Language Parser",
"Type": "CanvasPathGeometryPage",
"Subcategory": "Parser",
"About": "CanvasPathGeometry class allows you to convert Win2d Path Mini Language string to CanvasGeometry, Brushes, CanvasStrokes or CanvasStrokeStyles.",
"CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.WinUI.UI.Media/Geometry",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.WinUI.UI.Media/Geometry",
"Icon": "/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/CanvasPathGeometry.md"
},
@ -881,7 +890,7 @@
"Name": "Guard APIs",
"Subcategory": "Developer",
"About": "The Guard APIs can be used to validate method arguments in a streamlined manner, which is also faster, less verbose, more expressive and less error prone than manually writing checks and throwing exceptions.",
"CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.Diagnostics",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.Diagnostics",
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/developer-tools/Guard.md"
},
@ -889,7 +898,7 @@
"Name": "High Performance APIs",
"Subcategory": "Developer",
"About": "The High Performance package contains a set of APIs that are heavily focused on optimization. All the new APIs have been carefully crafted to achieve the best possible performance when using them, either through reduced memory allocation, micro-optimizations at the assembly level, or by structuring the APIs in a way that facilitates writing performance oriented code in general.",
"CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.HighPerformance",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.HighPerformance",
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/high-performance/Introduction.md"
},

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

@ -1,100 +1,110 @@
# Sample Application
For the latest info, [visit the wiki article here](https://github.com/CommunityToolkit/WindowsCommunityToolkit/wiki/Sample-Development).
# How to add new samples
## How to add new samples
This document describes how to add a new sample page for a new control you want to add to the toolkit.
*DropShadowPanel*, *ImageEx*, and *ImageCache* are good examples of most of the features mentioned below.
*`DropShadowPanel`*, *`ImageEx`*, and *`ImageCache`* are good examples of most of the features mentioned below.
### 1. Add Sample page and `.bind` template
## 1. Add Sample page and .bind template
First you need to create a Xaml page in the folder /SamplePages/YourControl. This will be the logical page used to by the app to navigate to the sample and contains code.
First you need to create a XAML page in the folder `/SamplePages/YourControl`. This will be the logical page used to by the app to navigate to the sample and contains code.
If providing 'live' XAML, a .bind file is loaded and dynamically fed to the XamlReader.Load method to convert into actual controls. This changes a few things about how samples need to be written (detailed below), but allows developers to actually change the sample and see the results live.
If providing 'live' XAML, a `.bind` file is loaded and dynamically fed to the `XamlReader.Load` method to convert into actual controls. This changes a few things about how samples need to be written (detailed below), but allows developers to actually change the sample and see the results live.
This not only gives us a killer sample app, but it also means that all our samples are also self-validating. There can't be a typo in the sample text given in the sample app anymore, as otherwise the sample won't work and should be caught during testing of said sample.
### 2. Binding text
## 2. Binding text
The .bind files are templates which use @[Property Name:Type:DefaultValue:Options] syntax to allow for customized options to be presented to the user in the sample app. The user can play with the values in the property page and see results change instantly. This is accomplished by using {Binding} syntax when on the property page, but switches to the raw value when the developer goes to the XAML page.
The `.bind` files are templates which use `@[Property Name:Type:DefaultValue:Options]` syntax to allow for customized options to be presented to the user in the sample app. The user can play with the values in the property page and see results change instantly. This is accomplished by using {Binding} syntax when on the property page, but switches to the raw value when the developer goes to the XAML page.
This makes it easy for a developer to test out values for a control and then copy the XAML needed for that exact result into their app.
In order to provide a property UI and associated code, you have to define a the .bind XAML file associated with your page.
In order to provide a property UI and associated code, you have to define a `.bind` XAML file associated with your page.
Here is an example:
```xaml
```xml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1"
Text="@[Text:String:Hey!]" Foreground="Black"
FontSize="@[FontSize:Slider:12:10-30]"
VerticalAlignment="@[Vertical Alignment:Enum:VerticalAlignment.Center]">
</TextBlock>
Foreground="Black"
Text="@[Text:String:Hey!]"
FontSize="@[FontSize:Slider:12:10-30]"
VerticalAlignment="@[Vertical Alignment:Enum:VerticalAlignment.Center]">
</TextBlock>
</Grid>
```
You can define "interactive" values in this file. The value types can be:
* String: You want the user to provide a text. The string is built like this @[Name:**String**:Default value]
* Slider: You want the user to provide a double value. The string is built like this @[Name:**Slider**:Default value:min-max]
* DoubleSlider: Same as slider but with double values (0.01 precision)
* TimeSpan: You want the user to provide a duration. The string is built like this (all values in milliseconds) @[Name:**TimeSpan**:DefaultValue:min-max]
* Enum: You want the user to provide a enum value. The string is built like this @[Name:**Enum**:EnumType.DefaultValue]
* Brush: You want the user to select a color from a list. The string is built like this @[Name:**Brush**:Black]
* Bool: You want the user to enable or disable a property. The string is built like this @[Name:**Bool**:True]
* Thickness: You want the user to provide a Thickness. The string is built like this @[Name:**Thickness**:0,20,10,0]
You can define “interactive” values in this file. The value types can be:
* `String`: You want the user to provide a text. An equivalent syntax is `@[Name:String:Default Value]`
* `Slider`: You want the user to provide a double value. An equivalent syntax is `@[Name:Slider:DefaultValue:Min-Max]`
* `DoubleSlider`: Same as slider but with double values (0.01 precision)
* `TimeSpan`: You want the user to provide a duration. An equivalent syntax is (all values in milliseconds) `@[Name:TimeSpan:DefaultValue:Min-Max]`
* `Enum`: You want the user to provide an enum value. An equivalent syntax is `@[Name:Enum:EnumType.DefaultValue]`
* `Brush`: You want the user to select a color from a list. An equivalent syntax is `@[Name:Brush:Black]`
* `Bool`: You want the user to enable or disable a property. An equivalent syntax is `@[Name:Bool:True]`
* `Thickness`: You want the user to provide a Thickness. An equivalent syntax is `@[Name:Thickness:0,20,10,0]`
The `Property Name` can also contain spaces, but these will be removed from the property name used for accessing the value in the property bag for any binding/access, see below.
The name and options will be translated **automatically** to the following syntax when your .bind template is being used on the property page:
The name and options will be translated **automatically** to the following syntax when your `.bind` template is being used on the property page:
```xaml
```xml
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="{Binding Text.Value, Mode=OneWay}" Foreground="Black"
FontSize="{Binding FontSize.Value, Mode=OneWay}"
VerticalAlignment="{Binding VerticalAlignment.Value, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="1" Foreground="Black"
Text="{Binding Text.Value, Mode=OneWay}"
FontSize="{Binding FontSize.Value, Mode=OneWay}"
VerticalAlignment="{Binding VerticalAlignment.Value, Mode=OneWay}">
</TextBlock>
</Grid>
```
When the developer switches to the XAML tab, they'll automatically see the selected values instead:
```xaml
```xml
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="User Entered Text" Foreground="Black"
FontSize="12"
VerticalAlignment="Left"></TextBlock>
<TextBlock Grid.Column="1"
Foreground="Black"
Text="User Entered Text"
FontSize="12"
VerticalAlignment="Left">
</TextBlock>
</Grid>
```
You can also reuse a `@[Property Name]` reference by itself again later to use the same binding/value again in the same template. This will automatically get mapped to the right place without the need to specify all the types/options again. Just set those options on your first usage.
If you happen to need a two-way binding for the generated XAML, then add an extra '@' after the property definition in the template:
If you happen to need a two-way binding for the generated XAML, then add an extra '**@**' after the property definition in the template:
```xaml
Value="@[Value:Slider:0:0-180]@"
```xml
<Slider Value="@[Value:Slider:0:0-180]@" />
```
## 3. Have a *'Shallow Copy'* of your example in the sample page
Even though the sample page content is ignored and the dynamic template injected, for the XamlReader to access some classes, a reference to the item is sometimes needed in the hosting app for it to be accessible. (I assume it's an optimization thing.)
### 3. Have a '*Shallow Copy*' of your example in the sample page
Therefore, for any new control/extension, you should still have a simplified snippet of it contained in the sample page compiled/loaded by the app. You should remove names, events, and properties (unless extensions) from these so the namespace isn't accidentally polluted. If you re-use the same control, you don't have to include it twice.
Even though the sample page content is ignored and the dynamic template injected, for the `XamlReader` to access some classes, a reference to the item is sometimes needed in the hosting app for it to be accessible. (I assume it's an optimization thing.)
Therefore, for any new control/extension, you should still have a simplified snippet of it contained in the sample page compiled/loaded by the app. You should remove names, events, and properties (unless extensions) from these, so the namespace isn't accidentally polluted. If you re-use the same control, you don't have to include it twice.
## 4. For Events/Resource Templates: Have your sample page implement the **IXamlRendererListener** interface
This gets called whenever the template gets parsed (due to loading or user modification). Here you can use the [LogicalTree](https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs) extensions to grab named controls in the template and register their events. **Check for null first** as the developer may have removed the name from the element.
### 4. For Events/Resource Templates: Have your sample page implement the **`IXamlRendererListener`** interface
```csharp
This gets called whenever the template gets parsed (due to loading or user modification). Here you can use the [`LogicalTree`](https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/CommunityToolkit.WinUI.UI/Extensions/FrameworkElement/FrameworkElementExtensions.LogicalTree.cs) extensions to grab named controls in the template and register their events. **Check for null first** as the developer may have removed the name from the element.
```cs
var markdownText = control.FindChild("MarkdownText") as MarkdownTextBlock;
if (markdownText != null)
{
@ -102,13 +112,13 @@ if (markdownText != null)
}
```
You'll have to register all events and grab **control.Resources** for templates from this method as the regular sample page XAML isn't used and you can't hook in an event from the dynamic XAML, it must be done via code by finding the element here.
You'll have to register all events and grab **`control.Resources`** for templates from this method as the regular sample page XAML isn't used, and you can't hook in an event from the dynamic XAML, it must be done via code by finding the element here.
### 5. For Interactive Buttons: Use **`SampleController.Current.RegisterNewCommand`**
## 5. For Interactive Buttons: Use **SampleController.Current.RegisterNewCommand**
Buttons can be added through this command and are accessible in the main panel so they can be clicked when changing properties or editing XAML. It's important instead of using buttons in your sample (as events can't be directly used, see above) to register these commands.
Buttons can be added through this command and are accessible in the main panel, so they can be clicked when changing properties or editing XAML. It's important instead of using buttons in your sample (as events can't be directly used, see above) to register these commands.
```csharp
```cs
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
@ -117,26 +127,30 @@ protected override async void OnNavigatedTo(NavigationEventArgs e)
{
AddImage(false, true);
});
// •••
}
```
If your command adds content dynamically, try and use a style template in the .bind XAML that the user can modify. Then grab `resources = control.Resources;` in the *OnXamlRendered* event and set the element style from it:
If your command adds content dynamically, try and use a style template in the `.bind` XAML that the user can modify. Then grab `resources = control.Resources;` in the *`OnXamlRendered`* event and set the element style from it:
```csharp
```cs
if (resources?.ContainsKey("ThingStyle") == true)
{
newThing.Style = resources["ThingStyle"] as Style;
}
```
## 6. *Optional:* If you need *extra stuff* around the sample
Now, the sample page content in the app is ignored, but you can override that behavior by adding a `<Grid x:Name="XamlRoot"/>` element to the page. If this element is found, it will serve as the host to the dynamic .bind content instead. In this manner you can have a status/warning message outside of the control of the developer in the XAML sample tab.
### 6. *Optional:* If you need *extra stuff* around the sample
Now, the sample page content in the app is ignored, but you can override that behavior by adding a `<Grid x:Name="XamlRoot"/>` element to the page. If this element is found, it will serve as the host to the dynamic `.bind` content instead. In this manner you can have a status/warning message outside the control of the developer in the XAML sample tab.
# Update Samples.json
After creating your page and the binding text, you just need to reference it in the /SamplePages/samples.json file.
## Update `Samples.json`
After creating your page and the binding text, you just need to reference it in the `/SamplePages/samples.json` file.
Select the category where you want your page to be listed and add the following information:
## Basic Structure
### Basic Structure
```json
[
@ -148,7 +162,7 @@ Select the category where you want your page to be listed and add the following
"Name": "AdaptiveGridView",
"Type": "AdaptiveGridViewPage",
"About": "The AdaptiveGridView control allows to present information within a Grid View perfectly adjusting the total display available space. It reacts to changes in the layout as well as the content so it can adapt to different form factors automatically. The number and the width of items are calculated based on the screen resolution in order to fully leverage the available screen space. The property ItemsHeight define the items fixed height and the property DesiredWidth sets the minimum width for the elements to add a new column.",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls/TextToolbar",
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/CommunityToolkit.WinUI.UI.Controls.Core/TextToolbar",
"XamlCodeFile": "AdaptiveGridViewCode.bind",
"DocumentationUrl": "https://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/docs/controls/AdaptiveGridView.md"
}
@ -157,48 +171,51 @@ Select the category where you want your page to be listed and add the following
]
```
## Thumbnail Images
### Thumbnail Images
> NOTE: If creating a new icon, follow the [Thumbnail Style Guide and templates](https://github.com/CommunityToolkit/WindowsCommunityToolkit-design-assets)
For creating new icons, please follow the [Thumbnail Style Guide and templates](https://github.com/CommunityToolkit/WindowsCommunityToolkit-design-assets)
## Restricting Samples to Specific API Sets
### Restricting Samples to Specific API Sets
Some features used by samples aren't available on all the OS versions that the Sample App runs on. In order to make sure a sample is valid for the host OS, add the `ApiCheck` key/value in your JSON definition.
The value is a string which is the fully-qualified typename to check for the presence of. You can also accompany this with the `BadgeUpdateVersionRequred` which uses the string provided to show a short message on the sample information so up level implementors know the minimum version required.
The value is a string which is the fully-qualified type-name to check for the presence of. You can also accompany this with the `BadgeUpdateVersionRequired` which uses the string provided to show a short message on the sample information so up level implementer know the minimum version required.
```json
{
//...
// •••
"About": "MySample needs 10.0.18362 or higher to work.",
"ApiCheck": "Microsoft.UI.Xaml.Controls.NavigationView",
"BadgeUpdateVersionRequired": "Fall Creators Update required",
//...
// •••
}
```
If the specified type is not found on the system running the sample app the sample will not appear in the sample list.
If the specified type is not found on the system running the sample app, the sample will not appear in the sample list.
#### Adding documentation
### Adding documentation
Every API must be accompanied by Markdown doc in the [documentation](https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Contributing.md#docs) [repository](https://github.com/MicrosoftDocs/WindowsCommunityToolkitDocs).
Every API must be accompanied by Markdown documentation in the [documentation repository](..\contributing.md#docs).
Use the DocumentationUrl property to add a link to the raw documentation in *samples.json*. Please follow the following pattern:
Use the `DocumentationUrl` property to add a link to the raw documentation in *`samples.json`*. Please follow the following pattern:
`https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/{branch}/docs/{folder/file.md}`
> NOTE: When building and running the app in release mode, the branch will automatically be changed to **main** before loading.
##### NOTES
> NOTE: The documentation is also packaged with the sample app. If there is no network connection, or the documentation is not yet on GitHub, the sample app will use the packaged version
* When building and running the app in release mode, the branch will automatically be changed to **main** before loading.
* The documentation is also packaged with the sample app. If there is no network connection, or the documentation is not yet on GitHub, the sample app will use the packaged version.
* To test your documentation in the sample app while running in debug mode, the Docs repository will need to be cloned in the same folder as this repository and named **`WindowsCommunityToolkitDocs`**. For
> NOTE: To test your documentation in the sample app while running in debug mode, the docs repository will need to be cloned in the same folder as this repository and named **WindowsCommunityToolkitDocs**. For example, this folder structure works best:
> ```
> repositories
> ├── WindowsCommunityToolkit
> ├── WindowsCommunityToolkitDocs
> ```
Example, this folder structure works best:
### CodeUrl
```txt
Repos
├── WindowsCommunityToolkit
├── WindowsCommunityToolkitDocs
└── Others
```
The value of CodeUrl is modified when the app is built in release mode. The branch is automatically changed to **main**. This allows you to test the link in debug while pointing to dev.
#### Using `CodeUrl`
The value of `CodeUrl` is modified when the app is built in release mode. The branch is automatically changed to **main**. This allows you to test the link in debug while pointing to dev.

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

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net5.0-windows10.0.18362.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>

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

@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Microsoft.UI;
using Microsoft.UI.Composition;
@ -283,9 +284,10 @@ namespace CommunityToolkit.WinUI.UI.Animations.Expressions
// Create a map to store the generated paramNames for each CompObj
_compObjToParamNameMap = new Dictionary<CompositionObject, string>();
var paramCount = 0u;
foreach (var compObj in compObjects)
{
string paramName = Guid.NewGuid().ToUppercaseAsciiLetters();
string paramName = CreateUniqueParamNameFromIndex(paramCount++);
_compObjToParamNameMap.Add(compObj, paramName);
}
@ -312,6 +314,37 @@ namespace CommunityToolkit.WinUI.UI.Animations.Expressions
refNode.ParamName = paramName;
}
}
// Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, BA...
// This implementation aggregates characters in reverse order to avoid having to
// precompute the exact number of characters in the resulting string. This is not
// important in this context as the only critical property to maintain is to have
// a unique mapping to each input value to the resulting sequence of letters.
[SkipLocalsInit]
static unsafe string CreateUniqueParamNameFromIndex(uint i)
{
const int alphabetLength = 'Z' - 'A' + 1;
// The total length of the resulting sequence is guaranteed to always
// be less than 8, given that log26(4294967295) ≈ 6.8. In this case we
// are just allocating the immediate next power of two following that.
// Note: this is using a char* buffer instead of Span<char> as the latter
// is not referenced here, and we don't want to pull in an extra package.
char* characters = stackalloc char[8];
characters[0] = (char)('A' + (i % alphabetLength));
int totalCharacters = 1;
while ((i /= alphabetLength) > 0)
{
i--;
characters[totalCharacters++] = (char)('A' + (i % alphabetLength));
}
return new string(characters, 0, totalCharacters);
}
}
/// <summary>
@ -670,4 +703,4 @@ namespace CommunityToolkit.WinUI.UI.Animations.Expressions
/// <value>The subchannels.</value>
protected internal string[] Subchannels { get; set; }
}
}
}

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

@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Used to indicate to the compiler that the <c>.locals init</c> flag should not be set in method headers.
/// </summary>
/// <remarks>Internal copy from the BCL attribute.</remarks>
[AttributeUsage(
AttributeTargets.Module |
AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Interface |
AttributeTargets.Constructor |
AttributeTargets.Method |
AttributeTargets.Property |
AttributeTargets.Event,
Inherited = false)]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}

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

@ -0,0 +1,62 @@
// 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 Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using Microsoft.Xaml.Interactivity;
using Windows.System;
namespace CommunityToolkit.WinUI.UI.Behaviors
{
/// <summary>
/// This behavior listens to a key down event on the associated <see cref="UIElement"/> when it is loaded and executes an action.
/// </summary>
[TypeConstraint(typeof(FrameworkElement))]
public class KeyDownTriggerBehavior : Trigger<FrameworkElement>
{
/// <summary>
/// Identifies the <see cref="Key"/> property.
/// </summary>
public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
nameof(Key),
typeof(VirtualKey),
typeof(KeyDownTriggerBehavior),
new PropertyMetadata(null));
/// <summary>
/// Gets or sets the key to listen when the associated object is loaded.
/// </summary>
public VirtualKey Key
{
get => (VirtualKey)GetValue(KeyProperty);
set => SetValue(KeyProperty, value);
}
/// <inheritdoc/>
protected override void OnAttached()
{
((FrameworkElement)AssociatedObject).KeyDown += OnAssociatedObjectKeyDown;
}
/// <inheritdoc/>
protected override void OnDetaching()
{
((FrameworkElement)AssociatedObject).KeyDown -= OnAssociatedObjectKeyDown;
}
/// <summary>
/// Invokes the current actions when the <see cref="Key"/> is pressed.
/// </summary>
/// <param name="sender">The source <see cref="UIElement"/> instance.</param>
/// <param name="keyRoutedEventArgs">The arguments for the event (unused).</param>
private void OnAssociatedObjectKeyDown(object sender, KeyRoutedEventArgs keyRoutedEventArgs)
{
if (keyRoutedEventArgs.Key == Key)
{
keyRoutedEventArgs.Handled = true;
Interaction.ExecuteActions(sender, Actions, keyRoutedEventArgs);
}
}
}
}

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

@ -1,6 +1,8 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.UI.Controls.Core.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.UI.Controls.CameraPreview" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DropShadowPanel" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.ImageEx" />
@ -13,6 +15,7 @@
<Item Type="CommunityToolkit.WinUI.UI.Controls.RotatorTile" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.ScrollHeader" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.TabbedCommandBar" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.TabbedCommandBarItem" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.TextToolbar" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.TileControl" />
</ToolboxItems>

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

@ -1,7 +1,26 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.UI.Controls.DataGrid.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGrid" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridCell" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridCellsPresenter" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridColumn" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridBoundColumn" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridFillerColumn" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridCheckBoxColumn" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridComboBoxColumn" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridColumnHeader" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridColumnHeadersPresenter" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridDetailsPresenter" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridFrozenGrid" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridRow" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridRowHeader" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridRowGroupHeader" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridRowsPresenter" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridTemplateColumn" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DataGridTextColumn" />
</ToolboxItems>
</File>
</FileList>

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

@ -157,6 +157,37 @@ namespace CommunityToolkit.WinUI.UI.Controls
typeof(TokenizingTextBox),
new PropertyMetadata(false));
/// <summary>
/// Identifies the <see cref="MaximumTokens"/> property.
/// </summary>
public static readonly DependencyProperty MaximumTokensProperty = DependencyProperty.Register(
nameof(MaximumTokens),
typeof(int),
typeof(TokenizingTextBox),
new PropertyMetadata(null, OnMaximumTokensChanged));
private static void OnMaximumTokensChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TokenizingTextBox ttb && ttb.ReadLocalValue(MaximumTokensProperty) != DependencyProperty.UnsetValue && e.NewValue is int newMaxTokens)
{
var tokenCount = ttb._innerItemsSource.ItemsSource.Count;
if (tokenCount > 0 && tokenCount > newMaxTokens)
{
int tokensToRemove = tokenCount - Math.Max(newMaxTokens, 0);
// Start at the end, remove any extra tokens.
for (var i = tokenCount; i > tokenCount - tokensToRemove; --i)
{
var token = ttb._innerItemsSource.ItemsSource[i - 1];
// Force remove the items. No warning and no option to cancel.
ttb._innerItemsSource.Remove(token);
ttb.TokenItemRemoved?.Invoke(ttb, token);
}
}
}
}
/// <summary>
/// Gets or sets the Style for the contained AutoSuggestBox template part.
/// </summary>
@ -303,5 +334,14 @@ namespace CommunityToolkit.WinUI.UI.Controls
return PrepareSelectionForClipboard();
}
}
/// <summary>
/// Gets or sets the maximum number of token results allowed at a time.
/// </summary>
public int MaximumTokens
{
get => (int)GetValue(MaximumTokensProperty);
set => SetValue(MaximumTokensProperty, value);
}
}
}
}

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

@ -2,14 +2,15 @@
// 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;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.WinUI.Deferred;
using CommunityToolkit.WinUI.UI.Automation.Peers;
using CommunityToolkit.WinUI.UI.Helpers;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Windows.System;
@ -78,6 +79,17 @@ namespace CommunityToolkit.WinUI.UI.Controls
if (ItemsSource != null && ItemsSource.GetType() != typeof(InterspersedObservableCollection))
{
_innerItemsSource = new InterspersedObservableCollection(ItemsSource);
if (ReadLocalValue(MaximumTokensProperty) != DependencyProperty.UnsetValue && _innerItemsSource.ItemsSource.Count >= MaximumTokens)
{
// Reduce down to below the max as necessary.
var endCount = MaximumTokens > 0 ? MaximumTokens : 0;
for (var i = _innerItemsSource.ItemsSource.Count - 1; i >= endCount; --i)
{
_innerItemsSource.Remove(_innerItemsSource[i]);
}
}
_currentTextEdit = _lastTextEdit = new PretokenStringContainer(true);
_innerItemsSource.Insert(_innerItemsSource.Count, _currentTextEdit);
ItemsSource = _innerItemsSource;
@ -278,18 +290,16 @@ namespace CommunityToolkit.WinUI.UI.Controls
}
else
{
// TODO: It looks like we're setting selection and focus together on items? Not sure if that's what we want...
// If that's the case, don't think this code will ever be called?
//// TODO: Behavior question: if no items selected (just focus) does it just go to our last active textbox?
//// Community voted that typing in the end box made sense
// If no items are selected, send input to the last active string container.
// This code is only fires during an edgecase where an item is in the process of being deleted and the user inputs a character before the focus has been redirected to a string container.
if (_innerItemsSource[_innerItemsSource.Count - 1] is ITokenStringContainer textToken)
{
var last = ContainerFromIndex(Items.Count - 1) as TokenizingTextBoxItem; // Should be our last text box
var position = last._autoSuggestTextBox.SelectionStart;
textToken.Text = last._autoSuggestTextBox.Text.Substring(0, position) + args.Character +
last._autoSuggestTextBox.Text.Substring(position);
var text = last._autoSuggestTextBox.Text;
var selectionStart = last._autoSuggestTextBox.SelectionStart;
var position = selectionStart > text.Length ? text.Length : selectionStart;
textToken.Text = text.Substring(0, position) + args.Character +
text.Substring(position);
last._autoSuggestTextBox.SelectionStart = position + 1; // Set position to after our new character inserted
@ -432,6 +442,12 @@ namespace CommunityToolkit.WinUI.UI.Controls
internal async Task AddTokenAsync(object data, bool? atEnd = null)
{
if (ReadLocalValue(MaximumTokensProperty) != DependencyProperty.UnsetValue && (MaximumTokens <= 0 || MaximumTokens <= _innerItemsSource.ItemsSource.Count))
{
// No tokens for you
return;
}
if (data is string str && TokenItemAdding != null)
{
var tiaea = new TokenItemAddingEventArgs(str);
@ -486,6 +502,15 @@ namespace CommunityToolkit.WinUI.UI.Controls
Text = edit.Text; // Update our text property.
}
/// <summary>
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
/// </summary>
/// <returns>An automation peer for this <see cref="TokenizingTextBox"/>.</returns>
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TokenizingTextBoxAutomationPeer(this);
}
/// <summary>
/// Remove the specified token from the list.
/// </summary>

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

@ -0,0 +1,131 @@
// 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 System.Collections.Generic;
using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Automation.Provider;
using Microsoft.UI.Xaml.Controls;
namespace CommunityToolkit.WinUI.UI.Automation.Peers
{
/// <summary>
/// Defines a framework element automation peer for the <see cref="TokenizingTextBox"/> control.
/// </summary>
public class TokenizingTextBoxAutomationPeer : ListViewBaseAutomationPeer, IValueProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="TokenizingTextBoxAutomationPeer"/> class.
/// </summary>
/// <param name="owner">
/// The <see cref="TokenizingTextBox" /> that is associated with this <see cref="T:CommunityToolkit.WinUI.UI.Automation.Peers.TokenizingTextBoxAutomationPeer" />.
/// </param>
public TokenizingTextBoxAutomationPeer(TokenizingTextBox owner)
: base(owner)
{
}
/// <summary>Gets a value indicating whether the value of a control is read-only.</summary>
/// <returns>**true** if the value is read-only; **false** if it can be modified.</returns>
public bool IsReadOnly => !this.OwningTokenizingTextBox.IsEnabled;
/// <summary>Gets the value of the control.</summary>
/// <returns>The value of the control.</returns>
public string Value => this.OwningTokenizingTextBox.Text;
private TokenizingTextBox OwningTokenizingTextBox
{
get
{
return Owner as TokenizingTextBox;
}
}
/// <summary>Sets the value of a control.</summary>
/// <param name="value">The value to set. The provider is responsible for converting the value to the appropriate data type.</param>
/// <exception cref="T:Windows.UI.Xaml.Automation.ElementNotEnabledException">Thrown if the control is in a read-only state.</exception>
public void SetValue(string value)
{
if (IsReadOnly)
{
throw new ElementNotEnabledException($"Could not set the value of the {nameof(TokenizingTextBox)} ");
}
this.OwningTokenizingTextBox.Text = value;
}
/// <summary>
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
/// differentiates the control represented by this AutomationPeer.
/// </summary>
/// <returns>The string that contains the name.</returns>
protected override string GetClassNameCore()
{
return Owner.GetType().Name;
}
/// <summary>
/// Called by GetName.
/// </summary>
/// <returns>
/// Returns the first of these that is not null or empty:
/// - Value returned by the base implementation
/// - Name of the owning TokenizingTextBox
/// - TokenizingTextBox class name
/// </returns>
protected override string GetNameCore()
{
string name = this.OwningTokenizingTextBox.Name;
if (!string.IsNullOrWhiteSpace(name))
{
return name;
}
name = AutomationProperties.GetName(this.OwningTokenizingTextBox);
return !string.IsNullOrWhiteSpace(name) ? name : base.GetNameCore();
}
/// <summary>
/// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
/// </summary>
/// <param name="patternInterface">A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.</param>
/// <returns>The object that supports the specified pattern, or null if unsupported.</returns>
protected override object GetPatternCore(PatternInterface patternInterface)
{
return patternInterface switch
{
PatternInterface.Value => this,
_ => base.GetPatternCore(patternInterface)
};
}
/// <summary>
/// Gets the collection of elements that are represented in the UI Automation tree as immediate
/// child elements of the automation peer.
/// </summary>
/// <returns>The children elements.</returns>
protected override IList<AutomationPeer> GetChildrenCore()
{
TokenizingTextBox owner = this.OwningTokenizingTextBox;
ItemCollection items = owner.Items;
if (items.Count <= 0)
{
return null;
}
List<AutomationPeer> peers = new List<AutomationPeer>(items.Count);
for (int i = 0; i < items.Count; i++)
{
if (owner.ContainerFromIndex(i) is TokenizingTextBoxItem element)
{
peers.Add(FromElement(element) ?? CreatePeerForElement(element));
}
}
return peers;
}
}
}

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

@ -2,10 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Windows.Foundation;
using Windows.System;
@ -16,9 +18,11 @@ namespace CommunityToolkit.WinUI.UI.Controls
/// </summary>
[global::System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "Organization")]
[TemplatePart(Name = PART_AutoSuggestBox, Type = typeof(AutoSuggestBox))] //// String case
[TemplatePart(Name = PART_TokensCounter, Type = typeof(TextBlock))]
public partial class TokenizingTextBoxItem
{
private const string PART_AutoSuggestBox = "PART_AutoSuggestBox";
private const string PART_TokensCounter = "PART_TokensCounter";
private AutoSuggestBox _autoSuggestBox;
@ -231,6 +235,8 @@ namespace CommunityToolkit.WinUI.UI.Controls
#region Inner TextBox
private void OnASBLoaded(object sender, RoutedEventArgs e)
{
UpdateTokensCounter(this);
// Local function for Selection changed
void AutoSuggestTextBox_SelectionChanged(object box, RoutedEventArgs args)
{
@ -329,6 +335,44 @@ namespace CommunityToolkit.WinUI.UI.Controls
Owner.SelectAllTokensAndText();
}
}
private void UpdateTokensCounter(TokenizingTextBoxItem ttbi)
{
var maxTokensCounter = (TextBlock)_autoSuggestBox?.FindDescendant(PART_TokensCounter);
if (maxTokensCounter == null)
{
return;
}
void OnTokenCountChanged(TokenizingTextBox ttb, object value = null)
{
var itemsSource = ttb.ItemsSource as InterspersedObservableCollection;
var currentTokens = itemsSource.ItemsSource.Count;
var maxTokens = ttb.MaximumTokens;
maxTokensCounter.Text = $"{currentTokens}/{maxTokens}";
maxTokensCounter.Visibility = Visibility.Visible;
maxTokensCounter.Foreground = (currentTokens >= maxTokens)
? new SolidColorBrush(Colors.Red)
: _autoSuggestBox.Foreground;
}
ttbi.Owner.TokenItemAdded -= OnTokenCountChanged;
ttbi.Owner.TokenItemRemoved -= OnTokenCountChanged;
if (Content is ITokenStringContainer str && str.IsLast && ttbi?.Owner != null && ttbi.Owner.ReadLocalValue(TokenizingTextBox.MaximumTokensProperty) != DependencyProperty.UnsetValue)
{
ttbi.Owner.TokenItemAdded += OnTokenCountChanged;
ttbi.Owner.TokenItemRemoved += OnTokenCountChanged;
OnTokenCountChanged(ttbi.Owner);
}
else
{
maxTokensCounter.Visibility = Visibility.Collapsed;
maxTokensCounter.Text = string.Empty;
}
}
#endregion
}
}

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

@ -137,6 +137,7 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@ -176,7 +177,7 @@
ZoomMode="Disabled" />
<ContentControl x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Grid.ColumnSpan="3"
Grid.ColumnSpan="2"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
Content="{TemplateBinding PlaceholderText}"
@ -194,9 +195,15 @@
IsTabStop="False"
Style="{StaticResource TokenizingTextBoxDeleteButtonStyle}"
Visibility="Collapsed" />
<TextBlock Name="PART_TokensCounter"
Grid.Row="1"
Grid.Column="2"
Margin="0,4,0,0" />
<Button x:Name="QueryButton"
Grid.Row="1"
Grid.Column="2"
Grid.Column="3"
Width="{TemplateBinding Height}"
MinWidth="30"
VerticalAlignment="Stretch"

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

@ -1,8 +1,16 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.UI.Controls.Input.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.UI.Controls.ColorPicker" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.ColorPickerButton" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.ColorPickerSlider" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.RadialGauge" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.RangeSelector" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.RemoteDevicePicker" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.TokenizingTextBox" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.TokenizingTextBoxItem" />
</ToolboxItems>
</File>
</FileList>

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

@ -1,9 +1,12 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.UI.Controls.Layout.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.UI.Controls.BladeItem" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.BladeView" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.Carousel" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.CarouselItem" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.CarouselPanel" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.Expander" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.GridSplitter" />

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

@ -1,6 +1,8 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.UI.Controls.MarkdownTextBlock.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<File Reference="CommunityToolkit.WinUI.UI.Controls.Markdown.dll">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.UI.Controls.MarkdownTextBlock" />
</ToolboxItems>
</File>

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

@ -1,9 +1,14 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.UI.Controls.Media.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.UI.Controls.Eyedropper" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.EyedropperToolButton" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.ImageCropper" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.ImageCropperThumb" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.InfiniteCanvas" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.InfiniteCanvasTextBox" />
</ToolboxItems>
</File>
</FileList>

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

@ -61,7 +61,7 @@ namespace CommunityToolkit.WinUI.UI.Controls
/// Identifies the <see cref="MultipleX"/> property.
/// </summary>
public static readonly DependencyProperty MultipleXProperty =
DependencyProperty.Register(nameof(MultipleX), typeof(int), typeof(ConstrainedBox), new PropertyMetadata(null));
DependencyProperty.Register(nameof(MultipleX), typeof(int), typeof(ConstrainedBox), new PropertyMetadata(null, ConstraintPropertyChanged));
/// <summary>
/// Gets or sets the integer multiple that the height of the panel should be floored to. Default is null (no snap).
@ -76,7 +76,7 @@ namespace CommunityToolkit.WinUI.UI.Controls
/// Identifies the <see cref="MultipleY"/> property.
/// </summary>
public static readonly DependencyProperty MultipleYProperty =
DependencyProperty.Register(nameof(MultipleY), typeof(int), typeof(ConstrainedBox), new PropertyMetadata(null));
DependencyProperty.Register(nameof(MultipleY), typeof(int), typeof(ConstrainedBox), new PropertyMetadata(null, ConstraintPropertyChanged));
/// <summary>
/// Gets or sets aspect Ratio to use for the contents of the Panel (after scaling).
@ -93,11 +93,71 @@ namespace CommunityToolkit.WinUI.UI.Controls
public static readonly DependencyProperty AspectRatioProperty =
DependencyProperty.Register(nameof(AspectRatio), typeof(AspectRatio), typeof(ConstrainedBox), new PropertyMetadata(null, ConstraintPropertyChanged));
private bool _propertyUpdating;
private static void ConstraintPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ConstrainedBox panel)
if (d is ConstrainedBox panel && !panel._propertyUpdating)
{
panel._propertyUpdating = true;
panel.CoerceValues();
panel.InvalidateMeasure();
panel._propertyUpdating = false;
}
}
private void CoerceValues()
{
// Check if scale properties are in range
if (!double.IsNaN(ScaleX))
{
if (ScaleX < 0)
{
ScaleX = 0;
}
else if (ScaleX > 1.0)
{
ScaleX = 1.0;
}
}
else
{
ScaleX = 1.0;
}
if (!double.IsNaN(ScaleY))
{
if (ScaleY < 0)
{
ScaleY = 0;
}
else if (ScaleY > 1.0)
{
ScaleY = 1.0;
}
}
else
{
ScaleY = 1.0;
}
// Clear invalid values less than 0 for other properties
if (ReadLocalValue(MultipleXProperty) is int value && value <= 0)
{
ClearValue(MultipleXProperty);
}
if (ReadLocalValue(MultipleYProperty) is int value2 && value2 <= 0)
{
ClearValue(MultipleYProperty);
}
if (ReadLocalValue(AspectRatioProperty) is AspectRatio ratio && ratio <= 0)
{
ClearValue(AspectRatioProperty);
}
}
}

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

@ -1,11 +1,15 @@
<FileList>
<File Reference="CommunityToolkit.WinUI.UI.Controls.Primitives.dll">
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
<ToolboxItems UIFramework="UAP"
VSCategory="Windows Community Toolkit"
BlendCategory="Windows Community Toolkit">
<Item Type="CommunityToolkit.WinUI.UI.Controls.AdaptiveGridView" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.DockPanel" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.StaggeredLayout" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.StaggeredPanel" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.SwitchPresenter" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.UniformGrid" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.WrapLayout" />
<Item Type="CommunityToolkit.WinUI.UI.Controls.WrapPanel" />
</ToolboxItems>
</File>

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

@ -1,39 +1,46 @@
# Contributing to the Windows Community Toolkit :sparkles::sparkles:
# Contributing to the Windows Community Toolkit
Thank you for exhibiting interest in contributing to the Windows Community Toolkit. The team is delighted to welcome you onboard to our exciting and growing project. Any contribution or value added go a long way to enhance the project!
In the next few steps, you will be able to see a glimpse of ways you can contribute to the Windows Community Toolkit.
:rotating_light: **It is highly recommended to visit [Windows Community Toolkit Wiki](https://aka.ms/wct/wiki) where you can find complete and detail-oriented content of this page** :rotating_light:
🚨 **It is highly recommended to visit [Windows Community Toolkit Wiki](https://aka.ms/wct/wiki) where you can find complete and detail-oriented content of this page** 🚨
## <a name="question"></a> Questions :grey_question:
Due to the high volume of incoming issues please keep our GitHub issues for bug reports and feature requests. For general questions, there is a higher chance of getting your question answered on [StackOverflow](https://stackoverflow.com/questions/tagged/windows-community-toolkit) where questions should be tagged with the tag windows-community-toolkit.
## ❔ Questions <a name="question"></a>
Due to the high volume of incoming issues please keep our GitHub issues for bug reports and feature requests. For general questions, there is a higher chance of getting your question answered on [StackOverflow](https://stackoverflow.com/questions/tagged/windows-community-toolkit) where questions should be tagged with the tag `windows-community-toolkit`.
For missing documentation related question, please file an issue at [Microsoft Docs](https://github.com/MicrosoftDocs/WindowsCommunityToolkitDocs/issues/new).
## <a name="bug"></a> Fix a Bug :bug:
If you find any bug, you can help the community by [submitting an issue](https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/new?assignees=&labels=bug+%3Abug%3A&template=bug_report.md&title=). Once the issue is filed, feel free to start working on the PR and submit a PR.
## 🐛 Fix a Bug <a name="bug"></a>
## <a name="issue"></a>Good First Issue :ok_hand:
If this is your first time contributing to the Windows Community Toolkit and do not have advanced level programming experience, we have got you covered :boom: WCT has a list of [good first issue](https://github.com/CommunityToolkit/WindowsCommunityToolkit/labels/good%20first%20issue%20%3Aok_hand%3A) that can be a great entryway to find and fix any issues that best fit your expertise or technical background.
If you find any bug, you can help the community by [submitting an issue](https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/new?template=bug_report.md&labels=bug+:bug:&title=[Bug]). Once the issue is filed, feel free to start working on the PR and submit a PR.
## <a name="help"></a>Help Wanted :raising_hand:
WCT has a list of issues that are labeled as [help wanted](https://github.com/CommunityToolkit/WindowsCommunityToolkit/labels/help%20wanted%20%3Araising_hand%3A). The level of complexity in the list can vary but if you have an advanced level of programming experience, feel free to jump in to solve these issues.
## 👌 Good First Issue <a name="issue"></a>
## <a name="feature"></a>Add New Feature :mailbox_with_mail:
* To contribute a new feature, fill out the [Feature Request Template](https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/new?assignees=&labels=feature+request+%3Amailbox_with_mail%3A&template=feature_request.md&title=%5BFeature%5D) and provide detailed information to express the proposal.
* Once the Feature Request is submitted, it will be open for discussion.
* If it gets approved by the team, proceed to submit a PR of the proposed Feature.
* If the PR contains an error-free code and the reviewer signs off, the PR will be merged.
If this is your first time contributing to the Windows Community Toolkit (_WCT_) and do not have advanced level programming experience, we have got you covered 💥 WCT has a list of [good first issue](https://github.com/CommunityToolkit/WindowsCommunityToolkit/labels/good%20first%20issue) that can be a great entryway to find and fix any issues that best fit your expertise or technical background.
## <a name="docs"></a> Add or Improve Documentation :page_with_curl:
## 🙋 Help Wanted <a name="help"></a>
WCT also has a list of issues that are labeled as [help wanted](https://github.com/CommunityToolkit/WindowsCommunityToolkit/labels/help%20wanted). The level of complexity in the list can vary but if you have an advanced level of programming experience, feel free to jump in to solve these issues.
## 📬 Add New Feature <a name="feature"></a>
* To contribute a new feature, fill out the [Feature Request Template](https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/new?template=feature_request.md&labels=feature+request+:mailbox_with_mail:&title=[Feature]) and provide detailed information to express the proposal.
* Once the Feature Request is submitted, it will be open for discussion.
* If it gets approved by the team, proceed to submit a PR of the proposed Feature.
* If the PR contains an error-free code and the reviewer signs off, the PR will be merged.
## 📝 Add or Improve Documentation <a name="docs"></a>
Due to the involvement of multiple steps to add or improve documents; it is required to visit [Windows Community Toolkit Wiki](https://aka.ms/wct/wiki) and follow contribution guidelines.
## <a name="pr"></a>Create, Submit or Review Pull Request :rocket:
Anyone with write access can create a Pull Request by forking the Windows Community Toolkit Repository. Here is how you can [Create a Pull Request from fork](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork). Once you fork the Windows Community Toolkit repo, it is essential to create all changes in the feature branch of your forked repository. If you have the changes in the forked feature branch, you can then create a Pull Request in the main Windows Community Toolkit.
## 🚀 Create, Submit or Review Pull Request <a name="pr"></a>
Anyone can create a Pull Request by forking the Windows Community Toolkit Repository. Here is how you can [Create a Pull Request from fork](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork). Once you fork the Windows Community Toolkit repo, it is essential to create all changes in the feature branch of your forked repository. If you have the changes in the forked feature branch, you can then create a Pull Request in the main Windows Community Toolkit.
Please visit [Windows Community Toolkit Wiki](https://aka.ms/wct/wiki) for detailed information and steps it requires to Submit or Review Pull Request.
# ThankYou :heart::heart:
**Thank you so much for contributing to this amazing project. We hope you will continue to add value and find yourself as a highly reliable source to the Windows Community Toolkit**
## 💙 Thank You
**Thank you so much for contributing to this amazing project. We hope you will continue to add value and find yourself as a highly reliable source to the Windows Community Toolkit.**

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

@ -1,13 +1,13 @@
# Windows Community Toolkit
Copyright (c) .NET Foundation and Contributors
Copyright © .NET Foundation and Contributors
All rights reserved.
# MIT License (MIT)
## MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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

@ -1,44 +1,55 @@
# Windows Community Toolkit :toolbox:
# 🧰 Windows Community Toolkit
The Windows Community Toolkit is a collection of helper functions, custom controls, and app services. It simplifies and demonstrates common developer patterns when building experiences for Windows 10.
| Target | Branch | Status | Recommended package version |
| ------ | ------ | ------ | ------ |
| Production | rel/7.0.2 | [![Build Status](https://dev.azure.com/dotnet/CommunityToolkit/_apis/build/status/Toolkit-CI?branchName=rel/7.0.2)](https://dev.azure.com/dotnet/CommunityToolkit/_build/latest?definitionId=10&branchName=rel/7.0.2) | [![NuGet](https://img.shields.io/nuget/v/Microsoft.Toolkit.Uwp.svg)](https://www.nuget.org/profiles/Microsoft.Toolkit) |
| Pre-release beta testing | main | [![Build Status](https://dev.azure.com/dotnet/CommunityToolkit/_apis/build/status/Toolkit-CI?branchName=main)](https://dev.azure.com/dotnet/CommunityToolkit/_build/latest?definitionId=10) | [![DevOps](https://vsrm.dev.azure.com/dotnet/_apis/public/Release/badge/696bc9fd-f160-4e97-a1bd-7cbbb3b58f66/1/1)](https://dev.azure.com/dotnet/CommunityToolkit/_packaging?_a=feed&feed=CommunityToolkit-MainLatest) |
| Previews | main | [![Build Status](https://dev.azure.com/dotnet/CommunityToolkit/_apis/build/status/Toolkit-CI?branchName=main)](https://dev.azure.com/dotnet/CommunityToolkit/_build/latest?definitionId=10) | [![DevOps](https://vsrm.dev.azure.com/dotnet/_apis/public/Release/badge/696bc9fd-f160-4e97-a1bd-7cbbb3b58f66/1/1)](https://dev.azure.com/dotnet/CommunityToolkit/_packaging?_a=feed&feed=CommunityToolkit-MainLatest) |
## 🙌 Getting Started
## Getting Started :raised_hands:
Please read the [Getting Started with the Windows Community Toolkit](https://docs.microsoft.com/windows/communitytoolkit/getting-started) page for more detailed information about using the toolkit.
## Documentation :pencil:
## 📃 Documentation
All documentation for the toolkit is hosted on [Microsoft Docs](https://docs.microsoft.com/windows/communitytoolkit/). All API documentation can be found at the [.NET API Browser](https://docs.microsoft.com/dotnet/api/?view=win-comm-toolkit-dotnet-stable).
## Windows Community Toolkit Sample App :iphone:
## 📱 Windows Community Toolkit Sample App
Want to see the toolkit in action before jumping into the code? Download and play with the [Windows Community Toolkit Sample App](https://www.microsoft.com/store/apps/9nblggh4tlcq) from the Store.
## Contribution :rocket:
## 🚀 Contribution
Do you want to contribute? Check out our [Windows Community Toolkit Wiki](https://aka.ms/wct/wiki) page to learn more about contribution and guidelines.
## NuGet Packages :package:
NuGet is a standard package manager for .NET applications which is built into Visual Studio. When you open solution in Visual Studio, choose the *Tools* menu > *NuGet Package Manager* > *Manage NuGet packages for solution...* Enter one of the package names mentioned in [Windows Community Toolkit Nuget Packages](https://docs.microsoft.com/en-us/windows/communitytoolkit/nuget-packages) table to search for it online.
## 📦 NuGet Packages
NuGet is a standard package manager for .NET applications which is built into Visual Studio. When you open solution in Visual Studio, choose the *Tools* menu > *NuGet Package Manager* > *Manage NuGet packages for solution…* Enter one of the package names mentioned in [Windows Community Toolkit NuGet Packages](https://docs.microsoft.com/windows/communitytoolkit/nuget-packages) table to search for it online.
## 📫 Features <a name="supported"></a>
## <a name="supported"></a> Features :mailbox:
The [Features list](https://github.com/MicrosoftDocs/WindowsCommunityToolkitDocs/blob/master/docs/toc.md#controls) refers to all the currently available features that can be found in the Windows Community Toolkit. Most features should work with the October 2018 Update (1809) SDK 17763 and above; however, refer to specific documentation on each feature for more information.
## Principles :ballot_box_with_check:
* Principle **#1**: The toolkit will be kept simple.
* Principle **#2**: As soon as a comparable feature is available in the Windows SDK for Windows 10, it will be marked as deprecated.
* Principle **#3**: All features will be supported for two Windows SDK for Windows 10 release cycles or until another principle supersedes it.
## 💠 Principles
1. The toolkit will be kept simple.
2. As soon as a comparable feature is available in the Windows SDK for Windows 10, it will be marked as deprecated.
3. All features will be supported for two Windows SDK for Windows 10 release cycles or until another principle supersedes it.
## 🌍 Roadmap
## Roadmap :earth_americas:
Read what we [plan for next iterations](https://github.com/CommunityToolkit/WindowsCommunityToolkit/milestones), and feel free to ask questions.
Check out our [Preview Packages Wiki Page](https://github.com/CommunityToolkit/WindowsCommunityToolkit/wiki/Preview-Packages) to learn more about updating your NuGet sources in Visual Studio, then you can also get pre-release packages of upcoming versions to try.
## Code of Conduct :page_facing_up:
## 📄 Code of Conduct
This project has adopted the code of conduct defined by the [Contributor Covenant](http://contributor-covenant.org/)
to clarify expected behavior in our community.
For more information see the [.NET Foundation Code of Conduct](CODE_OF_CONDUCT.md).
## .NET Foundation
## 🏢 .NET Foundation
This project is supported by the [.NET Foundation](http://dotnetfoundation.org).

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

@ -2,6 +2,7 @@
// 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.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
@ -75,5 +76,56 @@ namespace UnitTests.Mvvm
public partial class SampleModelWithoutHelpers
{
}
[TestCategory("Mvvm")]
[TestMethod]
public void Test_INotifyPropertyChanged_WithGeneratedProperties()
{
Assert.IsTrue(typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(SampleModelWithINPCAndObservableProperties)));
Assert.IsFalse(typeof(INotifyPropertyChanging).IsAssignableFrom(typeof(SampleModelWithINPCAndObservableProperties)));
SampleModelWithINPCAndObservableProperties model = new();
List<PropertyChangedEventArgs> eventArgs = new();
model.PropertyChanged += (s, e) => eventArgs.Add(e);
model.X = 42;
model.Y = 66;
Assert.AreEqual(eventArgs.Count, 2);
Assert.AreEqual(eventArgs[0].PropertyName, nameof(SampleModelWithINPCAndObservableProperties.X));
Assert.AreEqual(eventArgs[1].PropertyName, nameof(SampleModelWithINPCAndObservableProperties.Y));
}
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4167
[INotifyPropertyChanged]
public partial class SampleModelWithINPCAndObservableProperties
{
[ObservableProperty]
private int x;
[ObservableProperty]
private int y;
}
[TestCategory("Mvvm")]
[TestMethod]
public void Test_INotifyPropertyChanged_WithGeneratedProperties_ExternalNetStandard20Assembly()
{
Assert.IsTrue(typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(NetStandard.SampleModelWithINPCAndObservableProperties)));
Assert.IsFalse(typeof(INotifyPropertyChanging).IsAssignableFrom(typeof(NetStandard.SampleModelWithINPCAndObservableProperties)));
NetStandard.SampleModelWithINPCAndObservableProperties model = new();
List<PropertyChangedEventArgs> eventArgs = new();
model.PropertyChanged += (s, e) => eventArgs.Add(e);
model.X = 42;
model.Y = 66;
Assert.AreEqual(eventArgs.Count, 2);
Assert.AreEqual(eventArgs[0].PropertyName, nameof(NetStandard.SampleModelWithINPCAndObservableProperties.X));
Assert.AreEqual(eventArgs[1].PropertyName, nameof(NetStandard.SampleModelWithINPCAndObservableProperties.Y));
}
}
}

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

@ -7,9 +7,10 @@
<ItemGroup>
<ProjectReference Include="..\..\CommunityToolkit.Common\CommunityToolkit.Common.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Mvvm\CommunityToolkit.Mvvm.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Diagnostics\CommunityToolkit.Diagnostics.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="contentfiles;build" />
<ProjectReference Include="..\..\CommunityToolkit.Mvvm\CommunityToolkitToolkit.Mvvm.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Diagnostics\CommunityToolkitToolkit.Diagnostics.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkitToolkit.Mvvm.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="contentfiles;build" />
<ProjectReference Include="..\UnitTests.NetStandard\UnitTests.NetStandard.csproj" />
</ItemGroup>
<ItemGroup>

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

@ -0,0 +1,22 @@
// 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.Mvvm.ComponentModel;
namespace UnitTests.NetStandard
{
/// <summary>
/// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4167.
/// This model in particular is loaded from an external .NET Standard 2.0 assembly.
/// </summary>
[INotifyPropertyChanged]
public partial class SampleModelWithINPCAndObservableProperties
{
[ObservableProperty]
private int x;
[ObservableProperty]
private int y;
}
}

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

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\CommunityToolkit.Mvvm\CommunityToolkit.Mvvm.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="contentfiles;build" />
</ItemGroup>
</Project>

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

@ -0,0 +1,89 @@
// 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 System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp;
using Microsoft.Toolkit.Uwp.UI;
using Microsoft.Toolkit.Uwp.UI.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
namespace UnitTests.UWP.UI.Controls
{
/// <summary>
/// These tests check whether the inner alignment of the box within it's parent works as expected.
/// </summary>
public partial class Test_ConstrainedBox : VisualUITestBase
{
// For this test we're testing within the confines of a 200x200 box to position a contrained
// 50x100 element in all the different alignment combinations.
[TestCategory("ConstrainedBox")]
[TestMethod]
[DataRow("Left", 0, "Center", 50, DisplayName = "LeftCenter")]
[DataRow("Left", 0, "Top", 0, DisplayName = "LeftTop")]
[DataRow("Center", 75, "Top", 0, DisplayName = "CenterTop")]
[DataRow("Right", 150, "Top", 0, DisplayName = "RightTop")]
[DataRow("Right", 150, "Center", 50, DisplayName = "RightCenter")]
[DataRow("Right", 150, "Bottom", 100, DisplayName = "RightBottom")]
[DataRow("Center", 75, "Bottom", 100, DisplayName = "CenterBottom")]
[DataRow("Left", 0, "Bottom", 100, DisplayName = "LeftBottom")]
[DataRow("Center", 75, "Center", 50, DisplayName = "CenterCenter")]
public async Task Test_ConstrainedBox_Alignment_Aspect(string horizontalAlignment, int expectedLeft, string verticalAlignment, int expectedTop)
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@$"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<Grid x:Name=""ParentGrid""
Width=""200"" Height=""200"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""1:2"" MaxHeight=""100""
UseLayoutRounding=""False""
HorizontalAlignment=""{horizontalAlignment}""
VerticalAlignment=""{verticalAlignment}"">
<Border HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"" Background=""Red""/>
</controls:ConstrainedBox>
</Grid>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var grid = treeRoot.FindChild("ParentGrid") as Grid;
Assert.IsNotNull(grid, "Could not find the ParentGrid in tree.");
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Force Layout calculations
panel.UpdateLayout();
var child = panel.Content as Border;
Assert.IsNotNull(child, "Could not find inner Border");
// Check Size
Assert.AreEqual(50, child.ActualWidth, 0.01, "Actual width does not meet expected value of 50");
Assert.AreEqual(100, child.ActualHeight, 0.01, "Actual height does not meet expected value of 100");
// Check inner Positioning, we do this from the Grid as the ConstainedBox also modifies its own size
// and is hugging the child.
var position = grid.CoordinatesTo(child);
Assert.AreEqual(expectedLeft, position.X, 0.01, "X position does not meet expected value of 0");
Assert.AreEqual(expectedTop, position.Y, 0.01, "Y position does not meet expected value of 50");
});
}
}
}

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

@ -0,0 +1,140 @@
// 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 System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp;
using Microsoft.Toolkit.Uwp.UI;
using Microsoft.Toolkit.Uwp.UI.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
namespace UnitTests.UWP.UI.Controls
{
/// <summary>
/// These tests check for the various values which can be coerced and changed if out of bounds for each property.
/// </summary>
public partial class Test_ConstrainedBox : VisualUITestBase
{
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_Coerce_Scale()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" ScaleX=""0.5"" ScaleY=""0.5""/>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Check Size
Assert.AreEqual(0.5, panel.ScaleX, 0.01, "ScaleX does not meet expected initial value of 0.5");
Assert.AreEqual(0.5, panel.ScaleY, 0.01, "ScaleY does not meet expected initial value of 0.5");
// Change values now to be invalid
panel.ScaleX = double.NaN;
panel.ScaleY = double.NaN;
Assert.AreEqual(1.0, panel.ScaleX, 0.01, "ScaleX does not meet expected value of 1.0 after change.");
Assert.AreEqual(1.0, panel.ScaleY, 0.01, "ScaleY does not meet expected value of 1.0 after change.");
// Change values now to be invalid
panel.ScaleX = double.NegativeInfinity;
panel.ScaleY = double.NegativeInfinity;
Assert.AreEqual(0.0, panel.ScaleX, 0.01, "ScaleX does not meet expected value of 0.0 after change.");
Assert.AreEqual(0.0, panel.ScaleY, 0.01, "ScaleY does not meet expected value of 0.0 after change.");
// Change values now to be invalid
panel.ScaleX = double.PositiveInfinity;
panel.ScaleY = double.PositiveInfinity;
Assert.AreEqual(1.0, panel.ScaleX, 0.01, "ScaleX does not meet expected value of 1.0 after change.");
Assert.AreEqual(1.0, panel.ScaleY, 0.01, "ScaleY does not meet expected value of 1.0 after change.");
});
}
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_Coerce_Multiple()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" MultipleX=""32"" MultipleY=""32""/>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Check Size
Assert.AreEqual(32, panel.MultipleX, "MultipleX does not meet expected value of 32");
Assert.AreEqual(32, panel.MultipleY, "MultipleY does not meet expected value of 32");
// Change values now to be invalid
panel.MultipleX = 0;
panel.MultipleY = int.MinValue;
Assert.AreEqual(DependencyProperty.UnsetValue, panel.ReadLocalValue(ConstrainedBox.MultipleXProperty), "MultipleX does not meet expected value of UnsetValue after change.");
Assert.AreEqual(DependencyProperty.UnsetValue, panel.ReadLocalValue(ConstrainedBox.MultipleYProperty), "MultipleY does not meet expected value of UnsetValue after change.");
});
}
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_Coerce_AspectRatio()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""1.25:3.5""/>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Check Size
Assert.AreEqual(1.25 / 3.5, panel.AspectRatio, 0.01, "ApectRatio does not meet expected value of 1.25/3.5");
// Change values now to be invalid
panel.AspectRatio = -1;
Assert.AreEqual(DependencyProperty.UnsetValue, panel.ReadLocalValue(ConstrainedBox.AspectRatioProperty), "AspectRatio does not meet expected value of UnsetValue after change.");
});
}
}
}

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

@ -17,6 +17,9 @@ using Windows.UI.Xaml.Markup;
namespace UnitTests.UWP.UI.Controls
{
/// <summary>
/// These tests check multiple constraints are applied together in the correct order.
/// </summary>
public partial class Test_ConstrainedBox : VisualUITestBase
{
[TestCategory("ConstrainedBox")]

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

@ -0,0 +1,143 @@
// 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 System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp;
using Microsoft.Toolkit.Uwp.UI;
using Microsoft.Toolkit.Uwp.UI.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
namespace UnitTests.UWP.UI.Controls
{
/// <summary>
/// These tests check for cases where we want to constrain the size of the ConstrainedBox with other
/// properties like MaxWidth and MaxHeight. The ConstrainedBox is greedy about using all the available
/// space, so Min properties don't really play the same factor? At least hard to create a practical test.
/// </summary>
public partial class Test_ConstrainedBox : VisualUITestBase
{
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_Constrain_AspectMaxHeight()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<Grid HorizontalAlignment=""Center"" VerticalAlignment=""Center"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""2:1"" MaxHeight=""100"">
<Border HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"" Background=""Red""/>
</controls:ConstrainedBox>
</Grid>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Force Layout calculations
panel.UpdateLayout();
var child = panel.Content as Border;
Assert.IsNotNull(child, "Could not find inner Border");
// Check Size
Assert.AreEqual(200, child.ActualWidth, 0.01, "Actual width does not meet expected value of 200");
Assert.AreEqual(100, child.ActualHeight, 0.01, "Actual height does not meet expected value of 100");
});
}
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_Constrain_AspectMaxWidth()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<Grid HorizontalAlignment=""Center"" VerticalAlignment=""Center"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""2:1"" MaxWidth=""100"">
<Border HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"" Background=""Red""/>
</controls:ConstrainedBox>
</Grid>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Force Layout calculations
panel.UpdateLayout();
var child = panel.Content as Border;
Assert.IsNotNull(child, "Could not find inner Border");
// Check Size
Assert.AreEqual(100, child.ActualWidth, 0.01, "Actual width does not meet expected value of 100");
Assert.AreEqual(50, child.ActualHeight, 0.01, "Actual height does not meet expected value of 50");
});
}
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_Constrain_AspectBothMaxWidthHeight()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<Grid HorizontalAlignment=""Center"" VerticalAlignment=""Center"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""1:2"" MaxWidth=""200"" MaxHeight=""200"">
<Border HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"" Background=""Red""/>
</controls:ConstrainedBox>
</Grid>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Force Layout calculations
panel.UpdateLayout();
var child = panel.Content as Border;
Assert.IsNotNull(child, "Could not find inner Border");
// Check Size
Assert.AreEqual(100, child.ActualWidth, 0.01, "Actual width does not meet expected value of 100");
Assert.AreEqual(200, child.ActualHeight, 0.01, "Actual height does not meet expected value of 200");
});
}
}
}

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

@ -0,0 +1,157 @@
// 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 System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp;
using Microsoft.Toolkit.Uwp.UI;
using Microsoft.Toolkit.Uwp.UI.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
namespace UnitTests.UWP.UI.Controls
{
/// <summary>
/// These tests check for cases where one of the bounds provided by the parent panel is infinite.
/// </summary>
public partial class Test_ConstrainedBox : VisualUITestBase
{
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_AllInfinite_AspectWidthFallback()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
// Even though we constrain the size of the ScrollViewer, it initially reports infinite measure
// which is what we want to test. We constrain the dimension so that we have a specific value
// that we can measure against. If we instead restrained the inner Border, it'd just set
// it's side within the constrained space of the parent layout...
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<ScrollViewer HorizontalScrollMode=""Enabled"" HorizontalScrollBarVisibility=""Visible""
VerticalScrollMode=""Enabled"" VerticalScrollBarVisibility=""Visible""
Width=""200"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""2:1"">
<Border HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"" Background=""Red""/>
</controls:ConstrainedBox>
</ScrollViewer>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Force Layout calculations
panel.UpdateLayout();
var child = panel.Content as Border;
Assert.IsNotNull(child, "Could not find inner Border");
// Check Size
Assert.AreEqual(200, child.ActualWidth, 0.01, "Actual width does not meet expected value of 200");
Assert.AreEqual(100, child.ActualHeight, 0.01, "Actual height does not meet expected value of 100");
});
}
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_AllInfinite_AspectHeightFallback()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<ScrollViewer HorizontalScrollMode=""Enabled"" HorizontalScrollBarVisibility=""Visible""
VerticalScrollMode=""Enabled"" VerticalScrollBarVisibility=""Visible""
Height=""200"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""1:2"">
<Border HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"" Background=""Red""/>
</controls:ConstrainedBox>
</ScrollViewer>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Force Layout calculations
panel.UpdateLayout();
var child = panel.Content as Border;
Assert.IsNotNull(child, "Could not find inner Border");
// Check Size
Assert.AreEqual(100, child.ActualWidth, 0.01, "Actual width does not meet expected value of 100");
Assert.AreEqual(200, child.ActualHeight, 0.01, "Actual height does not meet expected value of 200");
});
}
[TestCategory("ConstrainedBox")]
[TestMethod]
public async Task Test_ConstrainedBox_AllInfinite_AspectBothFallback()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var treeRoot = XamlReader.Load(@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<ScrollViewer x:Name=""ScrollArea""
HorizontalScrollMode=""Enabled"" HorizontalScrollBarVisibility=""Visible""
VerticalScrollMode=""Enabled"" VerticalScrollBarVisibility=""Visible"">
<controls:ConstrainedBox x:Name=""ConstrainedBox"" AspectRatio=""1:2"">
<Border HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"" Background=""Red""/>
</controls:ConstrainedBox>
</ScrollViewer>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
// Initialize Visual Tree
await SetTestContentAsync(treeRoot);
var scroll = treeRoot.FindChild("ScrollArea") as ScrollViewer;
Assert.IsNotNull(scroll, "Could not find ScrollViewer in tree.");
var panel = treeRoot.FindChild("ConstrainedBox") as ConstrainedBox;
Assert.IsNotNull(panel, "Could not find ConstrainedBox in tree.");
// Force Layout calculations
panel.UpdateLayout();
var child = panel.Content as Border;
Assert.IsNotNull(child, "Could not find inner Border");
var width = scroll.ActualHeight / 2;
// Check Size
Assert.AreEqual(width, child.ActualWidth, 0.01, "Actual width does not meet expected value of " + width);
Assert.AreEqual(scroll.ActualHeight, child.ActualHeight, 0.01, "Actual height does not meet expected value of " + scroll.ActualHeight);
});
}
}
}

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

@ -0,0 +1,125 @@
// 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 System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.UI.Xaml.Automation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.UI.Xaml.Automation.Peers;
using Microsoft.Toolkit.Uwp;
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
using Microsoft.Toolkit.Uwp.UI.Controls;
namespace UnitTests.UWP.UI.Controls
{
[TestClass]
[TestCategory("Test_TokenizingTextBox")]
public class Test_TokenizingTextBox_AutomationPeer : VisualUITestBase
{
[TestMethod]
public async Task ShouldConfigureTokenizingTextBoxAutomationPeerAsync()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
const string expectedAutomationName = "MyAutomationName";
const string expectedName = "MyName";
const string expectedValue = "Wor";
var items = new ObservableCollection<TokenizingTextBoxTestItem> { new() { Title = "Hello" }, new() { Title = "World" } };
var tokenizingTextBox = new TokenizingTextBox { ItemsSource = items };
await SetTestContentAsync(tokenizingTextBox);
var tokenizingTextBoxAutomationPeer =
FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as TokenizingTextBoxAutomationPeer;
Assert.IsNotNull(tokenizingTextBoxAutomationPeer, "Verify that the AutomationPeer is TokenizingTextBoxAutomationPeer.");
// Asserts the automation peer name based on the Automation Property Name value.
tokenizingTextBox.SetValue(AutomationProperties.NameProperty, expectedAutomationName);
Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedAutomationName), "Verify that the UIA name contains the given AutomationProperties.Name of the TokenizingTextBox.");
// Asserts the automation peer name based on the element Name property.
tokenizingTextBox.Name = expectedName;
Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedName), "Verify that the UIA name contains the given Name of the TokenizingTextBox.");
tokenizingTextBoxAutomationPeer.SetValue(expectedValue);
Assert.IsTrue(tokenizingTextBoxAutomationPeer.Value.Equals(expectedValue), "Verify that the Value contains the given Text of the TokenizingTextBox.");
});
}
[TestMethod]
public async Task ShouldReturnTokensForTokenizingTextBoxAutomationPeerAsync()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
var items = new ObservableCollection<TokenizingTextBoxTestItem>
{
new() { Title = "Hello" }, new() { Title = "World" }
};
var tokenizingTextBox = new TokenizingTextBox { ItemsSource = items };
await SetTestContentAsync(tokenizingTextBox);
tokenizingTextBox
.SelectAllTokensAndText(); // Will be 3 items due to the `AndText` that will select an empty text item.
var tokenizingTextBoxAutomationPeer =
FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as
TokenizingTextBoxAutomationPeer;
Assert.IsNotNull(
tokenizingTextBoxAutomationPeer,
"Verify that the AutomationPeer is TokenizingTextBoxAutomationPeer.");
var selectedItems = tokenizingTextBoxAutomationPeer
.GetChildren()
.Cast<ListViewItemAutomationPeer>()
.Select(peer => peer.Owner as TokenizingTextBoxItem)
.Select(item => item?.Content as TokenizingTextBoxTestItem)
.ToList();
Assert.AreEqual(3, selectedItems.Count);
Assert.AreEqual(items[0], selectedItems[0]);
Assert.AreEqual(items[1], selectedItems[1]);
Assert.IsNull(selectedItems[2]); // The 3rd item is the empty text item.
});
}
[TestMethod]
public async Task ShouldThrowElementNotEnabledExceptionIfValueSetWhenDisabled()
{
await App.DispatcherQueue.EnqueueAsync(async () =>
{
const string expectedValue = "Wor";
var tokenizingTextBox = new TokenizingTextBox { IsEnabled = false };
await SetTestContentAsync(tokenizingTextBox);
var tokenizingTextBoxAutomationPeer =
FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as TokenizingTextBoxAutomationPeer;
Assert.ThrowsException<ElementNotEnabledException>(() =>
{
tokenizingTextBoxAutomationPeer.SetValue(expectedValue);
});
});
}
public class TokenizingTextBoxTestItem
{
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
}
}

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

@ -63,5 +63,57 @@ namespace UnitTests.UWP.UI.Controls
Assert.AreEqual(tokenBox.Items.Count, 5, "Cancelled Clear Failed ");
}
[TestCategory("Test_TokenizingTextBox_General")]
[UITestMethod]
public void Test_MaximumTokens()
{
var maxTokens = 2;
var treeRoot = XamlReader.Load(
$@"<Page
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:controls=""using:Microsoft.Toolkit.Uwp.UI.Controls"">
<controls:TokenizingTextBox x:Name=""tokenboxname"" MaximumTokens=""{maxTokens}"">
</controls:TokenizingTextBox>
</Page>") as FrameworkElement;
Assert.IsNotNull(treeRoot, "Could not load XAML tree.");
var tokenBox = treeRoot.FindChild("tokenboxname") as TokenizingTextBox;
Assert.IsNotNull(tokenBox, "Could not find TokenizingTextBox in tree.");
// Items includes the text fields as well, so we can expect at least one item to exist initially, the input box.
// Use the starting count as an offset.
var startingItemsCount = tokenBox.Items.Count;
// Add two items.
tokenBox.AddTokenItem("TokenItem1");
tokenBox.AddTokenItem("TokenItem2");
// Make sure we have the appropriate amount of items and that they are in the appropriate order.
Assert.AreEqual(startingItemsCount + maxTokens, tokenBox.Items.Count, "Token Add failed");
Assert.AreEqual("TokenItem1", tokenBox.Items[0]);
Assert.AreEqual("TokenItem2", tokenBox.Items[1]);
// Attempt to add an additional item, beyond the maximum.
tokenBox.AddTokenItem("TokenItem3");
// Check that the number of items did not change, because the maximum number of items are already present.
Assert.AreEqual(startingItemsCount + maxTokens, tokenBox.Items.Count, "Token Add succeeded, where it should have failed.");
Assert.AreEqual("TokenItem1", tokenBox.Items[0]);
Assert.AreEqual("TokenItem2", tokenBox.Items[1]);
// Reduce the maximum number of tokens.
tokenBox.MaximumTokens = 1;
// The last token should be removed to account for the reduced maximum.
Assert.AreEqual(startingItemsCount + 1, tokenBox.Items.Count);
Assert.AreEqual("TokenItem1", tokenBox.Items[0]);
}
}
}

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

@ -130,13 +130,18 @@
<Compile Include="UI\Collection\Test_IncrementalLoadingCollection.cs" />
<Compile Include="UI\Controls\Test_Carousel.cs" />
<Compile Include="UI\Controls\Test_BladeView.cs" />
<Compile Include="UI\Controls\Test_ConstrainedBox.Alignment.cs" />
<Compile Include="UI\Controls\Test_ConstrainedBox.Constrict.cs" />
<Compile Include="UI\Controls\Test_ConstrainedBox.Infinity.cs" />
<Compile Include="UI\Controls\Test_ConstrainedBox.AspectRatio.cs" />
<Compile Include="UI\Controls\Test_ConstrainedBox.Coerce.cs" />
<Compile Include="UI\Controls\Test_ConstrainedBox.Multiple.cs" />
<Compile Include="UI\Controls\Test_ConstrainedBox.Combined.cs" />
<Compile Include="UI\Controls\Test_ImageEx.cs" />
<Compile Include="UI\Controls\Test_RadialGauge.cs" />
<Compile Include="UI\Controls\Test_TextToolbar_Localization.cs" />
<Compile Include="UI\Controls\Test_InfiniteCanvas_Regression.cs" />
<Compile Include="UI\Controls\Test_TokenizingTextBox_AutomationPeer.cs" />
<Compile Include="UI\Controls\Test_TokenizingTextBox_General.cs" />
<Compile Include="UI\Controls\Test_TokenizingTextBox_InterspersedCollection.cs" />
<Compile Include="UI\Controls\Test_ListDetailsView.cs" />

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

@ -10,6 +10,7 @@
"UnitTests\\UnitTests.HighPerformance.NetCore\\UnitTests.HighPerformance.NetCore.csproj",
"UnitTests\\UnitTests.HighPerformance.Shared\\UnitTests.HighPerformance.Shared.shproj",
"UnitTests\\UnitTests.NetCore\\UnitTests.NetCore.csproj",
"UnitTests\\UnitTests.NetStandard\\UnitTests.NetStandard.csproj",
"UnitTests\\UnitTests.Shared\\UnitTests.Shared.shproj",
"UnitTests\\UnitTests.SourceGenerators\\UnitTests.SourceGenerators.csproj",
]

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

@ -175,6 +175,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.Sourc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.SourceGenerators", "UnitTests\UnitTests.SourceGenerators\UnitTests.SourceGenerators.csproj", "{338C3BE4-2E71-4F21-AD30-03FDBB47A272}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.NetStandard", "UnitTests\UnitTests.NetStandard\UnitTests.NetStandard.csproj", "{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
UITests\UITests.Tests.Shared\UITests.Tests.Shared.projitems*{05c83067-fa46-45e2-bec4-edee84ad18d0}*SharedItemsImports = 5
@ -1195,6 +1197,26 @@ Global
{338C3BE4-2E71-4F21-AD30-03FDBB47A272}.Release|x64.Build.0 = Release|Any CPU
{338C3BE4-2E71-4F21-AD30-03FDBB47A272}.Release|x86.ActiveCfg = Release|Any CPU
{338C3BE4-2E71-4F21-AD30-03FDBB47A272}.Release|x86.Build.0 = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|ARM.Build.0 = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|ARM64.Build.0 = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|x64.ActiveCfg = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|x64.Build.0 = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|x86.ActiveCfg = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Debug|x86.Build.0 = Debug|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|Any CPU.Build.0 = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|ARM.ActiveCfg = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|ARM.Build.0 = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|ARM64.ActiveCfg = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|ARM64.Build.0 = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|x64.ActiveCfg = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|x64.Build.0 = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|x86.ActiveCfg = Release|Any CPU
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1248,6 +1270,7 @@ Global
{02215818-3D33-439E-843E-F1EC53D5A1BD} = {6FAA1CFE-3368-4FD2-9DBD-F4700F69174C}
{13FA48DC-56B2-4F71-AF2F-5D4BD7771AA5} = {B30036C4-D514-4E5B-A323-587A061772CE}
{338C3BE4-2E71-4F21-AD30-03FDBB47A272} = {B30036C4-D514-4E5B-A323-587A061772CE}
{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1} = {B30036C4-D514-4E5B-A323-587A061772CE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5403B0C4-F244-4F73-A35C-FE664D0F4345}