Merge branch 'master' into color-picker-fixes
|
@ -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. -->
|
||||
|
|
|
@ -4,7 +4,6 @@ about: I have a documentation suggestion or question
|
|||
title: "[Docs]"
|
||||
labels: documentation
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
|
|
@ -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.-->
|
||||
|
|
|
@ -4,7 +4,6 @@ about: I have a question about how to use something in the toolkit.
|
|||
title: "[Question]"
|
||||
labels: "question :grey_question:"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
|
|
@ -4,7 +4,6 @@ about: I have an issue with a Toolkit WPF or WinForms control
|
|||
title: "[Win32]"
|
||||
labels:
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
|
|
@ -4,14 +4,16 @@
|
|||
|
||||
<!-- 📝 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)
|
||||
- [ ] 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. -->
|
||||
|
||||
|
||||
## 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>
|
||||
|
|
|
@ -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.
|
||||
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.
|
||||
|
||||
## 🙋 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.
|
||||
|
||||
## <a name="docs"></a> Add or Improve Documentation :page_with_curl:
|
||||
## 📝 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.
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Mvvm.SourceGenerators
|
||||
{
|
||||
/// <summary>
|
||||
/// A source generator for necessary nullability attributes.
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public sealed class NullabilityAttributesGenerator : ISourceGenerator
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullAttribute");
|
||||
AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the source for a given attribute type if it's not present already in the compilation.
|
||||
/// </summary>
|
||||
private void AddSourceCodeIfTypeIsNotPresent(GeneratorExecutionContext context, string typeFullName)
|
||||
{
|
||||
// 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";
|
||||
|
||||
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename);
|
||||
StreamReader reader = new(stream);
|
||||
|
||||
string
|
||||
originalSource = reader.ReadToEnd(),
|
||||
outputSource = originalSource.Replace("NETSTANDARD2_0", "true");
|
||||
|
||||
context.AddSource($"{typeFullName}.cs", SourceText.From(outputSource, Encoding.UTF8));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -50,7 +51,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <remarks>
|
||||
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, newValue))
|
||||
{
|
||||
|
@ -75,7 +76,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <param name="comparer">The <see cref="IEqualityComparer{T}"/> instance to use to compare the input values.</param>
|
||||
/// <param name="propertyName">(optional) The name of the property that changed.</param>
|
||||
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (comparer.Equals(field, newValue))
|
||||
{
|
||||
|
@ -280,7 +281,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <paramref name="taskNotifier"/> is different than the previous one, and it does not mean the new
|
||||
/// <see cref="Task"/> instance passed as argument is in any particular state.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
|
||||
}
|
||||
|
@ -300,7 +301,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <remarks>
|
||||
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
|
||||
}
|
||||
|
@ -338,7 +339,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <paramref name="taskNotifier"/> is different than the previous one, and it does not mean the new
|
||||
/// <see cref="Task{TResult}"/> instance passed as argument is in any particular state.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
|
||||
}
|
||||
|
@ -359,7 +360,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <remarks>
|
||||
/// The <see cref="PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Compile Remove="EmbeddedResources\INotifyPropertyChanged.cs" />
|
||||
<Compile Remove="EmbeddedResources\NotNullAttribute.cs" />
|
||||
<Compile Remove="EmbeddedResources\NotNullIfNotNullAttribute.cs" />
|
||||
<Compile Remove="EmbeddedResources\ObservableObject.cs" />
|
||||
<Compile Remove="EmbeddedResources\ObservableRecipient.cs" />
|
||||
</ItemGroup>
|
||||
|
@ -17,6 +19,12 @@
|
|||
<EmbeddedResource Include="EmbeddedResources\INotifyPropertyChanged.cs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="..\Microsoft.Toolkit.Mvvm\Attributes\NotNullAttribute.cs" Link="EmbeddedResources\NotNullAttribute.cs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="..\Microsoft.Toolkit.Mvvm\Attributes\NotNullIfNotNullAttribute.cs" Link="EmbeddedResources\NotNullIfNotNullAttribute.cs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="..\Microsoft.Toolkit.Mvvm\ComponentModel\ObservableObject.cs" Link="EmbeddedResources\ObservableObject.cs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
|
||||
namespace System.Diagnostics.CodeAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that an output will not be null even if the corresponding type allows it.
|
||||
/// Specifies that an input argument was not null when the call returns.
|
||||
/// </summary>
|
||||
/// <remarks>Internal copy from the BCL attribute.</remarks>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
|
||||
internal sealed class NotNullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
// 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.
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
|
||||
namespace System.Diagnostics.CodeAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that the output will be non-null if the named parameter is non-null.
|
||||
/// </summary>
|
||||
/// <remarks>Internal copy from the BCL attribute.</remarks>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
|
||||
internal sealed class NotNullIfNotNullAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotNullIfNotNullAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parameterName"> The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.</param>
|
||||
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
|
||||
|
||||
/// <summary>Gets the associated parameter name.</summary>
|
||||
public string ParameterName { get; }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -18,6 +18,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -84,7 +85,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// The <see cref="PropertyChanging"/> and <see cref="PropertyChanged"/> events are not raised
|
||||
/// if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
// We duplicate the code here instead of calling the overload because we can't
|
||||
// guarantee that the invoked SetProperty<T> will be inlined, and we need the JIT
|
||||
|
@ -120,7 +121,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <param name="comparer">The <see cref="IEqualityComparer{T}"/> instance to use to compare the input values.</param>
|
||||
/// <param name="propertyName">(optional) The name of the property that changed.</param>
|
||||
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (comparer.Equals(field, newValue))
|
||||
{
|
||||
|
@ -340,7 +341,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// indicates that the new value being assigned to <paramref name="taskNotifier"/> is different than the previous one,
|
||||
/// and it does not mean the new <see cref="Task"/> instance passed as argument is in any particular state.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
// We invoke the overload with a callback here to avoid code duplication, and simply pass an empty callback.
|
||||
// The lambda expression here is transformed by the C# compiler into an empty closure class with a
|
||||
|
@ -368,7 +369,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// The <see cref="PropertyChanging"/> and <see cref="PropertyChanged"/> events are not raised
|
||||
/// if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion(ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action<Task?> callback, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
|
||||
}
|
||||
|
@ -407,7 +408,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// indicates that the new value being assigned to <paramref name="taskNotifier"/> is different than the previous one,
|
||||
/// and it does not mean the new <see cref="Task{TResult}"/> instance passed as argument is in any particular state.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName);
|
||||
}
|
||||
|
@ -430,7 +431,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// The <see cref="PropertyChanging"/> and <see cref="PropertyChanged"/> events are not raised
|
||||
/// if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetPropertyAndNotifyOnCompletion<T>([NotNull] ref TaskNotifier<T>? taskNotifier, Task<T>? newValue, Action<Task<T>?> callback, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName);
|
||||
}
|
||||
|
|
|
@ -7,15 +7,9 @@
|
|||
// This file is inspired from the MvvmLight library (lbugnion/MvvmLight),
|
||||
// more info in ThirdPartyNotices.txt in the root of the project.
|
||||
|
||||
// ================================= NOTE =================================
|
||||
// This file is mirrored in the ObservableRecipient annotated copy
|
||||
// (for debugging info) in the Mvvm.SourceGenerators project.
|
||||
// If any changes are made to this file, they should also be appropriately
|
||||
// ported to that file as well to keep the behavior consistent.
|
||||
// ========================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Toolkit.Mvvm.Messaging;
|
||||
using Microsoft.Toolkit.Mvvm.Messaging.Messages;
|
||||
|
@ -145,7 +139,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// the <see cref="ObservableObject.PropertyChanging"/> and <see cref="ObservableObject.PropertyChanged"/> events
|
||||
/// are not raised if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, bool broadcast, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, bool broadcast, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
T oldValue = field;
|
||||
|
||||
|
@ -174,7 +168,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <param name="broadcast">If <see langword="true"/>, <see cref="Broadcast{T}"/> will also be invoked.</param>
|
||||
/// <param name="propertyName">(optional) The name of the property that changed.</param>
|
||||
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, bool broadcast, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer<T> comparer, bool broadcast, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
T oldValue = field;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
@ -137,7 +138,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// the <see cref="ObservableObject.PropertyChanging"/> and <see cref="ObservableObject.PropertyChanged"/> events
|
||||
/// are not raised if the current and new value for the target property are the same.
|
||||
/// </remarks>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, bool validate, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, bool validate, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
bool propertyChanged = SetProperty(ref field, newValue, propertyName);
|
||||
|
||||
|
@ -162,7 +163,7 @@ namespace Microsoft.Toolkit.Mvvm.ComponentModel
|
|||
/// <param name="validate">If <see langword="true"/>, <paramref name="newValue"/> will also be validated.</param>
|
||||
/// <param name="propertyName">(optional) The name of the property that changed.</param>
|
||||
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
|
||||
protected bool SetProperty<T>(ref T field, T newValue, IEqualityComparer<T> comparer, bool validate, [CallerMemberName] string? propertyName = null)
|
||||
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer<T> comparer, bool validate, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
bool propertyChanged = SetProperty(ref field, newValue, comparer, propertyName);
|
||||
|
||||
|
|
|
@ -135,9 +135,9 @@ namespace Microsoft.Toolkit.Uwp.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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<FileList>
|
||||
<File Reference="Microsoft.Toolkit.Uwp.DeveloperTools.dll">
|
||||
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
|
||||
<ToolboxItems UIFramework="UAP"
|
||||
VSCategory="Windows Community Toolkit"
|
||||
BlendCategory="Windows Community Toolkit">
|
||||
<Item Type="Microsoft.Toolkit.Uwp.DeveloperTools.AlignmentGrid" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.DeveloperTools.FocusTracker" />
|
||||
</ToolboxItems>
|
||||
|
|
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/Assets/Samples/RangeSelector.png
До Ширина: | Высота: | Размер: 510 B |
После Ширина: | Высота: | Размер: 161 B |
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/Assets/pixel.png
До Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -0,0 +1,57 @@
|
|||
// 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;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.Common
|
||||
{
|
||||
public class Vector3Converter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is string)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
var thickness = (Vector3)value;
|
||||
|
||||
return thickness.ToString().TrimStart('<').Replace(" ", string.Empty).TrimEnd('>');
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is string vectorString)
|
||||
{
|
||||
var vectorTokens = vectorString.Split(',')
|
||||
.Where(tkn => !string.IsNullOrWhiteSpace(tkn))
|
||||
.ToArray();
|
||||
switch (vectorTokens.Length)
|
||||
{
|
||||
case 1:
|
||||
var vectorValue = float.Parse(vectorString);
|
||||
return new Vector3(vectorValue);
|
||||
case 2:
|
||||
var xValue = float.Parse(vectorTokens[0]);
|
||||
var yValue = float.Parse(vectorTokens[1]);
|
||||
|
||||
return new Vector3(xValue, yValue, 0);
|
||||
case 3:
|
||||
return new Vector3(
|
||||
float.Parse(vectorTokens[0]),
|
||||
float.Parse(vectorTokens[1]),
|
||||
float.Parse(vectorTokens[2]));
|
||||
default:
|
||||
return default(Vector3);
|
||||
}
|
||||
}
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -179,6 +179,14 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls
|
|||
converter = new ThicknessConverter();
|
||||
break;
|
||||
|
||||
case PropertyKind.Vector3:
|
||||
var vectorTextBox = new TextBox { Text = (propertyDict[option.Name] as ValueHolder).Value.ToString() };
|
||||
|
||||
controlToAdd = vectorTextBox;
|
||||
dependencyProperty = TextBox.TextProperty;
|
||||
converter = new Vector3Converter();
|
||||
break;
|
||||
|
||||
default:
|
||||
var textBox = new TextBox { Text = (propertyDict[option.Name] as ValueHolder).Value.ToString() };
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Parsers.Markdown;
|
||||
using Microsoft.Toolkit.Parsers.Markdown.Blocks;
|
||||
using Microsoft.Toolkit.Parsers.Markdown.Inlines;
|
||||
|
@ -407,19 +408,19 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls
|
|||
{
|
||||
get
|
||||
{
|
||||
return storage.Read<string>(DesiredLangKey);
|
||||
return settingsStorage.Read<string>(DesiredLangKey);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
storage.Save(DesiredLangKey, value);
|
||||
settingsStorage.Save(DesiredLangKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Local Storage Helper.
|
||||
/// The local app data storage helper for storing settings.
|
||||
/// </summary>
|
||||
private LocalObjectStorageHelper storage = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
private readonly ApplicationDataStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
/// <summary>
|
||||
/// DocFX note types and styling info, keyed by identifier.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -111,7 +111,7 @@
|
|||
</PackageReference>
|
||||
-->
|
||||
<PackageReference Include="Microsoft.UI.Xaml">
|
||||
<Version>2.6.1</Version>
|
||||
<Version>2.6.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Monaco.Editor">
|
||||
<Version>0.7.0-alpha</Version>
|
||||
|
@ -130,6 +130,7 @@
|
|||
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
|
||||
<Content Include="Assets\BrushAssets\TileTexture.png" />
|
||||
<Content Include="Assets\BrushAssets\NoiseTexture.png" />
|
||||
<Content Include="Assets\checker.png" />
|
||||
<Content Include="Assets\Llama.mp3" />
|
||||
<Content Include="Assets\mslogo.png" />
|
||||
<Content Include="Assets\NotificationAssets\Cloudy-Square.png" />
|
||||
|
@ -271,7 +272,9 @@
|
|||
<Content Include="SamplePages\Graph\LoginButton.png" />
|
||||
<Content Include="SamplePages\Graph\PeoplePicker.png" />
|
||||
<Content Include="SamplePages\Graph\PersonView.png" />
|
||||
<Content Include="SamplePages\Primitives\ConstrainedBox.png" />
|
||||
<Content Include="SamplePages\Primitives\SwitchPresenter.png" />
|
||||
<Content Include="SamplePages\RichSuggestBox\RichSuggestBox.png" />
|
||||
<Content Include="SamplePages\TabbedCommandBar\TabbedCommandBar.png" />
|
||||
<Content Include="SamplePages\Animations\Effects\FadeBehavior.png" />
|
||||
<Content Include="SamplePages\ColorPicker\ColorPicker.png" />
|
||||
|
@ -313,10 +316,10 @@
|
|||
<Content Include="SamplePages\FocusTracker\FocusTracker.png" />
|
||||
<Content Include="SamplePages\BladeView\BladeView.png" />
|
||||
<Content Include="SamplePages\Carousel\Carousel.png" />
|
||||
<Content Include="SamplePages\DropShadowPanel\DropShadowPanel.png" />
|
||||
<Content Include="SamplePages\Shadows\DropShadowPanel.png" />
|
||||
<Content Include="SamplePages\Expander\Expander.png" />
|
||||
<Content Include="SamplePages\DropShadowPanel\Trex.png" />
|
||||
<Content Include="SamplePages\DropShadowPanel\Unicorn.png" />
|
||||
<Content Include="SamplePages\Shadows\Trex.png" />
|
||||
<Content Include="SamplePages\Shadows\Unicorn.png" />
|
||||
<Content Include="SamplePages\GazeInteraction\GazeInteraction.png" />
|
||||
<Content Include="SamplePages\GazeTracing\GazeTracing.png" />
|
||||
<Content Include="SamplePages\GridSplitter\GridSplitter.png" />
|
||||
|
@ -395,7 +398,7 @@
|
|||
<Content Include="SamplePages\ImageCache\ImageCacheXaml.bind" />
|
||||
<Content Include="SamplePages\Incremental Loading Collection\IncrementalLoadingCollectionCode.bind" />
|
||||
<Content Include="SamplePages\ImageCache\ImageCacheCode.bind" />
|
||||
<Content Include="SamplePages\DropShadowPanel\DropShadowPanelXaml.bind" />
|
||||
<Content Include="SamplePages\Shadows\DropShadowPanelXaml.bind" />
|
||||
<Content Include="SamplePages\Object Storage\ObjectStorageCode.bind" />
|
||||
<Content Include="SamplePages\BackgroundTaskHelper\BackgroundTaskHelperCode.bind" />
|
||||
<Content Include="SamplePages\ListDetailsView\ListDetailsView.bind" />
|
||||
|
@ -486,6 +489,7 @@
|
|||
<Content Include="SamplePages\DataGrid\DataGridCode.bind" />
|
||||
<Content Include="SamplePages\ViewportBehavior\ViewportBehaviorCode.bind" />
|
||||
<Compile Include="Common\TextBlockHyperlinkBehavior.cs" />
|
||||
<Compile Include="Common\Vector3Converter.cs" />
|
||||
<Compile Include="SamplePages\AutoFocusBehavior\AutoFocusBehaviorPage.xaml.cs">
|
||||
<DependentUpon>AutoFocusBehaviorPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -504,6 +508,13 @@
|
|||
<Compile Include="SamplePages\MetadataControl\MetadataControlPage.xaml.cs">
|
||||
<DependentUpon>MetadataControlPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\Shadows\AttachedDropShadowPage.xaml.cs">
|
||||
<DependentUpon>AttachedDropShadowPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\RichSuggestBox\RichSuggestBoxPage.xaml.cs">
|
||||
<DependentUpon>RichSuggestBoxPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\RichSuggestBox\SuggestionTemplateSelector.cs" />
|
||||
<Compile Include="SamplePages\TilesBrush\TilesBrushPage.xaml.cs">
|
||||
<DependentUpon>TilesBrushPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -621,6 +632,14 @@
|
|||
<Content Include="SamplePages\Graph\PeoplePickerXaml.bind" />
|
||||
<Content Include="SamplePages\Graph\PersonViewXaml.bind" />
|
||||
<Content Include="SamplePages\ListViewExtensions\ListViewExtensionsCode.bind" />
|
||||
<Content Include="SamplePages\Primitives\ConstrainedBox.bind">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="SamplePages\Shadows\AttachedShadowWin2DXaml.bind" />
|
||||
<Content Include="SamplePages\Shadows\AttachedShadowCompositionXaml.bind" />
|
||||
<Content Include="SamplePages\Animations\Shadows\AnimatedCardShadowXaml.bind" />
|
||||
<Content Include="SamplePages\KeyDownTriggerBehavior\KeyDownTriggerBehaviorXaml.bind" />
|
||||
<Content Include="SamplePages\RichSuggestBox\RichSuggestBoxCode.bind" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="App.xaml.cs">
|
||||
|
@ -657,7 +676,6 @@
|
|||
<Compile Include="SamplePages\UniformGrid\UniformGridPage.xaml.cs">
|
||||
<DependentUpon>UniformGridPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\PropertyDescriptor\ThicknessPropertyOptions.cs" />
|
||||
<Compile Include="Models\ThemeChangedArgs.cs" />
|
||||
<Compile Include="Pages\SampleController.xaml.cs">
|
||||
<DependentUpon>SampleController.xaml</DependentUpon>
|
||||
|
@ -803,9 +821,6 @@
|
|||
<Compile Include="SamplePages\DispatcherQueueHelper\DispatcherQueueHelperPage.xaml.cs">
|
||||
<DependentUpon>DispatcherQueueHelperPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\DropShadowPanel\DropShadowPanelPage.xaml.cs">
|
||||
<DependentUpon>DropShadowPanelPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\Expander\ExpanderPage.xaml.cs">
|
||||
<DependentUpon>ExpanderPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -980,6 +995,18 @@
|
|||
<Content Include="SamplePages\Primitives\SwitchPresenter.bind">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Page Include="SamplePages\Shadows\AttachedDropShadowPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Content Include="SamplePages\RichSuggestBox\RichSuggestBoxXaml.bind">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Content>
|
||||
<Page Include="SamplePages\RichSuggestBox\RichSuggestBoxPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\TilesBrush\TilesBrushPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
@ -1073,6 +1100,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Content>
|
||||
<Content Include="SamplePages\Triggers\ControlSizeTrigger.bind">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Content>
|
||||
<Page Include="SamplePages\Triggers\FullScreenModeStateTriggerPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
@ -1263,10 +1294,6 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SamplePages\DropShadowPanel\DropShadowPanelPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\GridSplitter\GridSplitterPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -1470,6 +1497,7 @@
|
|||
<Name>Visual C++ 2015 Runtime for Universal Windows Platform Apps</Name>
|
||||
</SDKReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Models
|
|||
Bool,
|
||||
Brush,
|
||||
TimeSpan,
|
||||
Thickness
|
||||
Thickness,
|
||||
Vector3,
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ using System.Threading.Tasks;
|
|||
// TODO Reintroduce graph controls
|
||||
// using Microsoft.Toolkit.Graph.Converters;
|
||||
// using Microsoft.Toolkit.Graph.Providers;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Input.GazeInteraction;
|
||||
using Microsoft.Toolkit.Uwp.SampleApp.Models;
|
||||
|
@ -45,7 +46,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
|
||||
public static async void EnsureCacheLatest()
|
||||
{
|
||||
var settingsStorage = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
var settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
var onlineDocsSHA = await GetDocsSHA();
|
||||
var cacheSHA = settingsStorage.Read<string>(_cacheSHAKey);
|
||||
|
@ -417,8 +418,12 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
{
|
||||
if (proxy[option.Name] is ValueHolder value)
|
||||
{
|
||||
var newString = value.Value is Windows.UI.Xaml.Media.SolidColorBrush brush ?
|
||||
brush.Color.ToString() : value.Value.ToString();
|
||||
var newString = value.Value switch
|
||||
{
|
||||
Windows.UI.Xaml.Media.SolidColorBrush brush => brush.Color.ToString(),
|
||||
System.Numerics.Vector3 vector => vector.ToString().TrimStart('<').Replace(" ", string.Empty).TrimEnd('>'),
|
||||
_ => value.Value.ToString()
|
||||
};
|
||||
|
||||
result = result.Replace(option.OriginalString, newString);
|
||||
result = result.Replace("@[" + option.Label + "]@", newString);
|
||||
|
@ -629,12 +634,27 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
case PropertyKind.Thickness:
|
||||
try
|
||||
{
|
||||
var thicknessOptions = new ThicknessPropertyOptions { DefaultValue = value };
|
||||
var thicknessOptions = new PropertyOptions { DefaultValue = value };
|
||||
options = thicknessOptions;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})");
|
||||
Debug.WriteLine($"Unable to extract thickness info from {value}({ex.Message})");
|
||||
TrackingManager.TrackException(ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PropertyKind.Vector3:
|
||||
try
|
||||
{
|
||||
var vector3Options = new PropertyOptions { DefaultValue = value };
|
||||
options = vector3Options;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Unable to extract vector3 info from {value}({ex.Message})");
|
||||
TrackingManager.TrackException(ex);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp
|
||||
|
@ -21,7 +22,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
private static SemaphoreSlim _semaphore = new SemaphoreSlim(1);
|
||||
|
||||
private static LinkedList<Sample> _recentSamples;
|
||||
private static LocalObjectStorageHelper _localObjectStorageHelper = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
private static ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
public static async Task<SampleCategory> GetCategoryBySample(Sample sample)
|
||||
{
|
||||
|
@ -59,7 +60,8 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
{
|
||||
allCategories = await JsonSerializer.DeserializeAsync<List<SampleCategory>>(jsonStream.AsStream(), new JsonSerializerOptions
|
||||
{
|
||||
ReadCommentHandling = JsonCommentHandling.Skip
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
AllowTrailingCommas = true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -98,7 +100,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
if (_recentSamples == null)
|
||||
{
|
||||
_recentSamples = new LinkedList<Sample>();
|
||||
var savedSamples = _localObjectStorageHelper.Read<string>(_recentSamplesStorageKey);
|
||||
var savedSamples = _settingsStorage.Read<string>(_recentSamplesStorageKey);
|
||||
|
||||
if (savedSamples != null)
|
||||
{
|
||||
|
@ -144,7 +146,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
}
|
||||
|
||||
var str = string.Join(";", _recentSamples.Take(10).Select(s => s.Name).ToArray());
|
||||
_localObjectStorageHelper.Save<string>(_recentSamplesStorageKey, str);
|
||||
_settingsStorage.Save<string>(_recentSamplesStorageKey, str);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
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"
|
||||
<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>
|
||||
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"
|
||||
<TextBlock Grid.Column="1"
|
||||
Foreground="Black"
|
||||
Text="User Entered Text"
|
||||
FontSize="12"
|
||||
VerticalAlignment="Left"></TextBlock>
|
||||
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/Microsoft.Toolkit.Uwp.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/Microsoft.Toolkit.Uwp.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": "Windows.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.
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:ani="using:Microsoft.Toolkit.Uwp.UI.Animations"
|
||||
xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors"
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Button Background="Gray" Width="200" Height="200" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
|
||||
xmlns:media="using:Microsoft.Toolkit.Uwp.UI.Media"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:ani="using:Microsoft.Toolkit.Uwp.UI.Animations"
|
||||
xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<media:AttachedCardShadow x:Key="CommonShadow" Offset="4" CornerRadius="0"/>
|
||||
|
||||
<ani:AnimationSet x:Key="ShadowEnterAnimation">
|
||||
<ani:OffsetDropShadowAnimation To="12"/>
|
||||
</ani:AnimationSet>
|
||||
|
||||
<ani:AnimationSet x:Key="ShadowExitAnimation">
|
||||
<ani:OffsetDropShadowAnimation To="4"/>
|
||||
</ani:AnimationSet>
|
||||
|
||||
<ani:AnimationSet x:Key="ShadowPopAnimation" IsSequential="True">
|
||||
<ani:TranslationAnimation To="-8" Duration="0:0:1"/>
|
||||
<ani:OffsetDropShadowAnimation To="16" Duration="0:0:2" Target="{StaticResource CommonShadow}"/>
|
||||
<ani:OffsetDropShadowAnimation To="4" Delay="0:0:0.5" Duration="0:0:2" Target="{StaticResource CommonShadow}"/>
|
||||
<ani:TranslationAnimation To="0" Duration="0:0:1"/>
|
||||
</ani:AnimationSet>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Height="100" Width="100"
|
||||
Source="ms-appx:///Assets/Photos/Owl.jpg">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactions:EventTriggerBehavior EventName="PointerEntered">
|
||||
<behaviors:StartAnimationAction Animation="{StaticResource ShadowEnterAnimation}"/>
|
||||
</interactions:EventTriggerBehavior>
|
||||
<interactions:EventTriggerBehavior EventName="PointerExited">
|
||||
<behaviors:StartAnimationAction Animation="{StaticResource ShadowExitAnimation}"/>
|
||||
</interactions:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Image>
|
||||
<Image ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Height="100" Width="100"
|
||||
Grid.Column="1"
|
||||
Source="ms-appx:///Assets/Photos/Owl.jpg">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactions:EventTriggerBehavior EventName="PointerEntered">
|
||||
<behaviors:StartAnimationAction Animation="{StaticResource ShadowEnterAnimation}"/>
|
||||
</interactions:EventTriggerBehavior>
|
||||
<interactions:EventTriggerBehavior EventName="PointerExited">
|
||||
<behaviors:StartAnimationAction Animation="{StaticResource ShadowExitAnimation}"/>
|
||||
</interactions:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Image>
|
||||
<Button Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Top" Content="Click Me">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactions:EventTriggerBehavior EventName="Click">
|
||||
<behaviors:StartAnimationAction Animation="{StaticResource ShadowPopAnimation}"/>
|
||||
</interactions:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,26 +0,0 @@
|
|||
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.DropShadowPanelPage"
|
||||
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"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<Style x:Key="TitleText"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Margin" Value="5,20,0,5" />
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource Brush-Grey-03}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
</Style>
|
||||
<Style x:Key="DividingBorder"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="0,0,0,1" />
|
||||
<Setter Property="BorderBrush" Value="Black" />
|
||||
<Setter Property="Margin" Value="0,0,0,20" />
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid x:Name="XamlRoot"/>
|
||||
</Page>
|
|
@ -1,22 +0,0 @@
|
|||
// 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 Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
/// <summary>
|
||||
/// A page that shows how to use the DropShadowPanel control.
|
||||
/// </summary>
|
||||
public sealed partial class DropShadowPanelPage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DropShadowPanelPage"/> class.
|
||||
/// </summary>
|
||||
public DropShadowPanelPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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:Microsoft.Toolkit.Uwp.UI.Animations"
|
||||
xmlns:behaviors="using:Microsoft.Toolkit.Uwp.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>
|
|
@ -74,9 +74,9 @@
|
|||
</controls:ListDetailsView.NoSelectionContentTemplate>
|
||||
<controls:ListDetailsView.ListCommandBar>
|
||||
<CommandBar>
|
||||
<!-- Button functionality to be implemented by developer -->
|
||||
<AppBarButton Icon="Back" Label="Back"/>
|
||||
<AppBarButton Icon="Forward" Label="Forward"/>
|
||||
|
||||
<CommandBar.Content>
|
||||
<TextBlock Margin="12,14">
|
||||
<Run Text="{Binding Emails.Count}" />
|
||||
|
@ -87,6 +87,7 @@
|
|||
</controls:ListDetailsView.ListCommandBar>
|
||||
<controls:ListDetailsView.DetailsCommandBar>
|
||||
<CommandBar>
|
||||
<!-- Button functionality to be implemented by developer -->
|
||||
<AppBarButton Icon="MailReply" Label="Reply" />
|
||||
<AppBarButton Icon="MailReplyAll" Label="Reply All" />
|
||||
<AppBarButton Icon="MailForward" Label="Forward" />
|
||||
|
|
|
@ -35,14 +35,16 @@
|
|||
</Grid>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="5,10,10,0" Width="200">
|
||||
<TextBlock Text="Smooth Scroll Settings" FontSize="{StaticResource TextStyleLargeFontSize}" Margin="0,0,0,10"/>
|
||||
<TextBlock Text="Smooth Scroll Settings" FontSize="{StaticResource TextStyleLargeFontSize}"/>
|
||||
<TextBox x:Name="IndexInput"
|
||||
Header="Index"
|
||||
InputScope="Number"
|
||||
Text="100" />
|
||||
Text="100"
|
||||
Margin="0,10,0,0" />
|
||||
<ComboBox x:Name="ItemPlacementInput"
|
||||
Header="Item Placement"
|
||||
SelectedIndex="0">
|
||||
SelectedIndex="0"
|
||||
Margin="0,10,0,0" >
|
||||
<x:String>Default</x:String>
|
||||
<x:String>Left</x:String>
|
||||
<x:String>Top</x:String>
|
||||
|
@ -52,18 +54,25 @@
|
|||
</ComboBox>
|
||||
<CheckBox x:Name="DisableAnimationInput"
|
||||
Content="Disable Animation"
|
||||
IsChecked="False" />
|
||||
IsChecked="False"
|
||||
Margin="0,10,0,0" />
|
||||
<CheckBox x:Name="ScrollIfVisibileInput"
|
||||
Content="Scroll If Visible"
|
||||
IsChecked="True" />
|
||||
<TextBox x:Name="AdditionalHorizontalOffsetInput"
|
||||
Header="Horizontal Offset"
|
||||
InputScope="Number"
|
||||
Text="0" />
|
||||
Text="0"
|
||||
Margin="0,10,0,0" />
|
||||
<TextBox x:Name="AdditionalVerticalOffsetInput"
|
||||
Header="Vertical Offset"
|
||||
InputScope="Number"
|
||||
Text="0" />
|
||||
Text="0"
|
||||
Margin="0,10,0,0" />
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Ellipse x:Name="ScrollIndicator" Fill="Red" Width="15" Height="15"/>
|
||||
<TextBlock x:Name="ScrollIndicatorTest" Text="Not Scrolling" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -6,9 +6,11 @@ using System;
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
|
@ -37,7 +39,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
|
||||
private void Load()
|
||||
{
|
||||
SampleController.Current.RegisterNewCommand("Start Smooth Scroll", (sender, args) =>
|
||||
SampleController.Current.RegisterNewCommand("Start Smooth Scroll", async (sender, args) =>
|
||||
{
|
||||
var index = int.TryParse(IndexInput.Text, out var i) ? i : 0;
|
||||
var itemPlacement = ItemPlacementInput.SelectedItem switch
|
||||
|
@ -55,7 +57,9 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
var scrollIfVisibile = ScrollIfVisibileInput.IsChecked ?? true;
|
||||
var additionalHorizontalOffset = int.TryParse(AdditionalHorizontalOffsetInput.Text, out var ho) ? ho : 0;
|
||||
var additionalVerticalOffset = int.TryParse(AdditionalVerticalOffsetInput.Text, out var vo) ? vo : 0;
|
||||
sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset);
|
||||
UpdateScrollIndicator(true);
|
||||
await sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset);
|
||||
UpdateScrollIndicator(false);
|
||||
});
|
||||
|
||||
if (sampleListView != null)
|
||||
|
@ -64,6 +68,20 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateScrollIndicator(bool isScrolling)
|
||||
{
|
||||
if (isScrolling)
|
||||
{
|
||||
ScrollIndicatorTest.Text = "Scrolling";
|
||||
ScrollIndicator.Fill = new SolidColorBrush(Colors.Green);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScrollIndicator.Fill = new SolidColorBrush(Colors.Red);
|
||||
ScrollIndicatorTest.Text = "Not Scolling";
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableCollection<string> GetOddEvenSource(int count)
|
||||
{
|
||||
var oddEvenSource = new ObservableCollection<string>();
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
var localObjectStorageHelper = new LocalObjectStorageHelper();
|
||||
var roamingObjectStorageHelper = new RoamingObjectStorageHelper();
|
||||
ApplicationDataStorageHelper appDataStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer());
|
||||
|
||||
// Read and Save with simple objects
|
||||
string keySimpleObject = "simple";
|
||||
string result = localObjectStorageHelper.Read<string>(keySimpleObject);
|
||||
localObjectStorageHelper.Save(keySimpleObject, 47);
|
||||
string result = appDataStorageHelper.Read<string>(keySimpleObject);
|
||||
appDataStorageHelper.Save(keySimpleObject, 47);
|
||||
|
||||
// Read and Save with complex/large objects
|
||||
string keyLargeObject = "large";
|
||||
var result = localObjectStorageHelper.ReadFileAsync<MyLargeObject>(keyLargeObject);
|
||||
string complexObjectKey = "complexObject";
|
||||
var complexObject = await appDataStorageHelper.ReadFileAsync<MyLargeObject>(complexObjectKey);
|
||||
|
||||
var o = new MyLargeObject
|
||||
var myComplexObject = new MyComplexObject()
|
||||
{
|
||||
...
|
||||
};
|
||||
localObjectStorageHelper.SaveFileAsync(keySimpleObject, o);
|
||||
await appDataStorageHelper.SaveFileAsync(complexObjectKey, myComplexObject);
|
||||
|
||||
// Complex object
|
||||
public class MyLargeObject
|
||||
public class MyComplexObject
|
||||
{
|
||||
public string MyContent { get; set; }
|
||||
public List<string> MyContents { get; set; }
|
||||
public List<MyLargeObject> MyObjects { get; set; }
|
||||
public List<MyComplexObject> MyObjects { get; set; }
|
||||
}
|
|
@ -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 Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
|
@ -9,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
{
|
||||
public sealed partial class ObjectStoragePage
|
||||
{
|
||||
private readonly IObjectStorageHelper localStorageHelper = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
private readonly ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
public ObjectStoragePage()
|
||||
{
|
||||
|
@ -24,9 +25,9 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
}
|
||||
|
||||
// Read from local storage
|
||||
if (localStorageHelper.KeyExists(KeyTextBox.Text))
|
||||
if (_settingsStorage.KeyExists(KeyTextBox.Text))
|
||||
{
|
||||
ContentTextBox.Text = localStorageHelper.Read<string>(KeyTextBox.Text);
|
||||
ContentTextBox.Text = _settingsStorage.Read<string>(KeyTextBox.Text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +44,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
}
|
||||
|
||||
// Save into local storage
|
||||
localStorageHelper.Save(KeyTextBox.Text, ContentTextBox.Text);
|
||||
_settingsStorage.Save(KeyTextBox.Text, ContentTextBox.Text);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:brushes="using:Microsoft.Toolkit.Uwp.UI.Media"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<controls:ConstrainedBox AspectRatio="16:3" VerticalAlignment="Top">
|
||||
<Image Source="/Assets/Photos/WestSeattleView.jpg"
|
||||
Stretch="UniformToFill"
|
||||
VerticalAlignment="Center"/> <!-- Center on the City -->
|
||||
</controls:ConstrainedBox>
|
||||
<controls:ConstrainedBox MultipleX="64"
|
||||
AspectRatio="1:1"
|
||||
MinWidth="64" MaxWidth="512">
|
||||
<controls:ConstrainedBox.Background>
|
||||
<!-- TODO: TilesBrush doesn't support Dpi image loading for this scenario
|
||||
This example is configured for 100% DPI at the moment.
|
||||
See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4150
|
||||
-->
|
||||
<brushes:TilesBrush TextureUri="ms-appx:///Assets/checker.png"/>
|
||||
</controls:ConstrainedBox.Background>
|
||||
</controls:ConstrainedBox>
|
||||
</Grid>
|
||||
</Page>
|
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Primitives/ConstrainedBox.png
Normal file
После Ширина: | Высота: | Размер: 825 B |
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RichSuggestBox/RichSuggestBox.png
Normal file
После Ширина: | Высота: | Размер: 35 KiB |
|
@ -0,0 +1,50 @@
|
|||
private void SuggestingBox_OnTokenPointerOver(RichSuggestBox sender, RichSuggestTokenPointerOverEventArgs args)
|
||||
{
|
||||
var flyout = (Flyout)FlyoutBase.GetAttachedFlyout(sender);
|
||||
var pointerPosition = args.CurrentPoint.Position;
|
||||
|
||||
if (flyout?.Content is ContentPresenter cp && sender.TextDocument.Selection.Type != SelectionType.Normal &&
|
||||
(!flyout.IsOpen || cp.Content != args.Token.Item))
|
||||
{
|
||||
this._dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
cp.Content = args.Token.Item;
|
||||
flyout.ShowAt(sender, new FlyoutShowOptions
|
||||
{
|
||||
Position = pointerPosition,
|
||||
ExclusionRect = sender.GetRectFromRange(args.Range),
|
||||
ShowMode = FlyoutShowMode.TransientWithDismissOnPointerMoveAway,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void SuggestingBox_OnSuggestionChosen(RichSuggestBox sender, SuggestionChosenEventArgs args)
|
||||
{
|
||||
if (args.Prefix == "#")
|
||||
{
|
||||
args.Format.BackgroundColor = Colors.DarkOrange;
|
||||
args.Format.ForegroundColor = Colors.OrangeRed;
|
||||
args.Format.Bold = FormatEffect.On;
|
||||
args.Format.Italic = FormatEffect.On;
|
||||
args.DisplayText = ((SampleDataType)args.SelectedItem).Text;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.DisplayText = ((SampleEmailDataType)args.SelectedItem).DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
private void SuggestingBox_OnSuggestionRequested(RichSuggestBox sender, SuggestionRequestedEventArgs args)
|
||||
{
|
||||
if (args.Prefix == "#")
|
||||
{
|
||||
sender.ItemsSource =
|
||||
this._samples.Where(x => x.Text.Contains(args.QueryText, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
sender.ItemsSource =
|
||||
this._emailSamples.Where(x => x.DisplayName.Contains(args.QueryText, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.RichSuggestBoxPage"
|
||||
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"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<local:SuggestionTemplateSelector x:Key="SuggestionTemplateSelector" />
|
||||
<local:NameToColorConverter x:Key="NameToColorConverter" />
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Visibility="Collapsed">
|
||||
<controls:RichSuggestBox />
|
||||
<ListView />
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,191 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Uwp.UI;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class RichSuggestBoxPage : Page, IXamlRenderListener
|
||||
{
|
||||
private readonly List<SampleEmailDataType> _emailSamples = new List<SampleEmailDataType>()
|
||||
{
|
||||
new SampleEmailDataType() { FirstName = "Marcus", FamilyName = "Perryman" },
|
||||
new SampleEmailDataType() { FirstName = "Michael", FamilyName = "Hawker" },
|
||||
new SampleEmailDataType() { FirstName = "Matt", FamilyName = "Lacey" },
|
||||
new SampleEmailDataType() { FirstName = "Alexandre", FamilyName = "Chohfi" },
|
||||
new SampleEmailDataType() { FirstName = "Filip", FamilyName = "Wallberg" },
|
||||
new SampleEmailDataType() { FirstName = "Shane", FamilyName = "Weaver" },
|
||||
new SampleEmailDataType() { FirstName = "Vincent", FamilyName = "Gromfeld" },
|
||||
new SampleEmailDataType() { FirstName = "Sergio", FamilyName = "Pedri" },
|
||||
new SampleEmailDataType() { FirstName = "Alex", FamilyName = "Wilber" },
|
||||
new SampleEmailDataType() { FirstName = "Allan", FamilyName = "Deyoung" },
|
||||
new SampleEmailDataType() { FirstName = "Adele", FamilyName = "Vance" },
|
||||
new SampleEmailDataType() { FirstName = "Grady", FamilyName = "Archie" },
|
||||
new SampleEmailDataType() { FirstName = "Megan", FamilyName = "Bowen" },
|
||||
new SampleEmailDataType() { FirstName = "Ben", FamilyName = "Walters" },
|
||||
new SampleEmailDataType() { FirstName = "Debra", FamilyName = "Berger" },
|
||||
new SampleEmailDataType() { FirstName = "Emily", FamilyName = "Braun" },
|
||||
new SampleEmailDataType() { FirstName = "Christine", FamilyName = "Cline" },
|
||||
new SampleEmailDataType() { FirstName = "Enrico", FamilyName = "Catteneo" },
|
||||
new SampleEmailDataType() { FirstName = "Davit", FamilyName = "Badalyan" },
|
||||
new SampleEmailDataType() { FirstName = "Diego", FamilyName = "Siciliani" },
|
||||
new SampleEmailDataType() { FirstName = "Raul", FamilyName = "Razo" },
|
||||
new SampleEmailDataType() { FirstName = "Miriam", FamilyName = "Graham" },
|
||||
new SampleEmailDataType() { FirstName = "Lynne", FamilyName = "Robbins" },
|
||||
new SampleEmailDataType() { FirstName = "Lydia", FamilyName = "Holloway" },
|
||||
new SampleEmailDataType() { FirstName = "Nestor", FamilyName = "Wilke" },
|
||||
new SampleEmailDataType() { FirstName = "Patti", FamilyName = "Fernandez" },
|
||||
new SampleEmailDataType() { FirstName = "Pradeep", FamilyName = "Gupta" },
|
||||
new SampleEmailDataType() { FirstName = "Joni", FamilyName = "Sherman" },
|
||||
new SampleEmailDataType() { FirstName = "Isaiah", FamilyName = "Langer" },
|
||||
new SampleEmailDataType() { FirstName = "Irvin", FamilyName = "Sayers" },
|
||||
new SampleEmailDataType() { FirstName = "Tung", FamilyName = "Huynh" },
|
||||
};
|
||||
|
||||
private readonly List<SampleDataType> _samples = new List<SampleDataType>()
|
||||
{
|
||||
new SampleDataType() { Text = "Account", Icon = Symbol.Account },
|
||||
new SampleDataType() { Text = "Add Friend", Icon = Symbol.AddFriend },
|
||||
new SampleDataType() { Text = "Attach", Icon = Symbol.Attach },
|
||||
new SampleDataType() { Text = "Attach Camera", Icon = Symbol.AttachCamera },
|
||||
new SampleDataType() { Text = "Audio", Icon = Symbol.Audio },
|
||||
new SampleDataType() { Text = "Block Contact", Icon = Symbol.BlockContact },
|
||||
new SampleDataType() { Text = "Calculator", Icon = Symbol.Calculator },
|
||||
new SampleDataType() { Text = "Calendar", Icon = Symbol.Calendar },
|
||||
new SampleDataType() { Text = "Camera", Icon = Symbol.Camera },
|
||||
new SampleDataType() { Text = "Contact", Icon = Symbol.Contact },
|
||||
new SampleDataType() { Text = "Favorite", Icon = Symbol.Favorite },
|
||||
new SampleDataType() { Text = "Link", Icon = Symbol.Link },
|
||||
new SampleDataType() { Text = "Mail", Icon = Symbol.Mail },
|
||||
new SampleDataType() { Text = "Map", Icon = Symbol.Map },
|
||||
new SampleDataType() { Text = "Phone", Icon = Symbol.Phone },
|
||||
new SampleDataType() { Text = "Pin", Icon = Symbol.Pin },
|
||||
new SampleDataType() { Text = "Rotate", Icon = Symbol.Rotate },
|
||||
new SampleDataType() { Text = "Rotate Camera", Icon = Symbol.RotateCamera },
|
||||
new SampleDataType() { Text = "Send", Icon = Symbol.Send },
|
||||
new SampleDataType() { Text = "Tags", Icon = Symbol.Tag },
|
||||
new SampleDataType() { Text = "UnFavorite", Icon = Symbol.UnFavorite },
|
||||
new SampleDataType() { Text = "UnPin", Icon = Symbol.UnPin },
|
||||
new SampleDataType() { Text = "Zoom", Icon = Symbol.Zoom },
|
||||
new SampleDataType() { Text = "ZoomIn", Icon = Symbol.ZoomIn },
|
||||
new SampleDataType() { Text = "ZoomOut", Icon = Symbol.ZoomOut },
|
||||
};
|
||||
|
||||
private RichSuggestBox _rsb;
|
||||
private RichSuggestBox _tsb;
|
||||
private DispatcherQueue _dispatcherQueue;
|
||||
|
||||
public RichSuggestBoxPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this._dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
Loaded += (sender, e) => { this.OnXamlRendered(this); };
|
||||
}
|
||||
|
||||
public void OnXamlRendered(FrameworkElement control)
|
||||
{
|
||||
if (this._rsb != null)
|
||||
{
|
||||
this._rsb.SuggestionChosen -= this.SuggestingBox_OnSuggestionChosen;
|
||||
this._rsb.SuggestionRequested -= this.SuggestingBox_OnSuggestionRequested;
|
||||
}
|
||||
|
||||
if (this._tsb != null)
|
||||
{
|
||||
this._tsb.SuggestionChosen -= this.SuggestingBox_OnSuggestionChosen;
|
||||
this._tsb.SuggestionRequested -= this.SuggestingBox_OnSuggestionRequested;
|
||||
this._tsb.TokenPointerOver -= this.SuggestingBox_OnTokenPointerOver;
|
||||
}
|
||||
|
||||
if (control.FindChild("SuggestingBox") is RichSuggestBox rsb)
|
||||
{
|
||||
this._rsb = rsb;
|
||||
this._rsb.SuggestionChosen += this.SuggestingBox_OnSuggestionChosen;
|
||||
this._rsb.SuggestionRequested += this.SuggestingBox_OnSuggestionRequested;
|
||||
}
|
||||
|
||||
if (control.FindChild("PlainTextSuggestingBox") is RichSuggestBox tsb)
|
||||
{
|
||||
this._tsb = tsb;
|
||||
this._tsb.SuggestionChosen += this.SuggestingBox_OnSuggestionChosen;
|
||||
this._tsb.SuggestionRequested += this.SuggestingBox_OnSuggestionRequested;
|
||||
this._tsb.TokenPointerOver += this.SuggestingBox_OnTokenPointerOver;
|
||||
}
|
||||
|
||||
if (control.FindChild("TokenListView1") is ListView tls1)
|
||||
{
|
||||
tls1.ItemsSource = this._rsb?.Tokens;
|
||||
}
|
||||
|
||||
if (control.FindChild("TokenListView2") is ListView tls2)
|
||||
{
|
||||
tls2.ItemsSource = this._tsb?.Tokens;
|
||||
}
|
||||
}
|
||||
|
||||
private void SuggestingBox_OnTokenPointerOver(RichSuggestBox sender, RichSuggestTokenPointerOverEventArgs args)
|
||||
{
|
||||
var flyout = (Flyout)FlyoutBase.GetAttachedFlyout(sender);
|
||||
var pointerPosition = args.CurrentPoint.Position;
|
||||
|
||||
if (flyout?.Content is ContentPresenter cp && sender.TextDocument.Selection.Type != SelectionType.Normal &&
|
||||
(!flyout.IsOpen || cp.Content != args.Token.Item))
|
||||
{
|
||||
this._dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
cp.Content = args.Token.Item;
|
||||
flyout.ShowAt(sender, new FlyoutShowOptions
|
||||
{
|
||||
Position = pointerPosition,
|
||||
ExclusionRect = sender.GetRectFromRange(args.Range),
|
||||
ShowMode = FlyoutShowMode.TransientWithDismissOnPointerMoveAway,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void SuggestingBox_OnSuggestionChosen(RichSuggestBox sender, SuggestionChosenEventArgs args)
|
||||
{
|
||||
if (args.Prefix == "#")
|
||||
{
|
||||
args.Format.BackgroundColor = Colors.DarkOrange;
|
||||
args.Format.ForegroundColor = Colors.OrangeRed;
|
||||
args.Format.Bold = FormatEffect.On;
|
||||
args.Format.Italic = FormatEffect.On;
|
||||
args.DisplayText = ((SampleDataType)args.SelectedItem).Text;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.DisplayText = ((SampleEmailDataType)args.SelectedItem).DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
private void SuggestingBox_OnSuggestionRequested(RichSuggestBox sender, SuggestionRequestedEventArgs args)
|
||||
{
|
||||
if (args.Prefix == "#")
|
||||
{
|
||||
sender.ItemsSource =
|
||||
this._samples.Where(x => x.Text.Contains(args.QueryText, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
sender.ItemsSource =
|
||||
this._emailSamples.Where(x => x.DisplayName.Contains(args.QueryText, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<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"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<local:NameToColorConverter x:Key="NameToColorConverter"/>
|
||||
|
||||
<DataTemplate x:Key="EmailTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Border CornerRadius="9999" Background="{Binding DisplayName, Converter={StaticResource NameToColorConverter}}"
|
||||
Width="20" Height="20">
|
||||
<TextBlock Text="{Binding Initials}" Foreground="White"
|
||||
FontSize="10"
|
||||
FontWeight="Semibold"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock Text="{Binding DisplayName}" Padding="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="DataTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Symbol="{Binding Icon}" />
|
||||
<TextBlock Padding="4,0,0,0"
|
||||
Text="{Binding Text}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<local:SuggestionTemplateSelector x:Key="SuggestionTemplateSelector"
|
||||
Data="{StaticResource DataTemplate}"
|
||||
Person="{StaticResource EmailTemplate}" />
|
||||
|
||||
<DataTemplate x:Key="TokenTemplate">
|
||||
<StackPanel Margin="0,4,0,4"
|
||||
Orientation="Vertical">
|
||||
<TextBlock>
|
||||
Text: <Run Text="{Binding DisplayText}" /></TextBlock>
|
||||
<TextBlock>
|
||||
Position: <Run Text="{Binding Position}" /></TextBlock>
|
||||
<TextBlock>
|
||||
Id: <Run Text="{Binding Id}" /></TextBlock>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<Flyout x:Key="TokenSelectedFlyout">
|
||||
<ContentPresenter x:Name="FlyoutPresenter"
|
||||
ContentTemplate="{StaticResource EmailTemplate}" />
|
||||
</Flyout>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Margin="40">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<controls:RichSuggestBox x:Name="SuggestingBox"
|
||||
MaxHeight="400"
|
||||
HorizontalAlignment="Stretch"
|
||||
Header="Suggest box that supports multiple prefixes"
|
||||
ItemTemplateSelector="{StaticResource SuggestionTemplateSelector}"
|
||||
Prefixes="@#" />
|
||||
|
||||
<ListView x:Name="TokenListView1"
|
||||
Grid.Row="1"
|
||||
Margin="0,16,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource TokenTemplate}" />
|
||||
|
||||
<controls:RichSuggestBox x:Name="PlainTextSuggestingBox"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
MaxHeight="400"
|
||||
HorizontalAlignment="Stretch"
|
||||
ClipboardCopyFormat="PlainText"
|
||||
ClipboardPasteFormat="PlainText"
|
||||
DisabledFormattingAccelerators="All"
|
||||
Header="Plain text suggest box with on token pointer over flyout"
|
||||
ItemTemplate="{StaticResource EmailTemplate}"
|
||||
Prefixes="@"
|
||||
FlyoutBase.AttachedFlyout="{StaticResource TokenSelectedFlyout}" />
|
||||
|
||||
<ListView x:Name="TokenListView2"
|
||||
Grid.Row="3"
|
||||
Margin="0,16,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource TokenTemplate}" />
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,21 @@
|
|||
// 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 Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
public class SuggestionTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate Person { get; set; }
|
||||
|
||||
public DataTemplate Data { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
return item is SampleEmailDataType ? this.Person : this.Data;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.AttachedDropShadowPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- Shallow Copy in XamlOnlyPage -->
|
||||
</Page>
|
|
@ -0,0 +1,30 @@
|
|||
// 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.Toolkit.Uwp.UI;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
public sealed partial class AttachedDropShadowPage : IXamlRenderListener
|
||||
{
|
||||
public AttachedDropShadowPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void OnXamlRendered(FrameworkElement control)
|
||||
{
|
||||
// This is done as we don't have x:Bind in live xaml, so we find and attach after.
|
||||
var castToTarget = control.FindChild("ShadowTarget");
|
||||
if (castToTarget != null)
|
||||
{
|
||||
if (control.Resources.TryGetValue("CommonShadow", out var resource) && resource is AttachedDropShadow shadow)
|
||||
{
|
||||
shadow.CastTo = castToTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<ui:AttachedDropShadow x:Key="CommonShadow" Offset="4"/><!-- CastTo="{x:Bind ShadowTarget}"/>-->
|
||||
|
||||
<Style TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}">
|
||||
<Setter Property="ui:Effects.Shadow" Value="{StaticResource CommonShadow}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
<!-- We set a solid color for the background button otherwise the shadow bleeds through as
|
||||
the new style is transparent. See AttachedCardShadow for proper element clipping. -->
|
||||
<Setter Property="Background" Value="Red"/>
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<Grid>
|
||||
<!-- The ShadowTarget Grid here is a *sibling* element behind where our elements which will cast
|
||||
shadows are located, this is important as otherwise if we used a parent element the
|
||||
shadows would appear on top of our elements instead!
|
||||
It is also placed within the ScrollViewer here so shadows move with their elements. -->
|
||||
<Grid x:Name="ShadowTarget"/>
|
||||
<StackPanel Spacing="32" VerticalAlignment="Center">
|
||||
<!-- All buttons on this page have the shadow from the common style!
|
||||
The Shadow definition is Shared! -->
|
||||
<Button Content="I Have a Shadow!"/>
|
||||
<!-- Can apply the same shadow to any type of element! -->
|
||||
<Image ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Height="100" Width="100"
|
||||
Source="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
<!-- You can still apply a Shadow directly and even use binding with it to manipulate at runtime! -->
|
||||
<Rectangle RadiusX="32" RadiusY="32"
|
||||
Height="100" Width="100"
|
||||
Stroke="Blue" StrokeThickness="1">
|
||||
<Rectangle.Fill>
|
||||
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
</Rectangle.Fill>
|
||||
<ui:Effects.Shadow>
|
||||
<ui:AttachedDropShadow BlurRadius="@[BlurRadius:DoubleSlider:8.0:0.0-10.0]"
|
||||
CornerRadius="32"
|
||||
Color="@[Color:Brush:Black]"
|
||||
Offset="@[Offset:Vector3:4,4]"
|
||||
Opacity="@[Opacity:DoubleSlider:1.0:0.0-1.0]"
|
||||
CastTo="{Binding ElementName=ShadowTarget}"/>
|
||||
</ui:Effects.Shadow>
|
||||
</Rectangle>
|
||||
<!-- This particular scenario of attaching directly to a raw element is easier than the Win2D equivelent. -->
|
||||
<Border Height="100" Width="100"
|
||||
CornerRadius="32"
|
||||
BorderBrush="White" BorderThickness="1">
|
||||
<Border.Background>
|
||||
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
</Border.Background>
|
||||
<ui:Effects.Shadow>
|
||||
<ui:AttachedDropShadow CornerRadius="32"
|
||||
Offset="4,4"
|
||||
CastTo="{Binding ElementName=ShadowTarget}"/>
|
||||
</ui:Effects.Shadow>
|
||||
</Border>
|
||||
<!-- Note how even though this element is transparent, the shadow still shows through,
|
||||
to have this not occur use the AttachedCardShadow. -->
|
||||
<Rectangle ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Fill="#80FF0000"
|
||||
RadiusX="4" RadiusY="4"
|
||||
Width="200" Height="100"/>
|
||||
<!-- This is the same behavior as the old DropShadowPanel where the shadow bleeds through and
|
||||
the Shadow opacity is tied to the Rectangle itself -->
|
||||
<controls:DropShadowPanel xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
OffsetX="4" OffsetY="4"
|
||||
BlurRadius="12"
|
||||
HorizontalAlignment="Center">
|
||||
<Rectangle Fill="#80FF0000"
|
||||
RadiusX="4" RadiusY="4"
|
||||
Width="200" Height="100"/>
|
||||
</controls:DropShadowPanel>
|
||||
<Button Content="I Also have a Shadow!"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Page>
|
|
@ -0,0 +1,80 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
|
||||
xmlns:media="using:Microsoft.Toolkit.Uwp.UI.Media"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- TODO: Animation-->
|
||||
<Page.Resources>
|
||||
<media:AttachedCardShadow x:Key="CommonShadow" Offset="4"/>
|
||||
|
||||
<Style TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}">
|
||||
<Setter Property="ui:Effects.Shadow" Value="{StaticResource CommonShadow}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Spacing="32" VerticalAlignment="Center">
|
||||
<!-- All buttons on this page have the shadow from the common style!
|
||||
The Shadow definition is Shared! -->
|
||||
<Button Content="I Have a Shadow!"/>
|
||||
<!-- Can apply the same shadow to any type of element! -->
|
||||
<Image ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Height="100" Width="100"
|
||||
Source="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
<!-- You can still apply a Shadow directly and even use binding with it to manipulate at runtime! -->
|
||||
<Rectangle RadiusX="32" RadiusY="32"
|
||||
Height="100" Width="100"
|
||||
Stroke="Blue" StrokeThickness="1">
|
||||
<Rectangle.Fill>
|
||||
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
</Rectangle.Fill>
|
||||
<ui:Effects.Shadow>
|
||||
<media:AttachedCardShadow BlurRadius="@[BlurRadius:DoubleSlider:8.0:0.0-10.0]"
|
||||
CornerRadius="32"
|
||||
Color="@[Color:Brush:Black]"
|
||||
Offset="@[Offset:Vector3:4,4]"
|
||||
Opacity="@[Opacity:DoubleSlider:1.0:0.0-1.0]"/>
|
||||
</ui:Effects.Shadow>
|
||||
</Rectangle>
|
||||
<!-- If you want to apply a shadow directly in your visual tree to an untemplated element
|
||||
You need to at least have a layer of depth as seen in this next example. -->
|
||||
<Border Height="100" Width="100">
|
||||
<Border CornerRadius="32"
|
||||
BorderBrush="White" BorderThickness="1">
|
||||
<Border.Background>
|
||||
<ImageBrush ImageSource="ms-appx:///Assets/Photos/Owl.jpg"/>
|
||||
</Border.Background>
|
||||
</Border>
|
||||
<!-- We need to put the Shadow on a parent element here as otherwise the
|
||||
rounding of the border of the image above clips the shadow itself.
|
||||
This is easier to perform with the Composition only based shadow as the
|
||||
Shadow is projected to another element. -->
|
||||
<ui:Effects.Shadow>
|
||||
<media:AttachedCardShadow CornerRadius="32"
|
||||
Offset="4,4"/>
|
||||
</ui:Effects.Shadow>
|
||||
</Border>
|
||||
<!-- Note how even though this element is transparent, the shadow only shows on its edges still! -->
|
||||
<Rectangle ui:Effects.Shadow="{StaticResource CommonShadow}"
|
||||
Fill="#80FF0000"
|
||||
RadiusX="4" RadiusY="4"
|
||||
Width="200" Height="100"/>
|
||||
<!-- Compared to the old DropShadowPanel where the shadow bleeds through and
|
||||
the Shadow opacity is tied to the Rectangle itself -->
|
||||
<controls:DropShadowPanel xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
OffsetX="4" OffsetY="4"
|
||||
BlurRadius="12"
|
||||
HorizontalAlignment="Center">
|
||||
<Rectangle Fill="#80FF0000"
|
||||
RadiusX="4" RadiusY="4"
|
||||
Width="200" Height="100"/>
|
||||
</controls:DropShadowPanel>
|
||||
<Button Content="I Also have a Shadow!"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Page>
|
До Ширина: | Высота: | Размер: 2.1 KiB После Ширина: | Высота: | Размер: 2.1 KiB |
|
@ -88,7 +88,7 @@
|
|||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
IsMasked="@[Is Masked]">
|
||||
<Image Width="200" Source="/SamplePages/DropShadowPanel/Unicorn.png" Stretch="Uniform" />
|
||||
<Image Width="200" Source="/SamplePages/Shadows/Unicorn.png" Stretch="Uniform" />
|
||||
</controls:DropShadowPanel>
|
||||
<controls:DropShadowPanel BlurRadius="@[BlurRadius]"
|
||||
ShadowOpacity="@[Opacity]"
|
||||
|
@ -98,7 +98,7 @@
|
|||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
IsMasked="@[Is Masked]">
|
||||
<Image Width="200" Source="/SamplePages/DropShadowPanel/Trex.png" Stretch="Uniform" />
|
||||
<Image Width="200" Source="/SamplePages/Shadows/Trex.png" Stretch="Uniform" />
|
||||
</controls:DropShadowPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
До Ширина: | Высота: | Размер: 5.6 KiB После Ширина: | Высота: | Размер: 5.6 KiB |
До Ширина: | Высота: | Размер: 6.1 KiB После Ширина: | Высота: | Размер: 6.1 KiB |
|
@ -14,7 +14,10 @@
|
|||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<controls:TabbedCommandBar>
|
||||
<controls:TabbedCommandBar> <!-- IsBackButtonVisible="Visible" -->
|
||||
<controls:TabbedCommandBar.AutoSuggestBox>
|
||||
<AutoSuggestBox QueryIcon="Find" AutomationProperties.Name="Search" />
|
||||
</controls:TabbedCommandBar.AutoSuggestBox>
|
||||
<controls:TabbedCommandBar.PaneFooter>
|
||||
<CommandBar Background="Transparent" DefaultLabelPosition="Right">
|
||||
<AppBarButton Label="Share" Icon="Share"/>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:triggers="using:Microsoft.Toolkit.Uwp.UI.Triggers"
|
||||
mc:Ignorable="d">
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState>
|
||||
<VisualState.StateTriggers>
|
||||
<triggers:ControlSizeTrigger
|
||||
TargetElement="{Binding ElementName=ParentGrid}"
|
||||
MinWidth="400"
|
||||
MaxWidth="500"/>
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ResizingText.FontSize" Value="20"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<StackPanel VerticalAlignment="Center" Width="500">
|
||||
<Grid
|
||||
x:Name="ParentGrid"
|
||||
Width="{Binding Value, ElementName=Slider, Mode=OneWay}"
|
||||
Height="50"
|
||||
Background="Blue"/>
|
||||
<TextBlock
|
||||
x:Name="ResizingText"
|
||||
FontSize="12"
|
||||
Text="Windows Community Toolkit"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Slider
|
||||
x:Name="Slider"
|
||||
Minimum="0"
|
||||
Maximum="500" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -31,7 +31,9 @@
|
|||
<triggers:RegexStateTrigger x:Key="RegexStateTrigger" />
|
||||
<triggers:UserHandPreferenceStateTrigger x:Key="UserHandPreferenceStateTrigger" />
|
||||
<triggers:UserInteractionModeStateTrigger x:Key="UserInteractionModeStateTrigger" />
|
||||
<triggers:ControlSizeTrigger x:Key="ControlSizeTrigger" />
|
||||
<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" />
|
||||
|
@ -47,6 +49,11 @@
|
|||
<controls:Case IsDefault="True" />
|
||||
</controls:CaseCollection>
|
||||
</controls:SwitchPresenter>
|
||||
<controls:ConstrainedBox x:Key="ConstrainedBoxControl" />
|
||||
<media:AttachedCardShadow x:Key="AttachedShadow" />
|
||||
<ui:AttachedDropShadow x:Key="AttachedDropShadow" />
|
||||
<controls:DropShadowPanel x:Key="DropShadowPanel"
|
||||
ui:Effects.Shadow="{StaticResource AttachedShadow}" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
|
@ -61,6 +68,10 @@
|
|||
<ani:SaturationEffectAnimation />
|
||||
<ani:AnimationScope />
|
||||
<ani:ExposureEffectAnimation />
|
||||
<ani:BlurRadiusDropShadowAnimation />
|
||||
<ani:ColorDropShadowAnimation />
|
||||
<ani:OffsetDropShadowAnimation />
|
||||
<ani:OpacityDropShadowAnimation />
|
||||
</ani:AnimationSet>
|
||||
</ani:Explicit.Animations>
|
||||
<media:UIElementExtensions.VisualFactory>
|
||||
|
|
|
@ -126,6 +126,8 @@
|
|||
{
|
||||
"Name": "RadialProgressBar",
|
||||
"Type": "RadialProgressBarPage",
|
||||
"BadgeUpdateVersionRequired": "DEPRECATED",
|
||||
"DeprecatedWarning": "Please migrate to the ProgressRing control from WinUI with IsIndeterminate set to false, this control will be removed in a future release. https://aka.ms/winui",
|
||||
"Subcategory": "Status and Info",
|
||||
"About": "The radial progress bar displays progress as a circle getting filled.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Core/RadialProgressBar",
|
||||
|
@ -183,15 +185,36 @@
|
|||
"Icon": "/SamplePages/GridSplitter/GridSplitter.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/GridSplitter.md"
|
||||
},
|
||||
{
|
||||
"Name": "AttachedDropShadow (Composition)",
|
||||
"Type": "AttachedDropShadowPage",
|
||||
"Subcategory": "Media",
|
||||
"About": "An AttachedDropShadow allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI/Shadows",
|
||||
"XamlCodeFile": "/SamplePages/Shadows/AttachedShadowCompositionXaml.bind",
|
||||
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md"
|
||||
},
|
||||
{
|
||||
"Name": "AttachedCardShadow (Win2D)",
|
||||
"Subcategory": "Media",
|
||||
"About": "An AttachedCardShadow allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Media/Shadows",
|
||||
"XamlCodeFile": "/SamplePages/Shadows/AttachedShadowWin2DXaml.bind",
|
||||
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md",
|
||||
"BadgeUpdateVersionRequired": "May 2019 update required",
|
||||
"ApiCheck": "Windows.UI.Composition.CompositionVisualSurface"
|
||||
},
|
||||
{
|
||||
"Name": "DropShadowPanel",
|
||||
"Type": "DropShadowPanelPage",
|
||||
"Subcategory": "Media",
|
||||
"About": "DropShadowPanel contol allows the creation of a DropShadow for any Xaml FrameworkElement in markup.",
|
||||
"BadgeUpdateVersionRequired": "DEPRECATED",
|
||||
"DeprecatedWarning": "This control will be removed in a future major release. Please use the AttachedDropShadow or AttachedCardShadow extensions instead.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel",
|
||||
"XamlCodeFile": "DropShadowPanelXaml.bind",
|
||||
"Icon": "/SamplePages/DropShadowPanel/DropShadowPanel.png",
|
||||
"BadgeUpdateVersionRequired": "Anniversary Update required",
|
||||
"XamlCodeFile": "/SamplePages/Shadows/DropShadowPanelXaml.bind",
|
||||
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md"
|
||||
},
|
||||
{
|
||||
|
@ -359,7 +382,6 @@
|
|||
"XamlCodeFile": "InfiniteCanvas.bind",
|
||||
"Icon": "/SamplePages/InfiniteCanvas/InfiniteCanvas.png",
|
||||
"ApiCheck": "Windows.UI.Xaml.Controls.ColorPicker",
|
||||
"BadgeUpdateVersionRequired": "Fall Creators Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/InfiniteCanvas.md"
|
||||
},
|
||||
{
|
||||
|
@ -475,6 +497,26 @@
|
|||
"XamlCodeFile": "/SamplePages/Primitives/SwitchPresenter.bind",
|
||||
"Icon": "/SamplePages/Primitives/SwitchPresenter.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/SwitchPresenter.md"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"XamlCodeFile": "/SamplePages/Primitives/ConstrainedBox.bind",
|
||||
"Icon": "/SamplePages/Primitives/ConstrainedBox.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/ConstrainedBox.md"
|
||||
},
|
||||
{
|
||||
"Name": "RichSuggestBox",
|
||||
"Type": "RichSuggestBoxPage",
|
||||
"Subcategory": "Input",
|
||||
"About": "A text input control that makes suggestions and keeps track of data token items in a rich document.",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox",
|
||||
"CodeFile": "RichSuggestBoxCode.bind",
|
||||
"XamlCodeFile": "RichSuggestBoxXaml.bind",
|
||||
"Icon": "/SamplePages/RichSuggestBox/RichSuggestBox.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/RichSuggestBox.md"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -550,7 +592,6 @@
|
|||
"CodeFile": "/SamplePages/Animations/Behaviors/BlurBehaviorCode.bind",
|
||||
"XamlCodeFile": "/SamplePages/Animations/Behaviors/BlurBehaviorXaml.bind",
|
||||
"Icon": "/SamplePages/Animations/Behaviors/BlurBehavior.png",
|
||||
"BadgeUpdateVersionRequired": "Anniversary Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/brushes/PipelineVisualFactory.md"
|
||||
},
|
||||
{
|
||||
|
@ -561,7 +602,6 @@
|
|||
"CodeFile": "/SamplePages/Animations/Behaviors/SaturationBehaviorCode.bind",
|
||||
"XamlCodeFile": "/SamplePages/Animations/Behaviors/SaturationBehaviorXaml.bind",
|
||||
"Icon": "/SamplePages/Animations/Behaviors/SaturationBehavior.png",
|
||||
"BadgeUpdateVersionRequired": "Anniversary Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/brushes/PipelineVisualFactory.md"
|
||||
},
|
||||
{
|
||||
|
@ -591,7 +631,6 @@
|
|||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Animations",
|
||||
"XamlCodeFile": "ItemsReorderAnimation.bind",
|
||||
"Icon": "/SamplePages/ItemsReorderAnimation/ItemsReorderAnimation.png",
|
||||
"BadgeUpdateVersionRequired": "Anniversary Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/ItemsReorderAnimation.md"
|
||||
},
|
||||
{
|
||||
|
@ -611,7 +650,6 @@
|
|||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Animations",
|
||||
"Icon": "/SamplePages/Implicit Animations/ImplicitAnimations.png",
|
||||
"XamlCodeFile": "ImplicitAnimationsCode.bind",
|
||||
"BadgeUpdateVersionRequired": "Creators Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/ImplicitAnimationSet.md"
|
||||
},
|
||||
{
|
||||
|
@ -623,7 +661,15 @@
|
|||
"XamlCodeFile": "ConnectedAnimationsCode.bind",
|
||||
"DisableXamlEditorRendering": true,
|
||||
"Icon": "/SamplePages/Connected Animations/ConnectedAnimations.png",
|
||||
"BadgeUpdateVersionRequired": "Creators Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/ConnectedAnimations.md"
|
||||
},
|
||||
{
|
||||
"Name": "Shadow Animations",
|
||||
"Subcategory": "Effect",
|
||||
"About": "Example of animating an Attached Shadow",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows",
|
||||
"XamlCodeFile": "/SamplePages/Animations/Shadows/AnimatedCardShadowXaml.bind",
|
||||
"Icon": "/SamplePages/Shadows/DropShadowPanel.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/ConnectedAnimations.md"
|
||||
}
|
||||
]
|
||||
|
@ -706,8 +752,7 @@
|
|||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.Connectivity/BluetoothLEHelper",
|
||||
"CodeFile": "BluetoothLEHelperCode.bind",
|
||||
"Icon": "/SamplePages/BluetoothLEHelper/BluetoothLEHelper.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/BluetoothLEHelper.md",
|
||||
"BadgeUpdateVersionRequired": "Creators Update required"
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/BluetoothLEHelper.md"
|
||||
},
|
||||
{
|
||||
"Name": "SystemInformation",
|
||||
|
@ -830,6 +875,15 @@
|
|||
"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/Microsoft.Toolkit.Uwp.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",
|
||||
|
@ -902,6 +956,15 @@
|
|||
"Icon": "/Assets/Helpers.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/Triggers.md"
|
||||
},
|
||||
{
|
||||
"Name": "ControlSizeTrigger",
|
||||
"Subcategory": "State Triggers",
|
||||
"About": "Enables a state if the target control meets the specified size",
|
||||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs",
|
||||
"XamlCodeFile": "/SamplePages/Triggers/ControlSizeTrigger.bind",
|
||||
"Icon": "/Assets/Helpers.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/Triggers.md"
|
||||
},
|
||||
{
|
||||
"Name": "IsEqualStateTrigger",
|
||||
"Subcategory": "State Triggers",
|
||||
|
@ -1152,7 +1215,6 @@
|
|||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/TextBox/TextBoxExtensions.SurfaceDial.cs",
|
||||
"XamlCodeFile": "/SamplePages/SurfaceDialTextbox/SurfaceDialTextboxCode.bind",
|
||||
"Icon": "/SamplePages/SurfaceDialTextbox/SurfaceDialTextbox.png",
|
||||
"BadgeUpdateVersionRequired": "Anniversary Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/extensions/SurfaceDialTextboxHelper.md"
|
||||
},
|
||||
{
|
||||
|
@ -1162,7 +1224,6 @@
|
|||
"CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/VisualExtensions.cs",
|
||||
"XamlCodeFile": "VisualExtensionsCode.bind",
|
||||
"Icon": "/SamplePages/Visual Extensions/VisualExtensions.png",
|
||||
"BadgeUpdateVersionRequired": "Creators Update required",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/extensions/VisualExtensions.md"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Composition;
|
||||
|
||||
|
@ -283,9 +284,10 @@ namespace Microsoft.Toolkit.Uwp.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 Microsoft.Toolkit.Uwp.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>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>uap10.0.17763</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Toolkit.Uwp.UI.Behaviors")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.Toolkit.Uwp.UI.Media")]
|
|
@ -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,112 @@
|
|||
// 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;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using static Microsoft.Toolkit.Uwp.UI.Animations.AnimationExtensions;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom animation targeting a property on an <see cref="IAttachedShadow"/> instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">
|
||||
/// The type to use for the public <see cref="Animation{TValue,TKeyFrame}.To"/> and <see cref="Animation{TValue,TKeyFrame}.From"/>
|
||||
/// properties. This can differ from <typeparamref name="TKeyFrame"/> to facilitate XAML parsing.
|
||||
/// </typeparam>
|
||||
/// <typeparam name="TKeyFrame">The actual type of keyframe values in use.</typeparam>
|
||||
public abstract class ShadowAnimation<TValue, TKeyFrame> : Animation<TValue, TKeyFrame>, IAttachedTimeline
|
||||
where TKeyFrame : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the linked <see cref="IAttachedShadow"/> instance to animate.
|
||||
/// </summary>
|
||||
public IAttachedShadow? Target
|
||||
{
|
||||
get => (IAttachedShadow?)GetValue(TargetProperty);
|
||||
set => SetValue(TargetProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <seealso cref="Target"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
|
||||
nameof(Target),
|
||||
typeof(IAttachedShadow),
|
||||
typeof(ShadowAnimation<TValue, TKeyFrame>),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeSpan? delayHint, TimeSpan? durationHint, EasingType? easingTypeHint, EasingMode? easingModeHint)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AnimationBuilder AppendToBuilder(AnimationBuilder builder, UIElement parent, TimeSpan? delayHint = null, TimeSpan? durationHint = null, EasingType? easingTypeHint = null, EasingMode? easingModeHint = null)
|
||||
{
|
||||
if (ExplicitTarget is not string explicitTarget)
|
||||
{
|
||||
static AnimationBuilder ThrowArgumentNullException()
|
||||
{
|
||||
throw new ArgumentNullException(
|
||||
"The target shadow cannot be animated at this time.");
|
||||
}
|
||||
|
||||
return ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
if (Target is IAttachedShadow allShadows)
|
||||
{
|
||||
// in this case we'll animate all the shadows being used.
|
||||
foreach (var context in allShadows.EnumerateElementContexts()) //// TODO: Find better way!!!
|
||||
{
|
||||
NormalizedKeyFrameAnimationBuilder<TKeyFrame>.Composition keyFrameBuilder = new(
|
||||
explicitTarget,
|
||||
Delay ?? delayHint ?? DefaultDelay,
|
||||
Duration ?? durationHint ?? DefaultDuration,
|
||||
Repeat,
|
||||
DelayBehavior);
|
||||
|
||||
AppendToBuilder(keyFrameBuilder, easingTypeHint, easingModeHint);
|
||||
|
||||
CompositionAnimation animation = keyFrameBuilder.GetAnimation(context.Shadow, out _);
|
||||
|
||||
builder.ExternalAnimation(context.Shadow, animation);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
else
|
||||
{
|
||||
var shadowBase = Effects.GetShadow(parent as FrameworkElement);
|
||||
if (shadowBase == null)
|
||||
{
|
||||
static AnimationBuilder ThrowArgumentNullException() => throw new ArgumentNullException("The target's shadow is null, make sure to set the Target property to an element with a Shadow");
|
||||
|
||||
return ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
var shadow = shadowBase.GetElementContext((FrameworkElement)parent).Shadow;
|
||||
|
||||
NormalizedKeyFrameAnimationBuilder<TKeyFrame>.Composition keyFrameBuilder = new(
|
||||
explicitTarget,
|
||||
Delay ?? delayHint ?? DefaultDelay,
|
||||
Duration ?? durationHint ?? DefaultDuration,
|
||||
Repeat,
|
||||
DelayBehavior);
|
||||
|
||||
AppendToBuilder(keyFrameBuilder, easingTypeHint, easingModeHint);
|
||||
|
||||
CompositionAnimation animation = keyFrameBuilder.GetAnimation(shadow, out _);
|
||||
|
||||
return builder.ExternalAnimation(shadow, animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -119,7 +119,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
foreach (object node in this)
|
||||
{
|
||||
if (node is ITimeline timeline)
|
||||
if (node is IAttachedTimeline attachedTimeline)
|
||||
{
|
||||
var builder = AnimationBuilder.Create();
|
||||
|
||||
attachedTimeline.AppendToBuilder(builder, element);
|
||||
|
||||
await builder.StartAsync(element, token);
|
||||
}
|
||||
else if (node is ITimeline timeline)
|
||||
{
|
||||
var builder = AnimationBuilder.Create();
|
||||
|
||||
|
@ -166,6 +174,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
{
|
||||
switch (node)
|
||||
{
|
||||
case IAttachedTimeline attachedTimeline:
|
||||
builder = attachedTimeline.AppendToBuilder(builder, element);
|
||||
break;
|
||||
case ITimeline timeline:
|
||||
builder = timeline.AppendToBuilder(builder);
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// 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;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface representing a XAML model for a custom animation that requires a specific parent <see cref="UIElement"/> context.
|
||||
/// </summary>
|
||||
public interface IAttachedTimeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Appends the current animation to a target <see cref="AnimationBuilder"/> instance.
|
||||
/// This method is used when the current <see cref="ITimeline"/> instance is explicitly triggered.
|
||||
/// </summary>
|
||||
/// <param name="builder">The target <see cref="AnimationBuilder"/> instance to schedule the animation on.</param>
|
||||
/// <param name="parent">The parent <see cref="UIElement"/> this animation will be started on.</param>
|
||||
/// <param name="delayHint">A hint for the animation delay, if present.</param>
|
||||
/// <param name="durationHint">A hint for the animation duration, if present.</param>
|
||||
/// <param name="easingTypeHint">A hint for the easing type, if present.</param>
|
||||
/// <param name="easingModeHint">A hint for the easing mode, if present.</param>
|
||||
/// <returns>The same <see cref="AnimationBuilder"/> instance as <paramref name="builder"/>.</returns>
|
||||
AnimationBuilder AppendToBuilder(
|
||||
AnimationBuilder builder,
|
||||
UIElement parent,
|
||||
TimeSpan? delayHint = null,
|
||||
TimeSpan? durationHint = null,
|
||||
EasingType? easingTypeHint = null,
|
||||
EasingMode? easingModeHint = null);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
public interface ITimeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Appens the current animation to a target <see cref="AnimationBuilder"/> instance.
|
||||
/// Appends the current animation to a target <see cref="AnimationBuilder"/> instance.
|
||||
/// This method is used when the current <see cref="ITimeline"/> instance is explicitly triggered.
|
||||
/// </summary>
|
||||
/// <param name="builder">The target <see cref="AnimationBuilder"/> instance to schedule the animation on.</param>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Composition;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
{
|
||||
/// <summary>
|
||||
/// A blur radius animation working on the composition layer.
|
||||
/// </summary>
|
||||
public sealed class BlurRadiusDropShadowAnimation : ShadowAnimation<double?, double>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override string ExplicitTarget => nameof(DropShadow.BlurRadius);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override (double?, double?) GetParsedValues()
|
||||
{
|
||||
return (To, From);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// 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 Windows.UI;
|
||||
using Windows.UI.Composition;
|
||||
|
||||
#pragma warning disable CS0419
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom <see cref="Color"/> animation on a <see cref="DropShadow"/>.
|
||||
/// </summary>
|
||||
public sealed class ColorDropShadowAnimation : ShadowAnimation<Color?, Color>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override string ExplicitTarget => nameof(DropShadow.Color);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override (Color?, Color?) GetParsedValues()
|
||||
{
|
||||
return (To, From);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
||||
using System.Numerics;
|
||||
using Windows.UI.Composition;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
{
|
||||
/// <summary>
|
||||
/// An offset animation working on the composition layer.
|
||||
/// </summary>
|
||||
public sealed class OffsetDropShadowAnimation : ShadowAnimation<string, Vector3>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override string ExplicitTarget => nameof(DropShadow.Offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override (Vector3?, Vector3?) GetParsedValues()
|
||||
{
|
||||
return (To?.ToVector3(), From?.ToVector3());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Composition;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Animations
|
||||
{
|
||||
/// <summary>
|
||||
/// An opacity animation working on the composition layer.
|
||||
/// </summary>
|
||||
public sealed class OpacityDropShadowAnimation : ShadowAnimation<double?, double>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override string ExplicitTarget => nameof(DropShadow.Opacity);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override (double?, double?) GetParsedValues()
|
||||
{
|
||||
return (To, From);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,13 +58,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
|
|||
ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
UIElement parent = null;
|
||||
|
||||
if (TargetObject is not null)
|
||||
{
|
||||
Animation.Start(TargetObject);
|
||||
}
|
||||
else if (Animation.ParentReference?.TryGetTarget(out parent) == true) //// TODO: Tidy... apply same pattern to Activities?
|
||||
{
|
||||
Animation.Start(parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Animation.Start();
|
||||
Animation.Start(sender as UIElement);
|
||||
}
|
||||
|
||||
return null!;
|
||||
|
|
|
@ -58,13 +58,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
|
|||
ThrowArgumentNullException();
|
||||
}
|
||||
|
||||
UIElement parent = null;
|
||||
|
||||
if (TargetObject is not null)
|
||||
{
|
||||
Animation.Stop(TargetObject);
|
||||
}
|
||||
else if (Animation.ParentReference?.TryGetTarget(out parent) == true) //// TODO: Tidy...
|
||||
{
|
||||
Animation.Stop(parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Animation.Stop();
|
||||
Animation.Stop(sender as UIElement);
|
||||
}
|
||||
|
||||
return null!;
|
||||
|
|
|
@ -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.Xaml.Interactivity;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// The <see cref="DropShadowPanel"/> control allows the creation of a DropShadow for any Xaml FrameworkElement in markup
|
||||
/// making it easier to add shadows to Xaml without having to directly drop down to Windows.UI.Composition APIs.
|
||||
/// </summary>
|
||||
[Obsolete("DropShadowPanel will be removed in a future release, please use the AttachedDropShadow or AttachedCardShadow implementations instead.")]
|
||||
[TemplatePart(Name = PartShadow, Type = typeof(Border))]
|
||||
public partial class DropShadowPanel : ContentControl
|
||||
{
|
||||
|
@ -170,9 +171,16 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
// We check for IAlphaMaskProvider first, to ensure that we use the custom
|
||||
// alpha mask even if Content happens to extend any of the other classes
|
||||
if (Content is IAlphaMaskProvider maskedControl)
|
||||
{
|
||||
if (maskedControl.WaitUntilLoaded && maskedControl is FrameworkElement element && !element.IsLoaded)
|
||||
{
|
||||
element.Loaded += CustomMaskedElement_Loaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = maskedControl.GetAlphaMask();
|
||||
}
|
||||
}
|
||||
else if (Content is Image)
|
||||
{
|
||||
mask = ((Image)Content).GetAlphaMask();
|
||||
|
@ -185,17 +193,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
{
|
||||
mask = ((TextBlock)Content).GetAlphaMask();
|
||||
}
|
||||
else if (Content is ImageExBase imageExBase)
|
||||
{
|
||||
imageExBase.ImageExInitialized += ImageExInitialized;
|
||||
|
||||
if (imageExBase.IsInitialized)
|
||||
{
|
||||
imageExBase.ImageExInitialized -= ImageExInitialized;
|
||||
|
||||
mask = ((ImageExBase)Content).GetAlphaMask();
|
||||
}
|
||||
}
|
||||
|
||||
_dropShadow.Mask = mask;
|
||||
}
|
||||
|
@ -205,15 +202,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
}
|
||||
}
|
||||
|
||||
private void ImageExInitialized(object sender, EventArgs e)
|
||||
private void CustomMaskedElement_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var imageExBase = (ImageExBase)Content;
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
element.Loaded -= CustomMaskedElement_Loaded;
|
||||
|
||||
imageExBase.ImageExInitialized -= ImageExInitialized;
|
||||
|
||||
CompositionBrush mask = ((ImageExBase)Content).GetAlphaMask();
|
||||
|
||||
_dropShadow.Mask = mask;
|
||||
_dropShadow.Mask = ((IAlphaMaskProvider)element).GetAlphaMask();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateShadowOffset(float x, float y, float z)
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
[TemplateVisualState(Name = UnloadedState, GroupName = CommonGroup)]
|
||||
[TemplateVisualState(Name = FailedState, GroupName = CommonGroup)]
|
||||
[TemplatePart(Name = PartImage, Type = typeof(object))]
|
||||
public abstract partial class ImageExBase : Control
|
||||
public abstract partial class ImageExBase : Control, IAlphaMaskProvider
|
||||
{
|
||||
private bool _isInViewport;
|
||||
|
||||
|
@ -58,6 +58,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// </summary>
|
||||
protected object Image { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool WaitUntilLoaded => true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageExBase"/> class.
|
||||
/// </summary>
|
||||
|
|
|
@ -6,8 +6,9 @@ using System.Linq;
|
|||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Markup;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using NavigationView = Microsoft.UI.Xaml.Controls.NavigationView;
|
||||
using NavigationViewSelectionChangedEventArgs = Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
|
@ -36,8 +37,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
public TabbedCommandBar()
|
||||
{
|
||||
DefaultStyleKey = typeof(TabbedCommandBar);
|
||||
DefaultStyleResourceUri = new System.Uri("ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Core/Themes/Generic.xaml");
|
||||
|
||||
SelectionChanged += SelectedItemChanged;
|
||||
Loaded += TabbedCommandBar_Loaded;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -55,6 +58,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
_tabbedCommandBarContentBorder = GetTemplateChild("PART_TabbedCommandBarContentBorder") as Border;
|
||||
_tabChangedStoryboard = GetTemplateChild("TabChangedStoryboard") as Storyboard;
|
||||
|
||||
// TODO: We could maybe optimize and use a lower-level Loaded event for what's hosting the MenuItems
|
||||
// to set SelectedItem, but then we may have to pull in another template part, so think we're OK
|
||||
// to do the Loaded event at the top level.
|
||||
}
|
||||
|
||||
private void TabbedCommandBar_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// We need to select the item after the template is realized, otherwise the SelectedItem's
|
||||
// DataTemplate bindings don't properly navigate the visual tree.
|
||||
SelectedItem = MenuItems.FirstOrDefault();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:contract4Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,4)"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:Windows10version1903="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 8)">
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml" />
|
||||
|
@ -16,239 +17,265 @@
|
|||
<Setter Property="PaneDisplayMode" Value="Top" />
|
||||
<Setter Property="PaneToggleButtonStyle" Value="{StaticResource PaneToggleButtonStyle}" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="IsBackButtonVisible" Value="Collapsed" />
|
||||
<Setter Property="IsSettingsVisible" Value="False" />
|
||||
<Setter Property="CompactPaneLength" Value="{ThemeResource NavigationViewCompactPaneLength}" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemChromeMediumLowColor}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:TabbedCommandBar">
|
||||
<Grid>
|
||||
<Grid x:Name="RootGrid">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AutoSuggestGroup">
|
||||
<VisualState x:Name="AutoSuggestBoxVisible" />
|
||||
<VisualState x:Name="AutoSuggestBoxCollapsed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TopPaneAutoSuggestArea.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="BackButtonGroup">
|
||||
<VisualState x:Name="BackButtonVisible" />
|
||||
<VisualState x:Name="BackButtonCollapsed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="BackButtonPlaceholderOnTopNav.Width" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- Button grid -->
|
||||
<Grid x:Name="PaneToggleButtonGrid"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Canvas.ZIndex="100">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid x:Name="RootGrid"
|
||||
Background="{TemplateBinding Background}">
|
||||
<Grid>
|
||||
<Grid x:Name="TogglePaneTopPadding"
|
||||
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.TopPadding}" />
|
||||
|
||||
<Grid x:Name="ButtonHolderGrid"
|
||||
Grid.Row="1"
|
||||
Margin="{ThemeResource NavigationViewButtonHolderGridMargin}">
|
||||
<Button x:Name="NavigationViewBackButton"
|
||||
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.SmallerPaneToggleButtonWidth}"
|
||||
VerticalAlignment="Top"
|
||||
IsEnabled="{TemplateBinding IsBackEnabled}"
|
||||
Style="{StaticResource NavigationBackButtonNormalStyle}"
|
||||
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.BackButtonVisibility}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="NavigationViewBackButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button x:Name="NavigationViewCloseButton"
|
||||
VerticalAlignment="Top"
|
||||
Style="{StaticResource NavigationBackButtonNormalStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="NavigationViewCloseButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button x:Name="TogglePaneButton"
|
||||
MinWidth="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.SmallerPaneToggleButtonWidth}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
AutomationProperties.LandmarkType="Navigation"
|
||||
FocusVisualMargin="0"
|
||||
Style="{TemplateBinding PaneToggleButtonStyle}"
|
||||
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.PaneToggleButtonVisibility}">
|
||||
<TextBlock x:Name="PaneTitleTextBlock"
|
||||
Grid.Column="0"
|
||||
Margin="0,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource NavigationViewItemHeaderTextStyle}"
|
||||
Text="{TemplateBinding PaneTitle}" />
|
||||
</Button>
|
||||
|
||||
<Grid x:Name="PaneTitleHolder"
|
||||
Height="40"
|
||||
Visibility="Collapsed">
|
||||
<ContentControl x:Name="PaneTitlePresenter"
|
||||
Margin="{ThemeResource NavigationViewPaneTitlePresenterMargin}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Content layouts -->
|
||||
<!-- DisplayMode top -->
|
||||
<StackPanel x:Name="TopNavArea"
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Background="{TemplateBinding Background}"
|
||||
Canvas.ZIndex="1"
|
||||
XYFocusKeyboardNavigation="Enabled">
|
||||
contract4Present:XYFocusKeyboardNavigation="Enabled"
|
||||
Background="{ThemeResource NavigationViewTopPaneBackground}"
|
||||
Canvas.ZIndex="1">
|
||||
|
||||
<Grid x:Name="TopNavTopPadding"
|
||||
Height="{Binding TemplateSettings.TopPadding, RelativeSource={RelativeSource Mode=TemplatedParent}}"
|
||||
Visibility="{Binding TemplateSettings.TopPaneVisibility, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
|
||||
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.TopPadding}"
|
||||
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.TopPaneVisibility}" />
|
||||
|
||||
<Grid x:Name="TopNavGrid"
|
||||
Height="{ThemeResource NavigationViewTopPaneHeight}"
|
||||
Visibility="{Binding TemplateSettings.TopPaneVisibility, RelativeSource={RelativeSource Mode=TemplatedParent}}">
|
||||
Margin="{ThemeResource TopNavigationViewTopNavGridMargin}"
|
||||
BorderBrush="{ThemeResource NavigationViewItemSeparatorForeground}"
|
||||
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.TopPaneVisibility}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="BackButtonPlaceholderOnTopNav"
|
||||
Width="{ThemeResource NavigationBackButtonWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*"
|
||||
MinWidth="48" />
|
||||
MinWidth="{ThemeResource TopNavigationViewPaneCustomContentMinWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid x:Name="TopNavLeftPadding"
|
||||
Grid.Column="1"
|
||||
Width="0" />
|
||||
|
||||
<ContentControl x:Name="PaneHeaderOnTopPane"
|
||||
Grid.Column="2"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
<NavigationViewList x:Name="TopNavMenuItemsHost"
|
||||
Grid.Column="3"
|
||||
|
||||
<ContentControl x:Name="PaneTitleOnTopPane"
|
||||
Grid.Column="2"
|
||||
Margin="{ThemeResource NavigationViewItemInnerHeaderMargin}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
|
||||
<!-- Top nav ItemsRepeater -->
|
||||
<muxc:ItemsRepeaterScrollHost Grid.Column="3">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
|
||||
HorizontalScrollMode="Disabled"
|
||||
VerticalScrollBarVisibility="Hidden"
|
||||
VerticalScrollMode="Disabled">
|
||||
<muxc:ItemsRepeater x:Name="TopNavMenuItemsHost"
|
||||
AutomationProperties.AccessibilityView="Content"
|
||||
AutomationProperties.LandmarkType="Navigation"
|
||||
IsItemClickEnabled="True"
|
||||
ItemContainerStyle="{TemplateBinding MenuItemContainerStyle}"
|
||||
ItemContainerStyleSelector="{TemplateBinding MenuItemContainerStyleSelector}"
|
||||
ItemTemplate="{TemplateBinding MenuItemTemplate}"
|
||||
ItemTemplateSelector="{TemplateBinding MenuItemTemplateSelector}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.HorizontalScrollMode="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.VerticalScrollMode="Disabled"
|
||||
SelectionMode="Single"
|
||||
SingleSelectionFollowsFocus="{Binding TemplateSettings.SingleSelectionFollowsFocus, RelativeSource={RelativeSource Mode=TemplatedParent}}">
|
||||
<NavigationViewList.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</NavigationViewList.ItemsPanel>
|
||||
</NavigationViewList>
|
||||
AutomationProperties.Name="{TemplateBinding AutomationProperties.Name}">
|
||||
<muxc:ItemsRepeater.Layout>
|
||||
<muxc:StackLayout Orientation="Horizontal" />
|
||||
</muxc:ItemsRepeater.Layout>
|
||||
</muxc:ItemsRepeater>
|
||||
</ScrollViewer>
|
||||
</muxc:ItemsRepeaterScrollHost>
|
||||
|
||||
<Button x:Name="TopNavOverflowButton"
|
||||
Grid.Column="4"
|
||||
Margin="{ThemeResource TopNavigationViewOverflowButtonMargin}"
|
||||
Content="More"
|
||||
Style="{StaticResource NavigationViewOverflowButtonStyleWhenPaneOnTop}"
|
||||
Visibility="{Binding TemplateSettings.OverflowButtonVisibility, RelativeSource={RelativeSource Mode=TemplatedParent}}">
|
||||
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.OverflowButtonVisibility}">
|
||||
|
||||
<Button.Flyout>
|
||||
<Flyout Placement="Bottom"
|
||||
Windows10version1903:ShouldConstrainToRootBounds="False">
|
||||
<Flyout ElementSoundMode="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ElementSoundMode}"
|
||||
Placement="BottomEdgeAlignedRight">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="0,8" />
|
||||
<Setter Property="Padding" Value="{ThemeResource TopNavigationViewOverflowMenuPadding}" />
|
||||
<!-- Set negative top margin to make the flyout align exactly with the button -->
|
||||
<Setter Property="Margin" Value="0,-4,0,0" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
|
||||
<Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource OverlayCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="FlyoutPresenter">
|
||||
<ScrollViewer x:Name="ScrollViewer"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
|
||||
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
|
||||
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}" />
|
||||
</ScrollViewer>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<NavigationViewList x:Name="TopNavMenuItemsOverflowHost"
|
||||
IsItemClickEnabled="True"
|
||||
ItemContainerStyle="{TemplateBinding MenuItemContainerStyle}"
|
||||
ItemContainerStyleSelector="{TemplateBinding MenuItemContainerStyleSelector}"
|
||||
ItemTemplate="{TemplateBinding MenuItemTemplate}"
|
||||
ItemTemplateSelector="{TemplateBinding MenuItemTemplateSelector}"
|
||||
SingleSelectionFollowsFocus="False">
|
||||
<NavigationViewList.ItemContainerTransitions>
|
||||
<TransitionCollection />
|
||||
</NavigationViewList.ItemContainerTransitions>
|
||||
</NavigationViewList>
|
||||
<muxc:ItemsRepeaterScrollHost>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<muxc:ItemsRepeater x:Name="TopNavMenuItemsOverflowHost"
|
||||
AutomationProperties.AccessibilityView="Content">
|
||||
<muxc:ItemsRepeater.Layout>
|
||||
<muxc:StackLayout />
|
||||
</muxc:ItemsRepeater.Layout>
|
||||
</muxc:ItemsRepeater>
|
||||
</ScrollViewer>
|
||||
</muxc:ItemsRepeaterScrollHost>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
<ContentControl x:Name="PaneCustomContentOnTopPane"
|
||||
Grid.Column="5"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
|
||||
<Grid x:Name="TopPaneAutoSuggestArea"
|
||||
Grid.Column="6"
|
||||
Height="{ThemeResource NavigationViewTopPaneHeight}">
|
||||
|
||||
<ContentControl x:Name="TopPaneAutoSuggestBoxPresenter"
|
||||
MinWidth="216"
|
||||
Margin="{ThemeResource TopNavigationViewAutoSuggestBoxMargin}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
IsTabStop="False" />
|
||||
</Grid>
|
||||
|
||||
<ContentControl x:Name="PaneFooterOnTopPane"
|
||||
Grid.Column="7"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
<!-- Top footer menu ItemsRepeater -->
|
||||
<muxc:ItemsRepeater x:Name="TopFooterMenuItemsHost"
|
||||
Grid.Column="8"
|
||||
AutomationProperties.AccessibilityView="Content"
|
||||
AutomationProperties.LandmarkType="Navigation">
|
||||
<muxc:ItemsRepeater.Layout>
|
||||
<muxc:StackLayout Orientation="Horizontal" />
|
||||
</muxc:ItemsRepeater.Layout>
|
||||
</muxc:ItemsRepeater>
|
||||
|
||||
</Grid>
|
||||
<Border x:Name="TopNavContentOverlayAreaGrid"
|
||||
Child="{TemplateBinding ContentOverlay}" />
|
||||
</StackPanel>
|
||||
<SplitView x:Name="RootSplitView"
|
||||
Grid.Row="1"
|
||||
x:Load="False"
|
||||
Background="{TemplateBinding Background}"
|
||||
CompactPaneLength="{TemplateBinding CompactPaneLength}"
|
||||
DisplayMode="Inline"
|
||||
IsPaneOpen="{Binding IsPaneOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
|
||||
IsTabStop="False"
|
||||
OpenPaneLength="{TemplateBinding OpenPaneLength}"
|
||||
PaneBackground="{ThemeResource NavigationViewDefaultPaneBackground}">
|
||||
<SplitView.Pane>
|
||||
<Grid x:Name="PaneContentGrid"
|
||||
Visibility="{Binding TemplateSettings.LeftPaneVisibility, RelativeSource={RelativeSource Mode=TemplatedParent}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="0" />
|
||||
<RowDefinition x:Name="PaneContentGridToggleButtonRow"
|
||||
Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="8" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="ContentPaneTopPadding"
|
||||
Height="{Binding TemplateSettings.TopPadding, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
|
||||
<Grid Grid.Row="2"
|
||||
Height="{StaticResource PaneToggleButtonHeight}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{ThemeResource PaneToggleButtonWidth}" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentControl x:Name="PaneHeaderContentBorder"
|
||||
Grid.Column="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
</Grid>
|
||||
<ContentControl x:Name="PaneCustomContentBorder"
|
||||
Grid.Row="4"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
<NavigationViewList x:Name="MenuItemsHost"
|
||||
Grid.Row="6"
|
||||
Margin="0,0,0,20"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsItemClickEnabled="True"
|
||||
ItemContainerStyle="{TemplateBinding MenuItemContainerStyle}"
|
||||
ItemContainerStyleSelector="{TemplateBinding MenuItemContainerStyleSelector}"
|
||||
ItemTemplate="{TemplateBinding MenuItemTemplate}"
|
||||
ItemTemplateSelector="{TemplateBinding MenuItemTemplateSelector}"
|
||||
SelectedItem="{TemplateBinding SelectedItem}"
|
||||
SelectionMode="Single"
|
||||
SingleSelectionFollowsFocus="{Binding TemplateSettings.SingleSelectionFollowsFocus, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
|
||||
<ContentControl x:Name="FooterContentBorder"
|
||||
Grid.Row="7"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
IsTabStop="False" />
|
||||
</Grid>
|
||||
</SplitView.Pane>
|
||||
</SplitView>
|
||||
</Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="DisplayModeGroup">
|
||||
<VisualState x:Name="Compact" />
|
||||
<VisualState x:Name="Expanded">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootSplitView.PaneBackground" Value="{ThemeResource NavigationViewExpandedPaneBackground}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Minimal">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="HeaderContent.Margin" Value="48,5,0,0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="TopNavigationMinimal" />
|
||||
<VisualState x:Name="MinimalWithBackButton">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="HeaderContent.Margin" Value="104,5,0,0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="HeaderGroup">
|
||||
<VisualState x:Name="HeaderVisible" />
|
||||
<VisualState x:Name="HeaderCollapsed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="HeaderContent.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="PaneStateListSizeGroup">
|
||||
<VisualState x:Name="ListSizeFull" />
|
||||
<VisualState x:Name="ListSizeCompact">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="MenuItemsHost.HorizontalAlignment" Value="Left" />
|
||||
<Setter Target="MenuItemsHost.Width" Value="{Binding CompactPaneLength, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
|
||||
<Setter Target="SettingsNavPaneItem.HorizontalAlignment" Value="Left" />
|
||||
<Setter Target="SettingsNavPaneItem.Width" Value="{Binding CompactPaneLength, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
|
||||
<Setter Target="PaneTitleTextBlock.Visibility" Value="Collapsed" />
|
||||
<Setter Target="PaneHeaderContentBorder.Visibility" Value="Collapsed" />
|
||||
<Setter Target="PaneCustomContentBorder.HorizontalAlignment" Value="Left" />
|
||||
<Setter Target="PaneCustomContentBorder.Width" Value="{Binding CompactPaneLength, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
|
||||
<Setter Target="FooterContentBorder.HorizontalAlignment" Value="Left" />
|
||||
<Setter Target="FooterContentBorder.Width" Value="{Binding CompactPaneLength, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="TitleBarVisibilityGroup">
|
||||
<VisualState x:Name="TitleBarVisible" />
|
||||
<VisualState x:Name="TitleBarCollapsed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="PaneContentGrid.Margin" Value="0,32,0,0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="OverflowLabelGroup">
|
||||
<VisualState x:Name="OverflowButtonWithLabel" />
|
||||
<VisualState x:Name="OverflowButtonNoLabel">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TopNavOverflowButton.Style" Value="{ThemeResource NavigationViewOverflowButtonNoLabelStyleWhenPaneOnTop}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
|
||||
|
||||
<Border x:Name="PART_TabbedCommandBarContentBorder"
|
||||
Grid.Row="1"
|
||||
|
|
|
@ -1,15 +1,34 @@
|
|||
<ResourceDictionary 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">
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<!-- TODO: This still seems a bit dark? -->
|
||||
<SolidColorBrush x:Key="ContextualTabBackground"
|
||||
Color="{ThemeResource SystemAltMediumHighColor}" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ContextualTabBackground"
|
||||
Color="{ThemeResource SystemChromeMediumColor}" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="ContextualTabBackground"
|
||||
Color="{ThemeResource SystemAltLowColor}" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<SolidColorBrush x:Key="ContextualTabBackground" Color="{ThemeResource SystemAltMediumColor}"/>
|
||||
<SolidColorBrush x:Key="NormalTabBackground" Color="{ThemeResource SystemChromeLowColor}"/>
|
||||
<SolidColorBrush x:Key="NormalTabAcrylicBackground" Color="{ThemeResource SystemControlChromeLowAcrylicWindowBrush}"/>
|
||||
<SolidColorBrush x:Key="NormalTabBackground"
|
||||
Color="{ThemeResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="NormalTabAcrylicBackground"
|
||||
Color="{ThemeResource SystemControlChromeLowAcrylicWindowBrush}" />
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultTabbedCommandBarItemStyle}" TargetType="controls:TabbedCommandBarItem"/>
|
||||
<Style BasedOn="{StaticResource DefaultTabbedCommandBarItemStyle}"
|
||||
TargetType="controls:TabbedCommandBarItem" />
|
||||
|
||||
<Style x:Key="DefaultTabbedCommandBarItemStyle" BasedOn="{StaticResource CommandBarRevealStyle}" TargetType="controls:TabbedCommandBarItem">
|
||||
<!-- TODO: Copy CommandBarRevealStyle instead of referencing it -->
|
||||
<Style x:Key="DefaultTabbedCommandBarItemStyle"
|
||||
BasedOn="{StaticResource DefaultCommandBarStyle}"
|
||||
TargetType="controls:TabbedCommandBarItem">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="DefaultLabelPosition" Value="Right" />
|
||||
<Setter Property="Background" Value="{ThemeResource NormalTabBackground}" />
|
||||
|
@ -18,42 +37,52 @@
|
|||
(See this message in the WinUI channel: https://discord.com/channels/372137812037730304/671870147354427422/771057634203402300 )
|
||||
Hardcoding the height of the CommandBar works, but it's a bit of a hack.
|
||||
-->
|
||||
<Setter Property="Height" Value="40" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="TabbedCommandBarItemAcrylicStyle" BasedOn="{StaticResource DefaultTabbedCommandBarItemStyle}" TargetType="controls:TabbedCommandBarItem">
|
||||
<Style x:Key="TabbedCommandBarItemAcrylicStyle"
|
||||
BasedOn="{StaticResource DefaultTabbedCommandBarItemStyle}"
|
||||
TargetType="controls:TabbedCommandBarItem">
|
||||
<Setter Property="Background" Value="{ThemeResource NormalTabAcrylicBackground}" />
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="NormalTabTemplate">
|
||||
<NavigationViewItem Content="{Binding Header}" Visibility="{Binding Visibility}"/>
|
||||
<muxc:NavigationViewItem Content="{Binding Header}"
|
||||
Visibility="{Binding Visibility}" />
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="ContextualTabTemplate">
|
||||
<NavigationViewItem Content="{Binding Header}" Background="{StaticResource ContextualTabBackground}" Visibility="{Binding Visibility}">
|
||||
<NavigationViewItem.Resources>
|
||||
<muxc:NavigationViewItem Background="{ThemeResource ContextualTabBackground}"
|
||||
Content="{Binding Header}"
|
||||
Visibility="{Binding Visibility}">
|
||||
<muxc:NavigationViewItem.Resources>
|
||||
<!-- TODO: These should reference TabbedCommandBarItem-specific resources so they can overriden -->
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForeground" Color="{ThemeResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForegroundSelected" Color="{ThemeResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForegroundPointerOver" Color="{ThemeResource SystemAccentColorLight2}" />
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForegroundPressed" Color="{ThemeResource SystemAccentColorLight2}" />
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForeground"
|
||||
Color="{ThemeResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForegroundSelected"
|
||||
Color="{ThemeResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForegroundPointerOver"
|
||||
Color="{ThemeResource SystemAccentColorLight2}" />
|
||||
<SolidColorBrush x:Key="TopNavigationViewItemForegroundPressed"
|
||||
Color="{ThemeResource SystemAccentColorLight2}" />
|
||||
|
||||
<!-- TODO: Set BackgroundSelected to match ContextualTabBackground -->
|
||||
<!--<StaticResource x:Key="TopNavigationViewItemBackgroundSelected" ResourceKey="ContextualTabBackgroundColor" />-->
|
||||
</NavigationViewItem.Resources>
|
||||
</NavigationViewItem>
|
||||
</muxc:NavigationViewItem.Resources>
|
||||
</muxc:NavigationViewItem>
|
||||
</DataTemplate>
|
||||
<controls:TabbedCommandBarItemTemplateSelector x:Key="DefaultTabbedCommandBarItemTemplateSelector"
|
||||
Contextual="{StaticResource ContextualTabTemplate}"
|
||||
Normal="{StaticResource NormalTabTemplate}" />
|
||||
|
||||
<Style x:Key="TabbedCommandBarElementContainerStyle" TargetType="AppBarElementContainer">
|
||||
<Style x:Key="TabbedCommandBarElementContainerStyle"
|
||||
TargetType="AppBarElementContainer">
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Margin" Value="1,0" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="AppBarSplitButtonStyle" TargetType="SplitButton">
|
||||
<Style x:Key="AppBarSplitButtonStyle"
|
||||
TargetType="SplitButton">
|
||||
<Setter Property="Background" Value="{ThemeResource AppBarButtonRevealBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SplitButtonBorderBrush}" />
|
||||
|
@ -72,11 +101,9 @@
|
|||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="SplitButton">
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
<Grid x:Name="RootGrid"
|
||||
Background="Transparent"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
|
||||
<Grid.Resources>
|
||||
<!-- Override the style of the inner buttons so that they don't affect background/foreground/border colors -->
|
||||
<Style TargetType="Button">
|
||||
|
@ -94,7 +121,8 @@
|
|||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid x:Name="RootGrid" Background="Transparent">
|
||||
<Grid x:Name="RootGrid"
|
||||
Background="Transparent">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
|
@ -135,17 +163,16 @@
|
|||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
|
@ -153,6 +180,16 @@
|
|||
</Style>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="PrimaryButtonColumn"
|
||||
Width="*"
|
||||
MinWidth="{ThemeResource SplitButtonPrimaryButtonSize}" />
|
||||
<ColumnDefinition x:Name="Separator"
|
||||
Width="1" />
|
||||
<ColumnDefinition x:Name="SecondaryButtonColumn"
|
||||
Width="{ThemeResource SplitButtonSecondaryButtonSize}" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
|
@ -300,18 +337,12 @@
|
|||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="PrimaryButtonColumn" Width="*" MinWidth="{ThemeResource SplitButtonPrimaryButtonSize}"/>
|
||||
<ColumnDefinition x:Name="Separator" Width="1" />
|
||||
<ColumnDefinition x:Name="SecondaryButtonColumn" Width="{ThemeResource SplitButtonSecondaryButtonSize}"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid x:Name="PrimaryBackgroundGrid"
|
||||
Background="{TemplateBinding Background}" />
|
||||
|
||||
<Grid x:Name="SecondaryBackgroundGrid"
|
||||
Background="{TemplateBinding Background}"
|
||||
Grid.Column="2"/>
|
||||
Grid.Column="2"
|
||||
Background="{TemplateBinding Background}" />
|
||||
|
||||
<Grid x:Name="Border"
|
||||
Grid.ColumnSpan="3"
|
||||
|
@ -321,42 +352,41 @@
|
|||
|
||||
<Button x:Name="PrimaryButton"
|
||||
Grid.Column="0"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Command="{TemplateBinding Command}"
|
||||
CommandParameter="{TemplateBinding CommandParameter}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
IsTabStop="False"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Command="{TemplateBinding Command}"
|
||||
CommandParameter="{TemplateBinding CommandParameter}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsTabStop="False" />
|
||||
|
||||
<Button x:Name="SecondaryButton"
|
||||
Grid.Column="2"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
Padding="0,0,9,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Padding="0,0,9,0"
|
||||
IsTabStop="False"
|
||||
AutomationProperties.AccessibilityView="Raw">
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsTabStop="False">
|
||||
<Button.Content>
|
||||
<TextBlock
|
||||
<TextBlock HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Text=""
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
Text="" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<FileList>
|
||||
<File Reference="Microsoft.Toolkit.Uwp.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="Microsoft.Toolkit.Uwp.UI.Controls.CameraPreview" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DropShadowPanel" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.ImageEx" />
|
||||
|
@ -13,6 +15,7 @@
|
|||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.RotatorTile" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.ScrollHeader" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TabbedCommandBar" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TabbedCommandBarItem" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TextToolbar" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TileControl" />
|
||||
</ToolboxItems>
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
<StaticResource x:Key="DataGridColumnHeaderDraggedBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPointerOverBrush" ResourceKey="SystemControlHighlightListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBrush" ResourceKey="SystemControlHighlightListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" ResourceKey="SystemControlBackgroundListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<StaticResource x:Key="DataGridRowHoveredBackgroundColor" ResourceKey="SystemListLowColor"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="InvalidBrush" Color="#FFFF00"/>
|
||||
|
@ -41,6 +45,10 @@
|
|||
<StaticResource x:Key="DataGridColumnHeaderDraggedBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPointerOverBrush" ResourceKey="SystemControlHighlightListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBrush" ResourceKey="SystemControlHighlightListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" ResourceKey="SystemControlBackgroundListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<StaticResource x:Key="DataGridRowHoveredBackgroundColor" ResourceKey="SystemListLowColor"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="InvalidBrush" Color="#C50500"/>
|
||||
|
@ -54,6 +62,10 @@
|
|||
<StaticResource x:Key="DataGridColumnHeaderDraggedBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPointerOverBrush" ResourceKey="SystemControlHighlightListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBrush" ResourceKey="SystemControlHighlightListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" ResourceKey="SystemControlBackgroundListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<StaticResource x:Key="DataGridRowHoveredBackgroundColor" ResourceKey="SystemListLowColor"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
|
@ -89,13 +101,9 @@
|
|||
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" ResourceKey="ListAccentLowOpacity"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundColor" ResourceKey="SystemAccentColor"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" ResourceKey="ListAccentMediumOpacity"/>
|
||||
<StaticResource x:Key="DataGridRowHoveredBackgroundColor" ResourceKey="SystemListLowColor"/>
|
||||
<StaticResource x:Key="DataGridRowHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowHeaderBackgroundBrush" ResourceKey="SystemControlBackgroundAltHighBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" ResourceKey="SystemControlBackgroundListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" ResourceKey="SystemControlBackgroundListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<StaticResource x:Key="DataGridRowInvalidBrush" ResourceKey="InvalidBrush"/>
|
||||
<StaticResource x:Key="DataGridCellBackgroundBrush" ResourceKey="SystemControlTransparentBrush"/>
|
||||
<StaticResource x:Key="DataGridCellFocusVisualPrimaryBrush" ResourceKey="SystemControlFocusVisualPrimaryBrush"/>
|
||||
|
|
|
@ -1122,40 +1122,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
{
|
||||
DiagnosticsDebug.Assert(this.Index != -1, "Expected Index other than -1.");
|
||||
|
||||
PropertyMetadata metadataInfo = DataGridRow.ForegroundProperty.GetMetadata(typeof(DataGridRow));
|
||||
Brush defaultForeground = metadataInfo == null ? null : metadataInfo.DefaultValue as Brush;
|
||||
Brush newForeground = null;
|
||||
var newForeground = this.Index % 2 == 0 || this.OwningGrid.AlternatingRowForeground == null
|
||||
? this.OwningGrid.RowForeground
|
||||
: this.OwningGrid.AlternatingRowForeground;
|
||||
|
||||
if (this.Foreground.Equals(defaultForeground))
|
||||
{
|
||||
if (this.Index % 2 == 0 || this.OwningGrid.AlternatingRowForeground == null)
|
||||
{
|
||||
// Use OwningGrid.RowForeground if the index is even or if the OwningGrid.AlternatingRowForeground is null
|
||||
if (this.OwningGrid.RowForeground != null)
|
||||
{
|
||||
newForeground = this.OwningGrid.RowForeground;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Alternate row
|
||||
if (this.OwningGrid.AlternatingRowForeground != null)
|
||||
{
|
||||
newForeground = this.OwningGrid.AlternatingRowForeground;
|
||||
}
|
||||
}
|
||||
|
||||
if (newForeground == null)
|
||||
{
|
||||
newForeground = this.Foreground;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newForeground = this.Foreground;
|
||||
}
|
||||
|
||||
this.ComputedForeground = newForeground;
|
||||
this.ComputedForeground = newForeground ?? this.Foreground;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,7 +1,26 @@
|
|||
<FileList>
|
||||
<File Reference="Microsoft.Toolkit.Uwp.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="Microsoft.Toolkit.Uwp.UI.Controls.DataGrid" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridCell" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridCellsPresenter" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridColumn" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridFillerColumn" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridCheckBoxColumn" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridComboBoxColumn" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridColumnHeader" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridColumnHeadersPresenter" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridDetailsPresenter" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridFrozenGrid" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridRow" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridRowHeader" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridRowGroupHeader" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridRowsPresenter" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridTemplateColumn" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGridTextColumn" />
|
||||
</ToolboxItems>
|
||||
</File>
|
||||
</FileList>
|
|
@ -0,0 +1,52 @@
|
|||
// 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 Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// The RichSuggestBox control extends <see cref="RichEditBox"/> control that suggests and embeds custom data in a rich document.
|
||||
/// </summary>
|
||||
public partial class RichSuggestBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Event raised when the control needs to show suggestions.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<RichSuggestBox, SuggestionRequestedEventArgs> SuggestionRequested;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when user click on a suggestion.
|
||||
/// This event lets you customize the token appearance in the document.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<RichSuggestBox, SuggestionChosenEventArgs> SuggestionChosen;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a token is fully highlighted.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<RichSuggestBox, RichSuggestTokenSelectedEventArgs> TokenSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a pointer is hovering over a token.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<RichSuggestBox, RichSuggestTokenPointerOverEventArgs> TokenPointerOver;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when text is changed, either by user or by internal formatting.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<RichSuggestBox, RoutedEventArgs> TextChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when the text selection has changed.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<RichSuggestBox, RoutedEventArgs> SelectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when text is pasted into the control.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<RichSuggestBox, TextControlPasteEventArgs> Paste;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
using System.Linq;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Display;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// The RichSuggestBox control extends <see cref="RichEditBox"/> control that suggests and embeds custom data in a rich document.
|
||||
/// </summary>
|
||||
public partial class RichSuggestBox
|
||||
{
|
||||
private static bool IsElementOnScreen(FrameworkElement element, double offsetX = 0, double offsetY = 0)
|
||||
{
|
||||
// DisplayInformation only works in UWP. No alternative to get DisplayInformation.ScreenHeightInRawPixels
|
||||
// Or Window position in Window.Current.Bounds
|
||||
// Tracking issues:
|
||||
// https://github.com/microsoft/WindowsAppSDK/issues/114
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/4228
|
||||
// TODO: Remove when DisplayInformation.ScreenHeightInRawPixels alternative is available
|
||||
if (CoreWindow.GetForCurrentThread() == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get bounds of element from root of tree
|
||||
var elementBounds = element.CoordinatesFrom(null).ToRect(element.ActualWidth, element.ActualHeight);
|
||||
|
||||
// Apply offset
|
||||
elementBounds.X += offsetX;
|
||||
elementBounds.Y += offsetY;
|
||||
|
||||
// Get Window position
|
||||
var windowBounds = Window.Current.Bounds;
|
||||
|
||||
// Offset Element within Window on Screen
|
||||
elementBounds.X += windowBounds.X;
|
||||
elementBounds.Y += windowBounds.Y;
|
||||
|
||||
// Get Screen DPI info
|
||||
var displayInfo = DisplayInformation.GetForCurrentView();
|
||||
var scaleFactor = displayInfo.RawPixelsPerViewPixel;
|
||||
var displayHeight = displayInfo.ScreenHeightInRawPixels;
|
||||
|
||||
// Check if top/bottom are within confines of screen
|
||||
return elementBounds.Top * scaleFactor >= 0 && elementBounds.Bottom * scaleFactor <= displayHeight;
|
||||
}
|
||||
|
||||
private static bool IsElementInsideWindow(FrameworkElement element, double offsetX = 0, double offsetY = 0)
|
||||
{
|
||||
// Get bounds of element from root of tree
|
||||
var elementBounds = element.CoordinatesFrom(null).ToRect(element.ActualWidth, element.ActualHeight);
|
||||
|
||||
// Apply offset
|
||||
elementBounds.X += offsetX;
|
||||
elementBounds.Y += offsetY;
|
||||
|
||||
// Get size of window itself
|
||||
var windowBounds = ControlHelpers.IsXamlRootAvailable && element.XamlRoot != null
|
||||
? element.XamlRoot.Size.ToRect()
|
||||
: ApplicationView.GetForCurrentView().VisibleBounds.ToSize().ToRect(); // Normalize
|
||||
|
||||
// Calculate if there's an intersection
|
||||
elementBounds.Intersect(windowBounds);
|
||||
|
||||
// See if we are still fully visible within the Window
|
||||
return elementBounds.Height >= element.ActualHeight;
|
||||
}
|
||||
|
||||
private static string EnforcePrefixesRequirements(string value)
|
||||
{
|
||||
return string.IsNullOrEmpty(value) ? string.Empty : string.Concat(value.Where(char.IsPunctuation));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pad range with Zero-Width-Spaces.
|
||||
/// </summary>
|
||||
/// <param name="range">Range to pad.</param>
|
||||
/// <param name="format">Character format to apply to the padding.</param>
|
||||
private static void PadRange(ITextRange range, ITextCharacterFormat format)
|
||||
{
|
||||
var startPosition = range.StartPosition;
|
||||
var endPosition = range.EndPosition + 1;
|
||||
var clone = range.GetClone();
|
||||
clone.Collapse(true);
|
||||
clone.SetText(TextSetOptions.Unhide, "\u200B");
|
||||
clone.CharacterFormat.SetClone(format);
|
||||
clone.SetRange(endPosition, endPosition);
|
||||
clone.SetText(TextSetOptions.Unhide, "\u200B");
|
||||
clone.CharacterFormat.SetClone(format);
|
||||
range.SetRange(startPosition, endPosition + 1);
|
||||
}
|
||||
|
||||
private static void ForEachLinkInDocument(ITextDocument document, Action<ITextRange> action)
|
||||
{
|
||||
var range = document.GetRange(0, 0);
|
||||
range.SetIndex(TextRangeUnit.Character, -1, false);
|
||||
|
||||
// Handle link at the very end of the document where GetIndex fails to detect
|
||||
range.Expand(TextRangeUnit.Link);
|
||||
if (!string.IsNullOrEmpty(range.Link))
|
||||
{
|
||||
action?.Invoke(range);
|
||||
}
|
||||
|
||||
var nextIndex = range.GetIndex(TextRangeUnit.Link);
|
||||
while (nextIndex != 0 && nextIndex != 1)
|
||||
{
|
||||
range.Move(TextRangeUnit.Link, -1);
|
||||
|
||||
var linkRange = range.GetClone();
|
||||
linkRange.Expand(TextRangeUnit.Link);
|
||||
|
||||
// Adjacent links have the same index. Manually check each link with Collapse and Expand.
|
||||
var previousStart = linkRange.StartPosition;
|
||||
var hasAdjacentToken = true;
|
||||
while (hasAdjacentToken)
|
||||
{
|
||||
action?.Invoke(linkRange);
|
||||
|
||||
linkRange.Collapse(false);
|
||||
linkRange.Expand(TextRangeUnit.Link);
|
||||
hasAdjacentToken = !string.IsNullOrEmpty(linkRange.Link) && linkRange.StartPosition != previousStart;
|
||||
previousStart = linkRange.StartPosition;
|
||||
}
|
||||
|
||||
nextIndex = range.GetIndex(TextRangeUnit.Link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
// 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.ObjectModel;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// The RichSuggestBox control extends <see cref="RichEditBox"/> control that suggests and embeds custom data in a rich document.
|
||||
/// </summary>
|
||||
public partial class RichSuggestBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="PlaceholderText"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PlaceholderTextProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PlaceholderText),
|
||||
typeof(string),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(string.Empty));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="RichEditBoxStyle"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty RichEditBoxStyleProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(RichEditBoxStyle),
|
||||
typeof(Style),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Header"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty HeaderProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(Header),
|
||||
typeof(object),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null, OnHeaderChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="HeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty HeaderTemplateProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(HeaderTemplate),
|
||||
typeof(DataTemplate),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Description"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty DescriptionProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(Description),
|
||||
typeof(object),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null, OnDescriptionChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="PopupPlacement"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PopupPlacementProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PopupPlacement),
|
||||
typeof(SuggestionPopupPlacementMode),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(SuggestionPopupPlacementMode.Floating, OnSuggestionPopupPlacementChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="PopupCornerRadius"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PopupCornerRadiusProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PopupCornerRadius),
|
||||
typeof(CornerRadius),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(default(CornerRadius)));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="PopupHeader"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PopupHeaderProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PopupHeader),
|
||||
typeof(object),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="PopupHeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PopupHeaderTemplateProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PopupHeaderTemplate),
|
||||
typeof(DataTemplate),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="PopupFooter"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PopupFooterProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PopupFooter),
|
||||
typeof(object),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="PopupFooterTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PopupFooterTemplateProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(PopupFooterTemplate),
|
||||
typeof(DataTemplate),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TokenBackground"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TokenBackgroundProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(TokenBackground),
|
||||
typeof(SolidColorBrush),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TokenForeground"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TokenForegroundProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(TokenForeground),
|
||||
typeof(SolidColorBrush),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Prefixes"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PrefixesProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(Prefixes),
|
||||
typeof(string),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(string.Empty, OnPrefixesChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ClipboardPasteFormat"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ClipboardPasteFormatProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(ClipboardPasteFormat),
|
||||
typeof(RichEditClipboardFormat),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(RichEditClipboardFormat.AllFormats));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ClipboardCopyFormat"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ClipboardCopyFormatProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(ClipboardCopyFormat),
|
||||
typeof(RichEditClipboardFormat),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(RichEditClipboardFormat.AllFormats));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="DisabledFormattingAccelerators"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty DisabledFormattingAcceleratorsProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(DisabledFormattingAccelerators),
|
||||
typeof(DisabledFormattingAccelerators),
|
||||
typeof(RichSuggestBox),
|
||||
new PropertyMetadata(DisabledFormattingAccelerators.None));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text that is displayed in the control until the value is changed by a user action or some other operation.
|
||||
/// </summary>
|
||||
public string PlaceholderText
|
||||
{
|
||||
get => (string)GetValue(PlaceholderTextProperty);
|
||||
set => SetValue(PlaceholderTextProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the style of the underlying <see cref="RichEditBox"/>.
|
||||
/// </summary>
|
||||
public Style RichEditBoxStyle
|
||||
{
|
||||
get => (Style)GetValue(RichEditBoxStyleProperty);
|
||||
set => SetValue(RichEditBoxStyleProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content for the control's header.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Suggestion popup relies on the actual size of the text control to calculate its placement on the screen.
|
||||
/// It is recommended to set the header using this property instead of using <see cref="RichEditBox.Header"/>.
|
||||
/// </remarks>
|
||||
public object Header
|
||||
{
|
||||
get => GetValue(HeaderProperty);
|
||||
set => SetValue(HeaderProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DataTemplate"/> used to display the content of the control's header.
|
||||
/// </summary>
|
||||
public DataTemplate HeaderTemplate
|
||||
{
|
||||
get => (DataTemplate)GetValue(HeaderTemplateProperty);
|
||||
set => SetValue(HeaderTemplateProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets content that is shown below the control. The content should provide guidance about the input expected by the control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Suggestion popup relies on the actual size of the text control to calculate its placement on the screen.
|
||||
/// It is recommended to set the description using this property instead of using <see cref="RichEditBox.Description"/>.
|
||||
/// </remarks>
|
||||
public object Description
|
||||
{
|
||||
get => GetValue(DescriptionProperty);
|
||||
set => SetValue(DescriptionProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets suggestion popup placement to either Floating or Attached to the text box.
|
||||
/// </summary>
|
||||
public SuggestionPopupPlacementMode PopupPlacement
|
||||
{
|
||||
get => (SuggestionPopupPlacementMode)GetValue(PopupPlacementProperty);
|
||||
set => SetValue(PopupPlacementProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the radius for the corners of the popup control's border.
|
||||
/// </summary>
|
||||
public CornerRadius PopupCornerRadius
|
||||
{
|
||||
get => (CornerRadius)GetValue(PopupCornerRadiusProperty);
|
||||
set => SetValue(PopupCornerRadiusProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content for the suggestion popup control's header.
|
||||
/// </summary>
|
||||
public object PopupHeader
|
||||
{
|
||||
get => GetValue(PopupHeaderProperty);
|
||||
set => SetValue(PopupHeaderProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DataTemplate"/> used to display the content of the suggestion popup control's header.
|
||||
/// </summary>
|
||||
public DataTemplate PopupHeaderTemplate
|
||||
{
|
||||
get => (DataTemplate)GetValue(PopupHeaderTemplateProperty);
|
||||
set => SetValue(PopupHeaderTemplateProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content for the suggestion popup control's footer.
|
||||
/// </summary>
|
||||
public object PopupFooter
|
||||
{
|
||||
get => GetValue(PopupFooterProperty);
|
||||
set => SetValue(PopupFooterProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DataTemplate"/> used to display the content of the suggestion popup control's footer.
|
||||
/// </summary>
|
||||
public DataTemplate PopupFooterTemplate
|
||||
{
|
||||
get => (DataTemplate)GetValue(PopupFooterTemplateProperty);
|
||||
set => SetValue(PopupFooterTemplateProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default brush used to color the suggestion token background.
|
||||
/// </summary>
|
||||
public SolidColorBrush TokenBackground
|
||||
{
|
||||
get => (SolidColorBrush)GetValue(TokenBackgroundProperty);
|
||||
set => SetValue(TokenBackgroundProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default brush used to color the suggestion token foreground.
|
||||
/// </summary>
|
||||
public SolidColorBrush TokenForeground
|
||||
{
|
||||
get => (SolidColorBrush)GetValue(TokenForegroundProperty);
|
||||
set => SetValue(TokenForegroundProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets prefix characters to start a query.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Prefix characters must be punctuations (must satisfy <see cref="char.IsPunctuation(char)"/> method).
|
||||
/// </remarks>
|
||||
public string Prefixes
|
||||
{
|
||||
get => (string)GetValue(PrefixesProperty);
|
||||
set => SetValue(PrefixesProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that specifies whether pasted text preserves all formats, or as plain text only.
|
||||
/// </summary>
|
||||
public RichEditClipboardFormat ClipboardPasteFormat
|
||||
{
|
||||
get => (RichEditClipboardFormat)GetValue(ClipboardPasteFormatProperty);
|
||||
set => SetValue(ClipboardPasteFormatProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that specifies whether text is copied with all formats, or as plain text only.
|
||||
/// </summary>
|
||||
public RichEditClipboardFormat ClipboardCopyFormat
|
||||
{
|
||||
get => (RichEditClipboardFormat)GetValue(ClipboardCopyFormatProperty);
|
||||
set => SetValue(ClipboardCopyFormatProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates which keyboard shortcuts for formatting are disabled.
|
||||
/// </summary>
|
||||
public DisabledFormattingAccelerators DisabledFormattingAccelerators
|
||||
{
|
||||
get => (DisabledFormattingAccelerators)GetValue(DisabledFormattingAcceleratorsProperty);
|
||||
set => SetValue(DisabledFormattingAcceleratorsProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that enables access to the text object model for the text contained in a <see cref="RichEditBox"/>.
|
||||
/// </summary>
|
||||
public RichEditTextDocument TextDocument => _richEditBox?.TextDocument;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance the content has been scrolled horizontally from the underlying <see cref="ScrollViewer"/>.
|
||||
/// </summary>
|
||||
public double HorizontalOffset => this._scrollViewer?.HorizontalOffset ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance the content has been scrolled vertically from the underlying <see cref="ScrollViewer"/>.
|
||||
/// </summary>
|
||||
public double VerticalOffset => this._scrollViewer?.VerticalOffset ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of suggestion tokens that are present in the document.
|
||||
/// </summary>
|
||||
public ReadOnlyObservableCollection<RichSuggestToken> Tokens { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,993 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Uwp.Deferred;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.System;
|
||||
using Windows.UI.Input;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// The RichSuggestBox control extends <see cref="RichEditBox"/> control that suggests and embeds custom data in a rich document.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = PartRichEditBox, Type = typeof(RichEditBox))]
|
||||
[TemplatePart(Name = PartSuggestionsPopup, Type = typeof(Popup))]
|
||||
[TemplatePart(Name = PartSuggestionsList, Type = typeof(ListViewBase))]
|
||||
[TemplatePart(Name = PartSuggestionsContainer, Type = typeof(Border))]
|
||||
[TemplatePart(Name = PartHeaderContentPresenter, Type = typeof(ContentPresenter))]
|
||||
[TemplatePart(Name = PartDescriptionPresenter, Type = typeof(ContentPresenter))]
|
||||
public partial class RichSuggestBox : ItemsControl
|
||||
{
|
||||
private const string PartRichEditBox = "RichEditBox";
|
||||
private const string PartSuggestionsPopup = "SuggestionsPopup";
|
||||
private const string PartSuggestionsList = "SuggestionsList";
|
||||
private const string PartSuggestionsContainer = "SuggestionsContainer";
|
||||
private const string PartHeaderContentPresenter = "HeaderContentPresenter";
|
||||
private const string PartDescriptionPresenter = "DescriptionPresenter";
|
||||
|
||||
private readonly object _tokensLock;
|
||||
private readonly Dictionary<string, RichSuggestToken> _tokens;
|
||||
private readonly ObservableCollection<RichSuggestToken> _visibleTokens;
|
||||
|
||||
private Popup _suggestionPopup;
|
||||
private RichEditBox _richEditBox;
|
||||
private ScrollViewer _scrollViewer;
|
||||
private ListViewBase _suggestionsList;
|
||||
private Border _suggestionsContainer;
|
||||
|
||||
private int _suggestionChoice;
|
||||
private bool _ignoreChange;
|
||||
private bool _popupOpenDown;
|
||||
private bool _textCompositionActive;
|
||||
private RichSuggestQuery _currentQuery;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RichSuggestBox"/> class.
|
||||
/// </summary>
|
||||
public RichSuggestBox()
|
||||
{
|
||||
_tokensLock = new object();
|
||||
_tokens = new Dictionary<string, RichSuggestToken>();
|
||||
_visibleTokens = new ObservableCollection<RichSuggestToken>();
|
||||
Tokens = new ReadOnlyObservableCollection<RichSuggestToken>(_visibleTokens);
|
||||
|
||||
DefaultStyleKey = typeof(RichSuggestBox);
|
||||
|
||||
RegisterPropertyChangedCallback(CornerRadiusProperty, OnCornerRadiusChanged);
|
||||
RegisterPropertyChangedCallback(PopupCornerRadiusProperty, OnCornerRadiusChanged);
|
||||
LostFocus += OnLostFocus;
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear unused tokens and undo/redo history.
|
||||
/// </summary>
|
||||
public void ClearUndoRedoSuggestionHistory()
|
||||
{
|
||||
TextDocument.ClearUndoRedoHistory();
|
||||
lock (_tokensLock)
|
||||
{
|
||||
if (_tokens.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var keysToDelete = _tokens.Where(pair => !pair.Value.Active).Select(pair => pair.Key).ToArray();
|
||||
foreach (var key in keysToDelete)
|
||||
{
|
||||
_tokens.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the document and token list. This will also clear the undo/redo history.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_tokensLock)
|
||||
{
|
||||
_tokens.Clear();
|
||||
_visibleTokens.Clear();
|
||||
TextDocument.Selection.Expand(TextRangeUnit.Story);
|
||||
TextDocument.Selection.Delete(TextRangeUnit.Story, 0);
|
||||
TextDocument.ClearUndoRedoHistory();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add tokens to be tracked against the document. Duplicate tokens will not be updated.
|
||||
/// </summary>
|
||||
/// <param name="tokens">The collection of tokens to be tracked.</param>
|
||||
public void AddTokens(IEnumerable<RichSuggestToken> tokens)
|
||||
{
|
||||
lock (_tokensLock)
|
||||
{
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
_tokens.TryAdd($"\"{token.Id}\"", token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the <see cref="RichSuggestBox"/> with an existing Rich Text Format (RTF) document and a collection of tokens.
|
||||
/// </summary>
|
||||
/// <param name="rtf">The Rich Text Format (RTF) text to be imported.</param>
|
||||
/// <param name="tokens">The collection of tokens embedded in the document.</param>
|
||||
public void Load(string rtf, IEnumerable<RichSuggestToken> tokens)
|
||||
{
|
||||
Clear();
|
||||
AddTokens(tokens);
|
||||
TextDocument.SetText(TextSetOptions.FormatRtf, rtf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try getting the token associated with a text range.
|
||||
/// </summary>
|
||||
/// <param name="range">The range of the token to get.</param>
|
||||
/// <param name="token">When this method returns, contains the token associated with the specified range; otherwise, it is null.</param>
|
||||
/// <returns>true if there is a token associated with the text range; otherwise false.</returns>
|
||||
public bool TryGetTokenFromRange(ITextRange range, out RichSuggestToken token)
|
||||
{
|
||||
token = null;
|
||||
range = range.GetClone();
|
||||
if (range != null && !string.IsNullOrEmpty(range.Link))
|
||||
{
|
||||
lock (_tokensLock)
|
||||
{
|
||||
return _tokens.TryGetValue(range.Link, out token);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the bounding rectangle that encompasses the text range
|
||||
/// with position measured from the top left of the <see cref="RichSuggestBox"/> control.
|
||||
/// </summary>
|
||||
/// <param name="range">Text range to retrieve the bounding box from.</param>
|
||||
/// <returns>The bounding rectangle.</returns>
|
||||
public Rect GetRectFromRange(ITextRange range)
|
||||
{
|
||||
var padding = _richEditBox.Padding;
|
||||
range.GetRect(PointOptions.None, out var rect, out var hit);
|
||||
rect.X += padding.Left - HorizontalOffset;
|
||||
rect.Y += padding.Top - VerticalOffset;
|
||||
var transform = _richEditBox.TransformToVisual(this);
|
||||
return transform.TransformBounds(rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
PointerEventHandler pointerPressedHandler = RichEditBox_OnPointerPressed;
|
||||
PointerEventHandler pointerMovedHandler = RichEditBox_OnPointerMoved;
|
||||
|
||||
_suggestionPopup = (Popup)GetTemplateChild(PartSuggestionsPopup);
|
||||
_richEditBox = (RichEditBox)GetTemplateChild(PartRichEditBox);
|
||||
_suggestionsList = (ListViewBase)GetTemplateChild(PartSuggestionsList);
|
||||
_suggestionsContainer = (Border)GetTemplateChild(PartSuggestionsContainer);
|
||||
ConditionallyLoadElement(Header, PartHeaderContentPresenter);
|
||||
ConditionallyLoadElement(Description, PartDescriptionPresenter);
|
||||
|
||||
if (_richEditBox != null)
|
||||
{
|
||||
_richEditBox.SizeChanged -= RichEditBox_SizeChanged;
|
||||
_richEditBox.TextChanging -= RichEditBox_TextChanging;
|
||||
_richEditBox.TextChanged -= RichEditBox_TextChanged;
|
||||
_richEditBox.TextCompositionStarted -= RichEditBox_TextCompositionStarted;
|
||||
_richEditBox.TextCompositionChanged -= RichEditBox_TextCompositionChanged;
|
||||
_richEditBox.TextCompositionEnded -= RichEditBox_TextCompositionEnded;
|
||||
_richEditBox.SelectionChanging -= RichEditBox_SelectionChanging;
|
||||
_richEditBox.SelectionChanged -= RichEditBox_SelectionChanged;
|
||||
_richEditBox.Paste -= RichEditBox_Paste;
|
||||
_richEditBox.PreviewKeyDown -= RichEditBox_PreviewKeyDown;
|
||||
_richEditBox.RemoveHandler(PointerMovedEvent, pointerMovedHandler);
|
||||
_richEditBox.RemoveHandler(PointerPressedEvent, pointerPressedHandler);
|
||||
_richEditBox.ProcessKeyboardAccelerators -= RichEditBox_ProcessKeyboardAccelerators;
|
||||
|
||||
_richEditBox.SizeChanged += RichEditBox_SizeChanged;
|
||||
_richEditBox.TextChanging += RichEditBox_TextChanging;
|
||||
_richEditBox.TextChanged += RichEditBox_TextChanged;
|
||||
_richEditBox.TextCompositionStarted += RichEditBox_TextCompositionStarted;
|
||||
_richEditBox.TextCompositionChanged += RichEditBox_TextCompositionChanged;
|
||||
_richEditBox.TextCompositionEnded += RichEditBox_TextCompositionEnded;
|
||||
_richEditBox.SelectionChanging += RichEditBox_SelectionChanging;
|
||||
_richEditBox.SelectionChanged += RichEditBox_SelectionChanged;
|
||||
_richEditBox.Paste += RichEditBox_Paste;
|
||||
_richEditBox.PreviewKeyDown += RichEditBox_PreviewKeyDown;
|
||||
_richEditBox.AddHandler(PointerMovedEvent, pointerMovedHandler, true);
|
||||
_richEditBox.AddHandler(PointerPressedEvent, pointerPressedHandler, true);
|
||||
_richEditBox.ProcessKeyboardAccelerators += RichEditBox_ProcessKeyboardAccelerators;
|
||||
}
|
||||
|
||||
if (_suggestionsList != null)
|
||||
{
|
||||
_suggestionsList.ItemClick -= SuggestionsList_ItemClick;
|
||||
_suggestionsList.SizeChanged -= SuggestionsList_SizeChanged;
|
||||
_suggestionsList.GotFocus -= SuggestionList_GotFocus;
|
||||
|
||||
_suggestionsList.ItemClick += SuggestionsList_ItemClick;
|
||||
_suggestionsList.SizeChanged += SuggestionsList_SizeChanged;
|
||||
_suggestionsList.GotFocus += SuggestionList_GotFocus;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var view = (RichSuggestBox)d;
|
||||
view.ConditionallyLoadElement(e.NewValue, PartHeaderContentPresenter);
|
||||
}
|
||||
|
||||
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var view = (RichSuggestBox)d;
|
||||
view.ConditionallyLoadElement(e.NewValue, PartDescriptionPresenter);
|
||||
}
|
||||
|
||||
private static void OnSuggestionPopupPlacementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var view = (RichSuggestBox)d;
|
||||
view.UpdatePopupWidth();
|
||||
}
|
||||
|
||||
private static void OnPrefixesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var view = (RichSuggestBox)d;
|
||||
|
||||
var newValue = (string)e.NewValue;
|
||||
var prefixes = EnforcePrefixesRequirements(newValue);
|
||||
|
||||
if (newValue != prefixes)
|
||||
{
|
||||
view.SetValue(PrefixesProperty, prefixes);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCornerRadiusChanged(DependencyObject sender, DependencyProperty dp)
|
||||
{
|
||||
UpdateCornerRadii();
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_scrollViewer = _richEditBox?.FindDescendant<ScrollViewer>();
|
||||
}
|
||||
|
||||
private void OnLostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowSuggestionsPopup(false);
|
||||
}
|
||||
|
||||
private void SuggestionsList_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (this._suggestionPopup.IsOpen)
|
||||
{
|
||||
this.UpdatePopupOffset();
|
||||
}
|
||||
}
|
||||
|
||||
private void SuggestionList_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_richEditBox != null)
|
||||
{
|
||||
_richEditBox.Focus(FocusState.Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
private void RichEditBox_OnPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var pointer = e.GetCurrentPoint(this);
|
||||
if (this.TokenPointerOver != null)
|
||||
{
|
||||
this.InvokeTokenPointerOver(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
private void RichEditBox_SelectionChanging(RichEditBox sender, RichEditBoxSelectionChangingEventArgs args)
|
||||
{
|
||||
var selection = TextDocument.Selection;
|
||||
|
||||
if (selection.Type != SelectionType.InsertionPoint && selection.Type != SelectionType.Normal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var range = selection.GetClone();
|
||||
range.Expand(TextRangeUnit.Link);
|
||||
lock (_tokensLock)
|
||||
{
|
||||
if (!_tokens.ContainsKey(range.Link))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ExpandSelectionOnPartialTokenSelect(selection, range);
|
||||
}
|
||||
|
||||
private async void RichEditBox_SelectionChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectionChanged?.Invoke(this, e);
|
||||
|
||||
// During text composition changing (e.g. user typing with an IME),
|
||||
// SelectionChanged event is fired multiple times with each keystroke.
|
||||
// To reduce the number of suggestion requests, the request is made
|
||||
// in TextCompositionChanged handler instead.
|
||||
if (_textCompositionActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await RequestSuggestionsAsync();
|
||||
}
|
||||
|
||||
private void RichEditBox_OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ShowSuggestionsPopup(false);
|
||||
}
|
||||
|
||||
private async void RichEditBox_ProcessKeyboardAccelerators(UIElement sender, ProcessKeyboardAcceleratorEventArgs args)
|
||||
{
|
||||
var itemsList = _suggestionsList.Items;
|
||||
if (!_suggestionPopup.IsOpen || itemsList == null || itemsList.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var key = args.Key;
|
||||
switch (key)
|
||||
{
|
||||
case VirtualKey.Up when itemsList.Count == 1:
|
||||
case VirtualKey.Down when itemsList.Count == 1:
|
||||
args.Handled = true;
|
||||
UpdateSuggestionsListSelectedItem(1);
|
||||
break;
|
||||
|
||||
case VirtualKey.Up:
|
||||
args.Handled = true;
|
||||
_suggestionChoice = _suggestionChoice <= 0 ? itemsList.Count : _suggestionChoice - 1;
|
||||
UpdateSuggestionsListSelectedItem(this._suggestionChoice);
|
||||
break;
|
||||
|
||||
case VirtualKey.Down:
|
||||
args.Handled = true;
|
||||
_suggestionChoice = _suggestionChoice >= itemsList.Count ? 0 : _suggestionChoice + 1;
|
||||
UpdateSuggestionsListSelectedItem(this._suggestionChoice);
|
||||
break;
|
||||
|
||||
case VirtualKey.Enter when _suggestionsList.SelectedItem != null:
|
||||
args.Handled = true;
|
||||
await CommitSuggestionAsync(_suggestionsList.SelectedItem);
|
||||
break;
|
||||
|
||||
case VirtualKey.Escape:
|
||||
args.Handled = true;
|
||||
ShowSuggestionsPopup(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void RichEditBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.Key == VirtualKey.Tab && _suggestionPopup.IsOpen && _suggestionsList.SelectedItem != null)
|
||||
{
|
||||
e.Handled = true;
|
||||
await CommitSuggestionAsync(_suggestionsList.SelectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
private async void SuggestionsList_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
var selectedItem = e.ClickedItem;
|
||||
await CommitSuggestionAsync(selectedItem);
|
||||
}
|
||||
|
||||
private void RichEditBox_TextChanging(RichEditBox sender, RichEditBoxTextChangingEventArgs args)
|
||||
{
|
||||
if (_ignoreChange || !args.IsContentChanging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ignoreChange = true;
|
||||
ValidateTokensInDocument();
|
||||
TextDocument.EndUndoGroup();
|
||||
TextDocument.BeginUndoGroup();
|
||||
_ignoreChange = false;
|
||||
}
|
||||
|
||||
private void RichEditBox_TextChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
UpdateVisibleTokenList();
|
||||
TextChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void RichEditBox_TextCompositionStarted(RichEditBox sender, TextCompositionStartedEventArgs args)
|
||||
{
|
||||
_textCompositionActive = true;
|
||||
}
|
||||
|
||||
private async void RichEditBox_TextCompositionChanged(RichEditBox sender, TextCompositionChangedEventArgs args)
|
||||
{
|
||||
var range = TextDocument.GetRange(args.StartIndex == 0 ? 0 : args.StartIndex - 1, args.StartIndex + args.Length);
|
||||
await RequestSuggestionsAsync(range);
|
||||
}
|
||||
|
||||
private void RichEditBox_TextCompositionEnded(RichEditBox sender, TextCompositionEndedEventArgs args)
|
||||
{
|
||||
_textCompositionActive = false;
|
||||
}
|
||||
|
||||
private void RichEditBox_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
this.UpdatePopupWidth();
|
||||
this.UpdatePopupOffset();
|
||||
}
|
||||
|
||||
private async void RichEditBox_Paste(object sender, TextControlPasteEventArgs e)
|
||||
{
|
||||
Paste?.Invoke(this, e);
|
||||
|
||||
if (e.Handled || TextDocument == null || ClipboardPasteFormat != RichEditClipboardFormat.PlainText)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
var dataPackageView = Clipboard.GetContent();
|
||||
if (dataPackageView.Contains(StandardDataFormats.Text))
|
||||
{
|
||||
var text = await dataPackageView.GetTextAsync();
|
||||
TextDocument.Selection.SetText(TextSetOptions.Unhide, text);
|
||||
TextDocument.Selection.Collapse(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpandSelectionOnPartialTokenSelect(ITextSelection selection, ITextRange tokenRange)
|
||||
{
|
||||
switch (selection.Type)
|
||||
{
|
||||
case SelectionType.InsertionPoint:
|
||||
// Snap selection to token on click
|
||||
if (tokenRange.StartPosition < selection.StartPosition && selection.EndPosition < tokenRange.EndPosition)
|
||||
{
|
||||
selection.Expand(TextRangeUnit.Link);
|
||||
InvokeTokenSelected(selection);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SelectionType.Normal:
|
||||
// We do not want user to partially select a token since pasting to a partial token can break
|
||||
// the token tracking system, which can result in unwanted character formatting issues.
|
||||
if ((tokenRange.StartPosition <= selection.StartPosition && selection.EndPosition < tokenRange.EndPosition) ||
|
||||
(tokenRange.StartPosition < selection.StartPosition && selection.EndPosition <= tokenRange.EndPosition))
|
||||
{
|
||||
// TODO: Figure out how to expand selection without breaking selection flow (with Shift select or pointer sweep select)
|
||||
selection.Expand(TextRangeUnit.Link);
|
||||
InvokeTokenSelected(selection);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void InvokeTokenSelected(ITextSelection selection)
|
||||
{
|
||||
if (TokenSelected == null || !TryGetTokenFromRange(selection, out var token) || token.RangeEnd != selection.EndPosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TokenSelected.Invoke(this, new RichSuggestTokenSelectedEventArgs
|
||||
{
|
||||
Token = token,
|
||||
Range = selection.GetClone()
|
||||
});
|
||||
}
|
||||
|
||||
private void InvokeTokenPointerOver(PointerPoint pointer)
|
||||
{
|
||||
var pointerPosition = TransformToVisual(_richEditBox).TransformPoint(pointer.Position);
|
||||
var padding = _richEditBox.Padding;
|
||||
pointerPosition.X += HorizontalOffset - padding.Left;
|
||||
pointerPosition.Y += VerticalOffset - padding.Top;
|
||||
var range = TextDocument.GetRangeFromPoint(pointerPosition, PointOptions.ClientCoordinates);
|
||||
var linkRange = range.GetClone();
|
||||
range.Expand(TextRangeUnit.Character);
|
||||
range.GetRect(PointOptions.None, out var hitTestRect, out _);
|
||||
hitTestRect.X -= hitTestRect.Width;
|
||||
hitTestRect.Width *= 2;
|
||||
if (hitTestRect.Contains(pointerPosition) && linkRange.Expand(TextRangeUnit.Link) > 0 &&
|
||||
TryGetTokenFromRange(linkRange, out var token))
|
||||
{
|
||||
this.TokenPointerOver.Invoke(this, new RichSuggestTokenPointerOverEventArgs
|
||||
{
|
||||
Token = token,
|
||||
Range = linkRange,
|
||||
CurrentPoint = pointer
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RequestSuggestionsAsync(ITextRange range = null)
|
||||
{
|
||||
string prefix;
|
||||
string query;
|
||||
var currentQuery = _currentQuery;
|
||||
var queryFound = range == null
|
||||
? TryExtractQueryFromSelection(out prefix, out query, out range)
|
||||
: TryExtractQueryFromRange(range, out prefix, out query);
|
||||
|
||||
if (queryFound && prefix == currentQuery?.Prefix && query == currentQuery?.QueryText &&
|
||||
range.EndPosition == currentQuery?.Range.EndPosition && _suggestionPopup.IsOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var previousTokenSource = currentQuery?.CancellationTokenSource;
|
||||
if (!(previousTokenSource?.IsCancellationRequested ?? true))
|
||||
{
|
||||
previousTokenSource.Cancel();
|
||||
}
|
||||
|
||||
if (queryFound)
|
||||
{
|
||||
using var tokenSource = new CancellationTokenSource();
|
||||
_currentQuery = new RichSuggestQuery
|
||||
{
|
||||
Prefix = prefix,
|
||||
QueryText = query,
|
||||
Range = range,
|
||||
CancellationTokenSource = tokenSource
|
||||
};
|
||||
|
||||
var cancellationToken = tokenSource.Token;
|
||||
var eventArgs = new SuggestionRequestedEventArgs { QueryText = query, Prefix = prefix };
|
||||
if (SuggestionRequested != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await SuggestionRequested.InvokeAsync(this, eventArgs, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!eventArgs.Cancel)
|
||||
{
|
||||
_suggestionChoice = 0;
|
||||
ShowSuggestionsPopup(_suggestionsList?.Items?.Count > 0);
|
||||
}
|
||||
|
||||
tokenSource.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowSuggestionsPopup(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task CommitSuggestionAsync(object selectedItem)
|
||||
{
|
||||
var currentQuery = _currentQuery;
|
||||
var range = currentQuery?.Range.GetClone();
|
||||
var id = Guid.NewGuid();
|
||||
var prefix = currentQuery?.Prefix;
|
||||
var query = currentQuery?.QueryText;
|
||||
|
||||
// range has length of 0 at the end of the commit.
|
||||
// Checking length == 0 to avoid committing twice.
|
||||
if (prefix == null || query == null || range == null || range.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var textBefore = range.Text;
|
||||
var format = CreateTokenFormat(range);
|
||||
var eventArgs = new SuggestionChosenEventArgs
|
||||
{
|
||||
Id = id,
|
||||
Prefix = prefix,
|
||||
QueryText = query,
|
||||
SelectedItem = selectedItem,
|
||||
DisplayText = query,
|
||||
Format = format
|
||||
};
|
||||
|
||||
if (SuggestionChosen != null)
|
||||
{
|
||||
await SuggestionChosen.InvokeAsync(this, eventArgs);
|
||||
}
|
||||
|
||||
var text = eventArgs.DisplayText;
|
||||
|
||||
// Since this operation is async, the document may have changed at this point.
|
||||
// Double check if the range still has the expected query.
|
||||
if (string.IsNullOrEmpty(text) || textBefore != range.Text ||
|
||||
!TryExtractQueryFromRange(range, out var testPrefix, out var testQuery) ||
|
||||
testPrefix != prefix || testQuery != query)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_tokensLock)
|
||||
{
|
||||
var displayText = prefix + text;
|
||||
|
||||
_ignoreChange = true;
|
||||
var committed = TryCommitSuggestionIntoDocument(range, displayText, id, eventArgs.Format ?? format);
|
||||
TextDocument.EndUndoGroup();
|
||||
TextDocument.BeginUndoGroup();
|
||||
_ignoreChange = false;
|
||||
|
||||
if (committed)
|
||||
{
|
||||
var token = new RichSuggestToken(id, displayText) { Active = true, Item = selectedItem };
|
||||
token.UpdateTextRange(range);
|
||||
_tokens.TryAdd(range.Link, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryCommitSuggestionIntoDocument(ITextRange range, string displayText, Guid id, ITextCharacterFormat format, bool addTrailingSpace = true)
|
||||
{
|
||||
// We don't want to set text when the display text doesn't change since it may lead to unexpected caret move.
|
||||
range.GetText(TextGetOptions.NoHidden, out var existingText);
|
||||
if (existingText != displayText)
|
||||
{
|
||||
range.SetText(TextSetOptions.Unhide, displayText);
|
||||
}
|
||||
|
||||
var formatBefore = range.CharacterFormat.GetClone();
|
||||
range.CharacterFormat.SetClone(format);
|
||||
PadRange(range, formatBefore);
|
||||
range.Link = $"\"{id}\"";
|
||||
|
||||
// In some rare case, setting Link can fail. Only observed when interacting with Undo/Redo feature.
|
||||
if (range.Link != $"\"{id}\"")
|
||||
{
|
||||
range.Delete(TextRangeUnit.Story, -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (addTrailingSpace)
|
||||
{
|
||||
var clone = range.GetClone();
|
||||
clone.Collapse(false);
|
||||
clone.SetText(TextSetOptions.Unhide, " ");
|
||||
clone.Collapse(false);
|
||||
TextDocument.Selection.SetRange(clone.EndPosition, clone.EndPosition);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ValidateTokensInDocument()
|
||||
{
|
||||
lock (_tokensLock)
|
||||
{
|
||||
foreach (var (_, token) in _tokens)
|
||||
{
|
||||
token.Active = false;
|
||||
}
|
||||
}
|
||||
|
||||
ForEachLinkInDocument(TextDocument, ValidateTokenFromRange);
|
||||
}
|
||||
|
||||
private void ValidateTokenFromRange(ITextRange range)
|
||||
{
|
||||
if (range.Length == 0 || !TryGetTokenFromRange(range, out var token))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicate tokens. This can happen if the user copies and pastes the token multiple times.
|
||||
if (token.Active && token.RangeStart != range.StartPosition && token.RangeEnd != range.EndPosition)
|
||||
{
|
||||
lock (_tokensLock)
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
if (TryCommitSuggestionIntoDocument(range, token.DisplayText, guid, CreateTokenFormat(range), false))
|
||||
{
|
||||
token = new RichSuggestToken(guid, token.DisplayText) { Active = true, Item = token.Item };
|
||||
token.UpdateTextRange(range);
|
||||
_tokens.Add(range.Link, token);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.ToString() != range.Text)
|
||||
{
|
||||
range.Delete(TextRangeUnit.Story, 0);
|
||||
token.Active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
token.UpdateTextRange(range);
|
||||
token.Active = true;
|
||||
}
|
||||
|
||||
private void ConditionallyLoadElement(object property, string elementName)
|
||||
{
|
||||
if (property != null && GetTemplateChild(elementName) is UIElement presenter)
|
||||
{
|
||||
presenter.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSuggestionsListSelectedItem(int choice)
|
||||
{
|
||||
var itemsList = _suggestionsList.Items;
|
||||
if (itemsList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_suggestionsList.SelectedItem = choice == 0 ? null : itemsList[choice - 1];
|
||||
_suggestionsList.ScrollIntoView(_suggestionsList.SelectedItem);
|
||||
}
|
||||
|
||||
private void ShowSuggestionsPopup(bool show)
|
||||
{
|
||||
if (_suggestionPopup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this._suggestionPopup.IsOpen = show;
|
||||
if (!show)
|
||||
{
|
||||
this._suggestionChoice = 0;
|
||||
this._suggestionPopup.VerticalOffset = 0;
|
||||
this._suggestionPopup.HorizontalOffset = 0;
|
||||
this._suggestionsList.SelectedItem = null;
|
||||
this._suggestionsList.ScrollIntoView(this._suggestionsList.Items?.FirstOrDefault());
|
||||
UpdateCornerRadii();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePopupWidth()
|
||||
{
|
||||
if (this._suggestionsContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.PopupPlacement == SuggestionPopupPlacementMode.Attached)
|
||||
{
|
||||
this._suggestionsContainer.MaxWidth = double.PositiveInfinity;
|
||||
this._suggestionsContainer.Width = this._richEditBox.ActualWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._suggestionsContainer.MaxWidth = this._richEditBox.ActualWidth;
|
||||
this._suggestionsContainer.Width = double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate whether to open the suggestion list up or down depends on how much screen space is available
|
||||
/// </summary>
|
||||
private void UpdatePopupOffset()
|
||||
{
|
||||
if (this._suggestionsContainer == null || this._suggestionPopup == null || this._richEditBox == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this._richEditBox.TextDocument.Selection.GetRect(PointOptions.None, out var selectionRect, out _);
|
||||
Thickness padding = this._richEditBox.Padding;
|
||||
selectionRect.X -= HorizontalOffset;
|
||||
selectionRect.Y -= VerticalOffset;
|
||||
|
||||
// Update horizontal offset
|
||||
if (this.PopupPlacement == SuggestionPopupPlacementMode.Attached)
|
||||
{
|
||||
this._suggestionPopup.HorizontalOffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double editBoxWidth = this._richEditBox.ActualWidth - padding.Left - padding.Right;
|
||||
if (this._suggestionPopup.HorizontalOffset == 0 && editBoxWidth > 0)
|
||||
{
|
||||
var normalizedX = selectionRect.X / editBoxWidth;
|
||||
this._suggestionPopup.HorizontalOffset =
|
||||
(this._richEditBox.ActualWidth - this._suggestionsContainer.ActualWidth) * normalizedX;
|
||||
}
|
||||
}
|
||||
|
||||
// Update vertical offset
|
||||
double downOffset = this._richEditBox.ActualHeight;
|
||||
double upOffset = -this._suggestionsContainer.ActualHeight;
|
||||
if (this.PopupPlacement == SuggestionPopupPlacementMode.Floating)
|
||||
{
|
||||
downOffset = selectionRect.Bottom + padding.Top + padding.Bottom;
|
||||
upOffset += selectionRect.Top;
|
||||
}
|
||||
|
||||
if (this._suggestionPopup.VerticalOffset == 0)
|
||||
{
|
||||
if (IsElementOnScreen(this._suggestionsContainer, offsetY: downOffset) &&
|
||||
(IsElementInsideWindow(this._suggestionsContainer, offsetY: downOffset) ||
|
||||
!IsElementInsideWindow(this._suggestionsContainer, offsetY: upOffset) ||
|
||||
!IsElementOnScreen(this._suggestionsContainer, offsetY: upOffset)))
|
||||
{
|
||||
this._suggestionPopup.VerticalOffset = downOffset;
|
||||
this._popupOpenDown = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._suggestionPopup.VerticalOffset = upOffset;
|
||||
this._popupOpenDown = false;
|
||||
}
|
||||
|
||||
UpdateCornerRadii();
|
||||
}
|
||||
else
|
||||
{
|
||||
this._suggestionPopup.VerticalOffset = this._popupOpenDown ? downOffset : upOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set corner radii so that inner corners, where suggestion list and text box connect, are square.
|
||||
/// This only applies when <see cref="PopupPlacement"/> is set to <see cref="SuggestionPopupPlacementMode.Attached"/>.
|
||||
/// </summary>
|
||||
/// https://docs.microsoft.com/en-us/windows/apps/design/style/rounded-corner#when-not-to-round
|
||||
private void UpdateCornerRadii()
|
||||
{
|
||||
if (this._richEditBox == null || this._suggestionsContainer == null ||
|
||||
!ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this._richEditBox.CornerRadius = CornerRadius;
|
||||
this._suggestionsContainer.CornerRadius = PopupCornerRadius;
|
||||
|
||||
if (this._suggestionPopup.IsOpen && PopupPlacement == SuggestionPopupPlacementMode.Attached)
|
||||
{
|
||||
if (this._popupOpenDown)
|
||||
{
|
||||
var cornerRadius = new CornerRadius(CornerRadius.TopLeft, CornerRadius.TopRight, 0, 0);
|
||||
this._richEditBox.CornerRadius = cornerRadius;
|
||||
var popupCornerRadius =
|
||||
new CornerRadius(0, 0, PopupCornerRadius.BottomRight, PopupCornerRadius.BottomLeft);
|
||||
this._suggestionsContainer.CornerRadius = popupCornerRadius;
|
||||
}
|
||||
else
|
||||
{
|
||||
var cornerRadius = new CornerRadius(0, 0, CornerRadius.BottomRight, CornerRadius.BottomLeft);
|
||||
this._richEditBox.CornerRadius = cornerRadius;
|
||||
var popupCornerRadius =
|
||||
new CornerRadius(PopupCornerRadius.TopLeft, PopupCornerRadius.TopRight, 0, 0);
|
||||
this._suggestionsContainer.CornerRadius = popupCornerRadius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryExtractQueryFromSelection(out string prefix, out string query, out ITextRange range)
|
||||
{
|
||||
prefix = string.Empty;
|
||||
query = string.Empty;
|
||||
range = null;
|
||||
if (TextDocument.Selection.Type != SelectionType.InsertionPoint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if selection is on existing link (suggestion)
|
||||
var expandCount = TextDocument.Selection.GetClone().Expand(TextRangeUnit.Link);
|
||||
if (expandCount != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var selection = TextDocument.Selection.GetClone();
|
||||
selection.MoveStart(TextRangeUnit.Word, -1);
|
||||
if (selection.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
range = selection;
|
||||
if (TryExtractQueryFromRange(selection, out prefix, out query))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
selection.MoveStart(TextRangeUnit.Word, -1);
|
||||
if (TryExtractQueryFromRange(selection, out prefix, out query))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
range = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryExtractQueryFromRange(ITextRange range, out string prefix, out string query)
|
||||
{
|
||||
prefix = string.Empty;
|
||||
query = string.Empty;
|
||||
range.GetText(TextGetOptions.NoHidden, out var possibleQuery);
|
||||
if (possibleQuery.Length > 0 && Prefixes.Contains(possibleQuery[0]) &&
|
||||
!possibleQuery.Any(char.IsWhiteSpace) && string.IsNullOrEmpty(range.Link))
|
||||
{
|
||||
if (possibleQuery.Length == 1)
|
||||
{
|
||||
prefix = possibleQuery;
|
||||
return true;
|
||||
}
|
||||
|
||||
prefix = possibleQuery[0].ToString();
|
||||
query = possibleQuery.Substring(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private ITextCharacterFormat CreateTokenFormat(ITextRange range)
|
||||
{
|
||||
var format = range.CharacterFormat.GetClone();
|
||||
if (this.TokenBackground != null)
|
||||
{
|
||||
format.BackgroundColor = this.TokenBackground.Color;
|
||||
}
|
||||
|
||||
if (this.TokenForeground != null)
|
||||
{
|
||||
format.ForegroundColor = this.TokenForeground.Color;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
private void UpdateVisibleTokenList()
|
||||
{
|
||||
lock (_tokensLock)
|
||||
{
|
||||
var toBeRemoved = _visibleTokens.Where(x => !x.Active || !_tokens.ContainsKey($"\"{x.Id}\"")).ToArray();
|
||||
|
||||
foreach (var elem in toBeRemoved)
|
||||
{
|
||||
_visibleTokens.Remove(elem);
|
||||
}
|
||||
|
||||
var toBeAdded = _tokens.Where(pair => pair.Value.Active && !_visibleTokens.Contains(pair.Value))
|
||||
.Select(pair => pair.Value).ToArray();
|
||||
|
||||
foreach (var elem in toBeAdded)
|
||||
{
|
||||
_visibleTokens.Add(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:contract8Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,8)"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
|
||||
<!-- Default style for RichSuggestBox -->
|
||||
<Style BasedOn="{StaticResource DefaultRichSuggestBoxStyle}"
|
||||
TargetType="controls:RichSuggestBox" />
|
||||
|
||||
<Style x:Key="DefaultRichSuggestBoxStyle"
|
||||
TargetType="controls:RichSuggestBox">
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Top" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Prefixes" Value="@" />
|
||||
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextControlForeground}" />
|
||||
<Setter Property="Background" Value="{ThemeResource TextControlBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource TextControlBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="TokenForeground" Value="{ThemeResource ContentLinkForegroundColor}" />
|
||||
<Setter Property="TokenBackground" Value="Transparent" />
|
||||
<Setter Property="RichEditBoxStyle" Value="{StaticResource DefaultRichEditBoxStyle}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="PopupCornerRadius" Value="{ThemeResource OverlayCornerRadius}" />
|
||||
<Setter Property="ClipboardCopyFormat" Value="AllFormats" />
|
||||
<Setter Property="ClipboardPasteFormat" Value="AllFormats" />
|
||||
<Setter Property="DisabledFormattingAccelerators" Value="None" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:RichSuggestBox">
|
||||
<Grid x:Name="LayoutRoot">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{ThemeResource TextBoxTopHeaderMargin}"
|
||||
VerticalAlignment="Top"
|
||||
x:Load="False"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
FontWeight="Normal"
|
||||
Foreground="{ThemeResource TextControlHeaderForeground}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<RichEditBox x:Name="RichEditBox"
|
||||
Grid.Row="1"
|
||||
Margin="0"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Canvas.ZIndex="0"
|
||||
ClipboardCopyFormat="{TemplateBinding ClipboardCopyFormat}"
|
||||
DesiredCandidateWindowAlignment="BottomEdge"
|
||||
DisabledFormattingAccelerators="{TemplateBinding DisabledFormattingAccelerators}"
|
||||
FontFamily="{TemplateBinding FontFamily}"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontStretch="{TemplateBinding FontStretch}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
PlaceholderText="{TemplateBinding PlaceholderText}"
|
||||
ScrollViewer.BringIntoViewOnFocusChange="False"
|
||||
Style="{TemplateBinding RichEditBoxStyle}"
|
||||
UseSystemFocusVisuals="{TemplateBinding UseSystemFocusVisuals}" />
|
||||
|
||||
<ContentPresenter x:Name="DescriptionPresenter"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
x:Load="False"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding Description}"
|
||||
Foreground="{ThemeResource SystemControlDescriptionTextForegroundBrush}" />
|
||||
|
||||
<Popup x:Name="SuggestionsPopup"
|
||||
Grid.Row="1"
|
||||
contract8Present:ShouldConstrainToRootBounds="False">
|
||||
<Border x:Name="SuggestionsContainer"
|
||||
Padding="{ThemeResource AutoSuggestListMargin}"
|
||||
Background="{ThemeResource AutoSuggestBoxSuggestionsListBackground}"
|
||||
BorderBrush="{ThemeResource AutoSuggestBoxSuggestionsListBorderBrush}"
|
||||
BorderThickness="{ThemeResource AutoSuggestListBorderThemeThickness}">
|
||||
<ListView x:Name="SuggestionsList"
|
||||
MaxHeight="{ThemeResource AutoSuggestListMaxHeight}"
|
||||
Margin="{ThemeResource AutoSuggestListPadding}"
|
||||
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
|
||||
Footer="{TemplateBinding PopupFooter}"
|
||||
FooterTemplate="{TemplateBinding PopupFooterTemplate}"
|
||||
Header="{TemplateBinding PopupHeader}"
|
||||
HeaderTemplate="{TemplateBinding PopupHeaderTemplate}"
|
||||
IsItemClickEnabled="True"
|
||||
ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
|
||||
ItemTemplate="{TemplateBinding ItemTemplate}"
|
||||
ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
|
||||
ItemsSource="{TemplateBinding ItemsSource}" />
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
|
@ -0,0 +1,23 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A structure for <see cref="RichSuggestBox"/> to keep track of the current query internally.
|
||||
/// </summary>
|
||||
internal class RichSuggestQuery
|
||||
{
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public string QueryText { get; set; }
|
||||
|
||||
public ITextRange Range { get; set; }
|
||||
|
||||
public CancellationTokenSource CancellationTokenSource { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
// 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;
|
||||
using System.ComponentModel;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// RichSuggestToken describes a suggestion token in the document.
|
||||
/// </summary>
|
||||
public class RichSuggestToken : INotifyPropertyChanged
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the token ID.
|
||||
/// </summary>
|
||||
public Guid Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text displayed in the document.
|
||||
/// </summary>
|
||||
public string DisplayText { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the suggested item associated with this token.
|
||||
/// </summary>
|
||||
public object Item { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start position of the text range.
|
||||
/// </summary>
|
||||
public int RangeStart { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end position of the text range.
|
||||
/// </summary>
|
||||
public int RangeEnd { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start position of the token in number of characters.
|
||||
/// </summary>
|
||||
public int Position => _range?.GetIndex(TextRangeUnit.Character) - 1 ?? 0;
|
||||
|
||||
internal bool Active { get; set; }
|
||||
|
||||
private ITextRange _range;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RichSuggestToken"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">Token ID</param>
|
||||
/// <param name="displayText">Text in the document</param>
|
||||
public RichSuggestToken(Guid id, string displayText)
|
||||
{
|
||||
Id = id;
|
||||
DisplayText = displayText;
|
||||
}
|
||||
|
||||
internal void UpdateTextRange(ITextRange range)
|
||||
{
|
||||
bool rangeStartChanged = RangeStart != range.StartPosition;
|
||||
bool rangeEndChanged = RangeEnd != range.EndPosition;
|
||||
bool positionChanged = _range == null || rangeStartChanged;
|
||||
_range = range.GetClone();
|
||||
RangeStart = _range.StartPosition;
|
||||
RangeEnd = _range.EndPosition;
|
||||
|
||||
if (rangeStartChanged)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RangeStart)));
|
||||
}
|
||||
|
||||
if (rangeEndChanged)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RangeEnd)));
|
||||
}
|
||||
|
||||
if (positionChanged)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Position)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"HYPERLINK \"{Id}\"\u200B{DisplayText}\u200B";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// 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;
|
||||
using Windows.UI.Input;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for <see cref="RichSuggestBox.TokenPointerOver"/> event.
|
||||
/// </summary>
|
||||
public class RichSuggestTokenPointerOverEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the selected token.
|
||||
/// </summary>
|
||||
public RichSuggestToken Token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the range associated with the token.
|
||||
/// </summary>
|
||||
public ITextRange Range { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a PointerPoint object relative to the <see cref="RichSuggestBox"/> control.
|
||||
/// </summary>
|
||||
public PointerPoint CurrentPoint { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// 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;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for <see cref="RichSuggestBox.TokenSelected"/> event.
|
||||
/// </summary>
|
||||
public class RichSuggestTokenSelectedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the selected token.
|
||||
/// </summary>
|
||||
public RichSuggestToken Token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the range associated with the token.
|
||||
/// </summary>
|
||||
public ITextRange Range { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// 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;
|
||||
using Microsoft.Toolkit.Deferred;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for the <see cref="RichSuggestBox.SuggestionChosen"/> event.
|
||||
/// </summary>
|
||||
public class SuggestionChosenEventArgs : DeferredEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the query used for this token.
|
||||
/// </summary>
|
||||
public string QueryText { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prefix character used for this token.
|
||||
/// </summary>
|
||||
public string Prefix { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display text.
|
||||
/// </summary>
|
||||
public string DisplayText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the suggestion item associated with this token.
|
||||
/// </summary>
|
||||
public object SelectedItem { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets token ID.
|
||||
/// </summary>
|
||||
public Guid Id { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ITextCharacterFormat"/> object used to format the display text for this token.
|
||||
/// </summary>
|
||||
public ITextCharacterFormat Format { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// 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 Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Placement modes for the suggestion popup in <see cref="RichSuggestBox"/>.
|
||||
/// </summary>
|
||||
public enum SuggestionPopupPlacementMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Suggestion popup floats above or below the typing caret.
|
||||
/// </summary>
|
||||
Floating,
|
||||
|
||||
/// <summary>
|
||||
/// Suggestion popup is attached to either the top edge or the bottom edge of the text box.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In this mode, popup width will be text box's width and the interior corners that connect the text box and the popup are square.
|
||||
/// This is the same behavior as in <see cref="AutoSuggestBox"/>.
|
||||
/// </remarks>
|
||||
Attached
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
||||
using Microsoft.Toolkit.Deferred;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide data for <see cref="RichSuggestBox.SuggestionRequested"/> event.
|
||||
/// </summary>
|
||||
public class SuggestionRequestedEventArgs : DeferredCancelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the prefix character used for the query.
|
||||
/// </summary>
|
||||
public string Prefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the query for suggestions.
|
||||
/// </summary>
|
||||
public string QueryText { get; set; }
|
||||
}
|
||||
}
|
|
@ -8,5 +8,6 @@
|
|||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Input/RangeSelector/RangeSelector.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Input/RemoteDevicePicker/RemoteDevicePicker.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
|
@ -157,6 +157,37 @@ namespace Microsoft.Toolkit.Uwp.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 Microsoft.Toolkit.Uwp.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,15 +2,16 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.Deferred;
|
||||
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
|
||||
using Microsoft.Toolkit.Uwp.UI.Helpers;
|
||||
using Windows.System;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
|
@ -77,6 +78,17 @@ namespace Microsoft.Toolkit.Uwp.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 Microsoft.Toolkit.Uwp.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 Microsoft.Toolkit.Uwp.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 Microsoft.Toolkit.Uwp.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>
|
||||
|
|