From 5ae673a81d9f4781cf46e89666df73166b464644 Mon Sep 17 00:00:00 2001 From: Poker Date: Sun, 17 Nov 2024 04:17:38 +0800 Subject: [PATCH 1/7] push --- components/Behaviors/OpenSolution.bat | 3 + components/Behaviors/samples/Assets/icon.png | Bin 0 -> 2216 bytes .../samples/Behaviors.Samples.csproj | 21 ++ .../samples/BehaviorsTemplatedSample.xaml | 16 ++ .../samples/BehaviorsTemplatedSample.xaml.cs | 21 ++ .../BehaviorsTemplatedStyleCustomSample.xaml | 26 ++ ...ehaviorsTemplatedStyleCustomSample.xaml.cs | 21 ++ .../samples/BehaviorsXbindBackedSample.xaml | 16 ++ .../BehaviorsXbindBackedSample.xaml.cs | 21 ++ ...BehaviorsXbindBackedStyleCustomSample.xaml | 28 ++ ...aviorsXbindBackedStyleCustomSample.xaml.cs | 21 ++ .../Behaviors/samples/Dependencies.props | 35 +++ .../Behaviors/samples/ItemsView Behaviors.md | 65 +++++ .../NeedMoreItemTriggerBehaviorSample.xaml | 44 ++++ .../NeedMoreItemTriggerBehaviorSample.xaml.cs | 81 ++++++ components/Behaviors/src/Behaviors.cs | 108 ++++++++ .../src/BehaviorsStyle_ClassicBinding.xaml | 62 +++++ .../Behaviors/src/BehaviorsStyle_xBind.xaml | 69 +++++ .../src/BehaviorsStyle_xBind.xaml.cs | 20 ++ .../Behaviors/src/Behaviors_ClassicBinding.cs | 94 +++++++ components/Behaviors/src/Behaviors_xBind.cs | 71 +++++ ...ityToolkit.WinUI.Controls.Behaviors.csproj | 13 + components/Behaviors/src/Dependencies.props | 35 +++ .../Behaviors/src/LoadMoreItemBehavior.cs | 244 ++++++++++++++++++ components/Behaviors/src/MultiTarget.props | 9 + .../src/NeedMoreItemTriggerBehavior.cs | 119 +++++++++ components/Behaviors/src/Themes/Generic.xaml | 10 + .../Behaviors/tests/Behaviors.Tests.projitems | 23 ++ .../Behaviors/tests/Behaviors.Tests.shproj | 13 + .../tests/ExampleBehaviorsTestClass.cs | 134 ++++++++++ .../tests/ExampleBehaviorsTestPage.xaml | 14 + .../tests/ExampleBehaviorsTestPage.xaml.cs | 16 ++ global.json | 2 +- tooling | 2 +- 34 files changed, 1475 insertions(+), 2 deletions(-) create mode 100644 components/Behaviors/OpenSolution.bat create mode 100644 components/Behaviors/samples/Assets/icon.png create mode 100644 components/Behaviors/samples/Behaviors.Samples.csproj create mode 100644 components/Behaviors/samples/BehaviorsTemplatedSample.xaml create mode 100644 components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs create mode 100644 components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml create mode 100644 components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs create mode 100644 components/Behaviors/samples/BehaviorsXbindBackedSample.xaml create mode 100644 components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs create mode 100644 components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml create mode 100644 components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs create mode 100644 components/Behaviors/samples/Dependencies.props create mode 100644 components/Behaviors/samples/ItemsView Behaviors.md create mode 100644 components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml create mode 100644 components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs create mode 100644 components/Behaviors/src/Behaviors.cs create mode 100644 components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml create mode 100644 components/Behaviors/src/BehaviorsStyle_xBind.xaml create mode 100644 components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs create mode 100644 components/Behaviors/src/Behaviors_ClassicBinding.cs create mode 100644 components/Behaviors/src/Behaviors_xBind.cs create mode 100644 components/Behaviors/src/CommunityToolkit.WinUI.Controls.Behaviors.csproj create mode 100644 components/Behaviors/src/Dependencies.props create mode 100644 components/Behaviors/src/LoadMoreItemBehavior.cs create mode 100644 components/Behaviors/src/MultiTarget.props create mode 100644 components/Behaviors/src/NeedMoreItemTriggerBehavior.cs create mode 100644 components/Behaviors/src/Themes/Generic.xaml create mode 100644 components/Behaviors/tests/Behaviors.Tests.projitems create mode 100644 components/Behaviors/tests/Behaviors.Tests.shproj create mode 100644 components/Behaviors/tests/ExampleBehaviorsTestClass.cs create mode 100644 components/Behaviors/tests/ExampleBehaviorsTestPage.xaml create mode 100644 components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs diff --git a/components/Behaviors/OpenSolution.bat b/components/Behaviors/OpenSolution.bat new file mode 100644 index 000000000..814a56d4b --- /dev/null +++ b/components/Behaviors/OpenSolution.bat @@ -0,0 +1,3 @@ +@ECHO OFF + +powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %* \ No newline at end of file diff --git a/components/Behaviors/samples/Assets/icon.png b/components/Behaviors/samples/Assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8435bcaa9fc371ca8e92db07ae596e0d57c8b9b0 GIT binary patch literal 2216 zcmV;Z2v_%sP);M1&0drDELIAGL9O(c600d`2O+f$vv5yP=YKs(Je9p7&Ka&|n*ecc!Pix~iV~>Yi6*wXL?Fq6O}_ef#!O2;q#X&d1-r zFW&c8*L5NOYWz*lA^xUE>kGNx-uL6v^z@vr)V_TA(vQ#Yoo1$I^Fv;IKkA`?U*Z6OoBe{XX@DLpL{F3+>RV2oP732rn@P@98F ziH~#f`tA7f<8t}(<$sLlUkdm_STvOKGYXY{9St3tGiu1>duN9F9aTe*kTXOCkoLYj zr)5cJP?i}f+kBoB8b~Q1rbJi`730DXGLx}aCV-6tC522QjZs)X03S%#=jXopQNe7G z@dlToiDbGDSy!a_DD)NYWiV?sGap0Dq-qgnA&~LHECw(NL8T=) z1ct(ga2;|14nD@qHwmVQ0;2|YUomW^!U#`7l>>&Bh;u)pT<|$jFoqk6%HTc~^RQ@( z5hZ5X^n7`vt!*nY9@rFRqF{^wF`}&H4I4JdfddC*W@e@$oS$Q04aThBn*gT3)URMp z>G{o@H*)RTHCb6%8B?H}yjcXcUm9p(K=nWD0vP!PaCP$@(k!31bkrIJ!2)-tl96*+&}@I6!3M8qT~Q2?u8 zcy@MHS+IzlwjvzRGx~+*l>(Gi6$!C8Jgi;2)=XVKe*CB(K745TS`)G2;nuBN9cqsP zh3?9y5yI0j3rZt%Q;V3i*L;-@bj}#EBDL zi-O{(`k1i!rOBH%ZPF-Qh^8PnZ{F0;pFa!x1_lMnUFbI&&8gFC}WVB;VU)DL&bgeyDBGT1Uw=yFE1xQ zlXdIXN%Xx|Sv6HKV+J+vFk{k20ni@>s(7s{7```cTQ%Vf8y`xM()#6VjL@l-2UZ)1 zMAlp(2n!()MJZ+A7DT29I(lXL!EfSTFx}nSy)d`h{3}%2w00Npq^YO)x9XqDcq0Kb`t5*xfQqpFjCI=5f3~jf78p^G}no2Fzdc;)IysSSZVr(fukQEprykDy< zqbZnxBN8(`!7W?1$e}}r1c|2>qh?{>d-v{@?c2BeJQ<2<$;_cXbnDiw*59|?yLU^< zSA=eeDMyH&Dn;O?U<@moWoql!uTK{z=_+aO*s+8Ar_R9^^Jcn6#~@GNDp>Q(&lq|B z{JGq_cdrQ3>E_6hBQiff?~J4|4F&T## ze2Ot?F7i1RStkmn!!cL^bKdFtd(+&zckeRXgNN#PA!`aD zjaC7J&2fZoFH{s(d8}#I6o7s`O)w?K`{bKiENsKV!h(MK^r(|LX> z*~rJxUHVoXzp-XhUqnbQUAiRq@89q5e?*H1Nj(o2FJ5%B>%JZY`Gw<0&+dhGuj!C7 z?uYbkO5XY{KIV*@{VRoX8s`fm<068)Y!=w) zn?l)IstDTf!(Iu(8i;vTaeMOuE%QV$RY7$1cP-aqS07(jX4W{bFHp!#3m}TTAaaF3L~n9Q)s^3xP5nw5 zM&HvBw5z{TbdA3!8Di)wIs`5S;W!fVdWDbiH|P}wlVfDMuB&#@so4j+uC6L7Q#M38 z*q?Pnc_gqfql3rn?qh)V@~B|(78+7U zKOCcocD&A`ELB-^?%cVvanNF%vtSH&^_LVuBqdkprWDoUyaLCf$s5zvc?rH#NGXk= qmFA_t_5FR}!i7I&wXL?Ful)xU?DJJ%Hwu*i0000 + + + + Behaviors + + + + + + + + + + + + + + + + diff --git a/components/Behaviors/samples/BehaviorsTemplatedSample.xaml b/components/Behaviors/samples/BehaviorsTemplatedSample.xaml new file mode 100644 index 000000000..3cf1a527c --- /dev/null +++ b/components/Behaviors/samples/BehaviorsTemplatedSample.xaml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs b/components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs new file mode 100644 index 000000000..cb580e742 --- /dev/null +++ b/components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs @@ -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. + +namespace BehaviorsExperiment.Samples; + +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] +// Single values without a colon are used for both label and value. +// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] + +[ToolkitSample(id: nameof(BehaviorsTemplatedSample), "Templated control", description: "A sample for showing how to create and use a templated control.")] +public sealed partial class BehaviorsTemplatedSample : Page +{ + public BehaviorsTemplatedSample() + { + this.InitializeComponent(); + } +} diff --git a/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml b/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml new file mode 100644 index 000000000..50740bdc0 --- /dev/null +++ b/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs b/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs new file mode 100644 index 000000000..978a76806 --- /dev/null +++ b/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs @@ -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. + +namespace BehaviorsExperiment.Samples; + +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] +// Single values without a colon are used for both label and value. +// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, true, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] + +[ToolkitSample(id: nameof(BehaviorsTemplatedStyleCustomSample), "Templated control (restyled)", description: "A sample for showing how to create a use and templated control with a custom style.")] +public sealed partial class BehaviorsTemplatedStyleCustomSample : Page +{ + public BehaviorsTemplatedStyleCustomSample() + { + this.InitializeComponent(); + } +} diff --git a/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml b/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml new file mode 100644 index 000000000..01769ca42 --- /dev/null +++ b/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs b/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs new file mode 100644 index 000000000..3545c6bca --- /dev/null +++ b/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs @@ -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. + +namespace BehaviorsExperiment.Samples; + +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] +// Single values without a colon are used for both label and value. +// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] + +[ToolkitSample(id: nameof(BehaviorsXbindBackedSample), "Backed templated control", description: "A sample for showing how to create and use a templated control with a backed resource dictionary.")] +public sealed partial class BehaviorsXbindBackedSample : Page +{ + public BehaviorsXbindBackedSample() + { + this.InitializeComponent(); + } +} diff --git a/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml b/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml new file mode 100644 index 000000000..e47a10725 --- /dev/null +++ b/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs b/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs new file mode 100644 index 000000000..d33b57410 --- /dev/null +++ b/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs @@ -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. + +namespace BehaviorsExperiment.Samples; + +[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] +// Single values without a colon are used for both label and value. +// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). +[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, true, Title = "FontSize")] +[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] +[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] + +[ToolkitSample(id: nameof(BehaviorsXbindBackedStyleCustomSample), "Backed templated control (restyled)", description: "A sample for showing how to create and use a templated control with a backed resource dictionary and a custom style.")] +public sealed partial class BehaviorsXbindBackedStyleCustomSample : Page +{ + public BehaviorsXbindBackedStyleCustomSample() + { + this.InitializeComponent(); + } +} diff --git a/components/Behaviors/samples/Dependencies.props b/components/Behaviors/samples/Dependencies.props new file mode 100644 index 000000000..d7912ab7d --- /dev/null +++ b/components/Behaviors/samples/Dependencies.props @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/Behaviors/samples/ItemsView Behaviors.md b/components/Behaviors/samples/ItemsView Behaviors.md new file mode 100644 index 000000000..42d84820b --- /dev/null +++ b/components/Behaviors/samples/ItemsView Behaviors.md @@ -0,0 +1,65 @@ +--- +title: ItemsView Behaviors +author: githubaccount +description: A set of behaviors for the ItemsView control. +keywords: Behaviors, Control, Behavior +dev_langs: + - csharp +category: Xaml +subcategory: Behaviors +discussion-id: 532 +issue-id: 0 +icon: assets/icon.png +--- + + + + + + + + + +# ItemsView Behaviors + +TODO: Fill in information about this experiment and how to get started here... + +## Custom Control + +You can inherit from an existing component as well, like `Panel`, this example shows a control without a +XAML Style that will be more light-weight to consume by an app developer: + +> + +## Templated Controls + +The Toolkit is built with templated controls. This provides developers a flexible way to restyle components +easily while still inheriting the general functionality a control provides. The examples below show +how a component can use a default style and then get overridden by the end developer. + +TODO: Two types of templated control building methods are shown. Delete these if you're building a custom component. +Otherwise, pick one method for your component and delete the files related to the unchosen `_ClassicBinding` or `_xBind` +classes (and the custom non-suffixed one as well). Then, rename your component to just be your component name. + +The `_ClassicBinding` class shows the traditional method used to develop components with best practices. + +### Implict style + +> [!SAMPLE BehaviorsTemplatedSample] + +### Custom style + +> [!SAMPLE BehaviorsTemplatedStyleCustomSample] + +## Templated Controls with x:Bind + +This is an _experimental_ new way to define components which allows for the use of x:Bind within the style. + +### Implict style + +> [!SAMPLE BehaviorsXbindBackedSample] + +### Custom style + +> [!SAMPLE BehaviorsXbindBackedStyleCustomSample] + diff --git a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml new file mode 100644 index 000000000..8de1a7400 --- /dev/null +++ b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs new file mode 100644 index 000000000..d1945f5e4 --- /dev/null +++ b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using CommunityToolkit.WinUI.Controls; + +namespace BehaviorsExperiment.Samples; + +/// +/// An example sample page of a custom control inheriting from Panel. +/// +[ToolkitSampleNumericOption("LoadingOffset", 100d, 500d, 50d, Title = "LoadingOffset")] + +[ToolkitSample(id: nameof(NeedMoreItemTriggerBehaviorSample), "Custom control", description: $"A sample for showing how to create and use a {nameof(Behaviors)} custom control.")] +public sealed partial class NeedMoreItemTriggerBehaviorSample : Page +{ + private static readonly Random _random = new Random(); + + public NeedMoreItemTriggerBehaviorSample() + { + this.InitializeComponent(); + } + + public static SolidColorBrush GetColor() + { + var rand = _random.Next(4); + var color = rand switch + { + 0 => Colors.Red, + 1 => Colors.Blue, + 2 => Colors.Green, + 3 => Colors.Yellow, + _ => Colors.Black + }; + return new(color); + } + + public MyCollection ViewModels { get; } = [.. Enumerable.Repeat(0, 50).Select(_ => _random.Next(5, 10) * 500)]; +} + +public partial class MyCollection : ObservableCollection, ISupportIncrementalLoading +{ + private static readonly Random _random = new Random(); + + public int Num + { + get => _num; + set + { + if (_num == value) + { + return; + } + + _num = value; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(Num))); + } + } + + private int _num; + + public bool HasMoreItems => true; + + public IAsyncOperation LoadMoreItemsAsync(uint count) + { + return LoadMoreItemsTaskAsync(count).AsAsyncOperation(); + } + + public async Task LoadMoreItemsTaskAsync(uint count) + { + await Task.Delay(1000); + foreach (var i in Enumerable.Repeat(0, (int)count).Select(_ => _random.Next(5, 10) * 500)) + { + Add(i); + } + + Num++; + return new LoadMoreItemsResult { Count = count }; + } +} diff --git a/components/Behaviors/src/Behaviors.cs b/components/Behaviors/src/Behaviors.cs new file mode 100644 index 000000000..d18cb94f8 --- /dev/null +++ b/components/Behaviors/src/Behaviors.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace CommunityToolkit.WinUI.Controls; + +/// +/// This is an example control based off of the BoxPanel sample here: https://docs.microsoft.com/windows/apps/design/layout/boxpanel-example-custom-panel. If you need this similar sort of layout component for an application, see UniformGrid in the Toolkit. +/// It is provided as an example of how to inherit from another control like . +/// You can choose to start here or from the or example components. Remove unused components and rename as appropriate. +/// +public partial class Behaviors : Panel +{ + /// + /// Identifies the property. + /// + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(Behaviors), new PropertyMetadata(null, OnOrientationChanged)); + + /// + /// Gets the preference of the rows/columns when there are a non-square number of children. Defaults to Vertical. + /// + public Orientation Orientation + { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + + // Invalidate our layout when the property changes. + private static void OnOrientationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) + { + if (dependencyObject is Behaviors panel) + { + panel.InvalidateMeasure(); + } + } + + // Store calculations we want to use between the Measure and Arrange methods. + int _columnCount; + double _cellWidth, _cellHeight; + + protected override Size MeasureOverride(Size availableSize) + { + // Determine the square that can contain this number of items. + var maxrc = (int)Math.Ceiling(Math.Sqrt(Children.Count)); + // Get an aspect ratio from availableSize, decides whether to trim row or column. + var aspectratio = availableSize.Width / availableSize.Height; + if (Orientation == Orientation.Vertical) { aspectratio = 1 / aspectratio; } + + int rowcount; + + // Now trim this square down to a rect, many times an entire row or column can be omitted. + if (aspectratio > 1) + { + rowcount = maxrc; + _columnCount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc; + } + else + { + rowcount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc; + _columnCount = maxrc; + } + + // Now that we have a column count, divide available horizontal, that's our cell width. + _cellWidth = (int)Math.Floor(availableSize.Width / _columnCount); + // Next get a cell height, same logic of dividing available vertical by rowcount. + _cellHeight = Double.IsInfinity(availableSize.Height) ? Double.PositiveInfinity : availableSize.Height / rowcount; + + double maxcellheight = 0; + + foreach (UIElement child in Children) + { + child.Measure(new Size(_cellWidth, _cellHeight)); + maxcellheight = (child.DesiredSize.Height > maxcellheight) ? child.DesiredSize.Height : maxcellheight; + } + + return LimitUnboundedSize(availableSize, maxcellheight); + } + + // This method limits the panel height when no limit is imposed by the panel's parent. + // That can happen to height if the panel is close to the root of main app window. + // In this case, base the height of a cell on the max height from desired size + // and base the height of the panel on that number times the #rows. + Size LimitUnboundedSize(Size input, double maxcellheight) + { + if (Double.IsInfinity(input.Height)) + { + input.Height = maxcellheight * _columnCount; + _cellHeight = maxcellheight; + } + return input; + } + + protected override Size ArrangeOverride(Size finalSize) + { + int count = 1; + double x, y; + foreach (UIElement child in Children) + { + x = (count - 1) % _columnCount * _cellWidth; + y = ((int)(count - 1) / _columnCount) * _cellHeight; + Point anchorPoint = new Point(x, y); + child.Arrange(new Rect(anchorPoint, child.DesiredSize)); + count++; + } + return finalSize; + } +} diff --git a/components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml b/components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml new file mode 100644 index 000000000..8a6b6dd91 --- /dev/null +++ b/components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + 4,4,4,4 + + + + + + + + diff --git a/components/Behaviors/src/BehaviorsStyle_xBind.xaml b/components/Behaviors/src/BehaviorsStyle_xBind.xaml new file mode 100644 index 000000000..09e762736 --- /dev/null +++ b/components/Behaviors/src/BehaviorsStyle_xBind.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + 4,4,4,4 + + + + + + + + diff --git a/components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs b/components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs new file mode 100644 index 000000000..69d6f866c --- /dev/null +++ b/components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs @@ -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. + +namespace CommunityToolkit.WinUI.Controls; + +/// +/// Backing code for this resource dictionary. +/// +public sealed partial class BehaviorsStyle_xBind : ResourceDictionary +{ + // NOTICE + // This file only exists to enable x:Bind in the resource dictionary. + // Do not add code here. + // Instead, add code-behind to your templated control. + public BehaviorsStyle_xBind() + { + this.InitializeComponent(); + } +} diff --git a/components/Behaviors/src/Behaviors_ClassicBinding.cs b/components/Behaviors/src/Behaviors_ClassicBinding.cs new file mode 100644 index 000000000..c23b76d26 --- /dev/null +++ b/components/Behaviors/src/Behaviors_ClassicBinding.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace CommunityToolkit.WinUI.Controls; + +/// +/// An example templated control. +/// +[TemplatePart(Name = nameof(PART_HelloWorld), Type = typeof(TextBlock))] +public partial class Behaviors_ClassicBinding : Control +{ + /// + /// Creates a new instance of the class. + /// + public Behaviors_ClassicBinding() + { + this.DefaultStyleKey = typeof(Behaviors_ClassicBinding); + } + + /// + /// The primary text block that displays "Hello world". + /// + protected TextBlock? PART_HelloWorld { get; private set; } + + /// + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + // Detach all attached events when a new template is applied. + if (PART_HelloWorld is not null) + { + PART_HelloWorld.PointerEntered -= Element_PointerEntered; + } + + // Attach events when the template is applied and the control is loaded. + PART_HelloWorld = GetTemplateChild(nameof(PART_HelloWorld)) as TextBlock; + + if (PART_HelloWorld is not null) + { + PART_HelloWorld.PointerEntered += Element_PointerEntered; + } + } + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ItemPaddingProperty = DependencyProperty.Register( + nameof(ItemPadding), + typeof(Thickness), + typeof(Behaviors_ClassicBinding), + new PropertyMetadata(defaultValue: new Thickness(0))); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register( + nameof(MyProperty), + typeof(string), + typeof(Behaviors_ClassicBinding), + new PropertyMetadata(defaultValue: string.Empty, (d, e) => ((Behaviors_ClassicBinding)d).OnMyPropertyChanged((string)e.OldValue, (string)e.NewValue))); + + /// + /// Gets or sets an example string. A basic DependencyProperty example. + /// + public string MyProperty + { + get => (string)GetValue(MyPropertyProperty); + set => SetValue(MyPropertyProperty, value); + } + + /// + /// Gets or sets a padding for an item. A basic DependencyProperty example. + /// + public Thickness ItemPadding + { + get => (Thickness)GetValue(ItemPaddingProperty); + set => SetValue(ItemPaddingProperty, value); + } + + protected virtual void OnMyPropertyChanged(string oldValue, string newValue) + { + // Do something with the changed value. + } + + public void Element_PointerEntered(object sender, PointerRoutedEventArgs e) + { + if (sender is TextBlock text) + { + text.Opacity = 1; + } + } +} diff --git a/components/Behaviors/src/Behaviors_xBind.cs b/components/Behaviors/src/Behaviors_xBind.cs new file mode 100644 index 000000000..46f213ae7 --- /dev/null +++ b/components/Behaviors/src/Behaviors_xBind.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace CommunityToolkit.WinUI.Controls; + +/// +/// An example templated control. +/// +public partial class Behaviors_xBind: Control +{ + /// + /// Creates a new instance of the class. + /// + public Behaviors_xBind() + { + this.DefaultStyleKey = typeof(Behaviors_xBind); + + // Allows directly using this control as the x:DataType in the template. + this.DataContext = this; + } + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty ItemPaddingProperty = DependencyProperty.Register( + nameof(ItemPadding), + typeof(Thickness), + typeof(Behaviors_xBind), + new PropertyMetadata(defaultValue: new Thickness(0))); + + /// + /// The backing for the property. + /// + public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register( + nameof(MyProperty), + typeof(string), + typeof(Behaviors_xBind), + new PropertyMetadata(defaultValue: string.Empty, (d, e) => ((Behaviors_xBind)d).OnMyPropertyChanged((string)e.OldValue, (string)e.NewValue))); + + /// + /// Gets or sets an example string. A basic DependencyProperty example. + /// + public string MyProperty + { + get => (string)GetValue(MyPropertyProperty); + set => SetValue(MyPropertyProperty, value); + } + + /// + /// Gets or sets a padding for an item. A basic DependencyProperty example. + /// + public Thickness ItemPadding + { + get => (Thickness)GetValue(ItemPaddingProperty); + set => SetValue(ItemPaddingProperty, value); + } + + protected virtual void OnMyPropertyChanged(string oldValue, string newValue) + { + // Do something with the changed value. + } + + public void Element_PointerEntered(object sender, PointerRoutedEventArgs e) + { + if (sender is TextBlock text) + { + text.Opacity = 1; + } + } +} diff --git a/components/Behaviors/src/CommunityToolkit.WinUI.Controls.Behaviors.csproj b/components/Behaviors/src/CommunityToolkit.WinUI.Controls.Behaviors.csproj new file mode 100644 index 000000000..43551ee24 --- /dev/null +++ b/components/Behaviors/src/CommunityToolkit.WinUI.Controls.Behaviors.csproj @@ -0,0 +1,13 @@ + + + + + Behaviors + This package contains Behaviors. + + CommunityToolkit.WinUI.Controls.BehaviorsRns + + + + + diff --git a/components/Behaviors/src/Dependencies.props b/components/Behaviors/src/Dependencies.props new file mode 100644 index 000000000..d7912ab7d --- /dev/null +++ b/components/Behaviors/src/Dependencies.props @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/Behaviors/src/LoadMoreItemBehavior.cs b/components/Behaviors/src/LoadMoreItemBehavior.cs new file mode 100644 index 000000000..fdc814e40 --- /dev/null +++ b/components/Behaviors/src/LoadMoreItemBehavior.cs @@ -0,0 +1,244 @@ +#region Copyright + +// 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. + +#endregion + +#if WINAPPSDK + +using System.Collections; +using System.Collections.Specialized; +using Microsoft.Xaml.Interactivity; + +namespace CommunityToolkit.WinUI.Controls; + +/// +/// A behavior that makes support . +/// +public class LoadMoreItemBehavior : Behavior +{ + /// + /// Identifies the property. + /// + public static readonly DependencyProperty LoadingOffsetProperty = DependencyProperty.Register( + nameof(LoadingOffset), + typeof(double), + typeof(LoadMoreItemBehavior), + new PropertyMetadata(100d)); + + /// + /// Gets or sets Distance of content from scrolling to bottom. + /// + public double LoadingOffset + { + get => (double)this.GetValue(LoadingOffsetProperty); + set => this.SetValue(LoadingOffsetProperty, value); + } + + /// + /// Identifies the property. + /// + public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register( + nameof(IsActive), + typeof(bool), + typeof(LoadMoreItemBehavior), + new PropertyMetadata(true)); + + /// + /// Gets a value indicating whether the behavior is active. + /// + public bool IsActive + { + get => (bool)this.GetValue(IsActiveProperty); + set => this.SetValue(IsActiveProperty, value); + } + + /// + /// Identifies the property. + /// + public static readonly DependencyProperty IsLoadingMoreProperty = DependencyProperty.Register( + nameof(IsLoadingMore), + typeof(bool), + typeof(LoadMoreItemBehavior), + new PropertyMetadata(false)); + + /// + /// Gets or sets if more items are being loaded. + /// + public bool IsLoadingMore + { + get => (bool)this.GetValue(IsLoadingMoreProperty); + set => this.SetValue(IsLoadingMoreProperty, value); + } + + /// + /// Identifies the property. + /// + public static readonly DependencyProperty LoadCountProperty = DependencyProperty.Register( + nameof(LoadCount), + typeof(int), + typeof(LoadMoreItemBehavior), + new PropertyMetadata(20)); + + /// + /// Gets or sets the "count" parameter when triggering . + /// + public int LoadCount + { + get => (int)this.GetValue(LoadCountProperty); + set => this.SetValue(LoadCountProperty, value); + } + + /// + /// Raised when more items need to be loaded. + /// + public event Func>? LoadMoreRequested; + + private MUXC.ItemsRepeater? ItemsRepeater => (MUXC.ItemsRepeater?)this.ScrollView?.Content; + + private ScrollView? ScrollView => this.AssociatedObject.ScrollView; + + private long _scrollViewOnPropertyChangedToken; + private long _itemsSourceOnPropertyChangedToken; + + private INotifyCollectionChanged? _lastObservableCollection; + + /// + protected override void OnAttached() + { + this.LoadMoreRequested += async (sender, args) => + { + if (sender.ItemsSource is ISupportIncrementalLoading sil) + { + _ = await sil.LoadMoreItemsAsync((uint)this.LoadCount); + return sil.HasMoreItems; + } + + return false; + }; + this._scrollViewOnPropertyChangedToken = this.AssociatedObject.RegisterPropertyChangedCallback(ItemsView.ScrollViewProperty, this.ScrollViewOnPropertyChanged); + this._itemsSourceOnPropertyChangedToken = this.RegisterPropertyChangedCallback(ItemsView.ItemsSourceProperty, this.ItemsSourceOnPropertyChanged); + } + + /// + protected override void OnDetaching() + { + this.AssociatedObject.UnregisterPropertyChangedCallback(ItemsView.ScrollViewProperty, this._scrollViewOnPropertyChangedToken); + this.AssociatedObject.UnregisterPropertyChangedCallback(ItemsView.ItemsSourceProperty, this._itemsSourceOnPropertyChangedToken); + if (this._lastObservableCollection is not null) + this._lastObservableCollection.CollectionChanged -= this.TryRaiseLoadMoreRequested; + if (this.ItemsRepeater is not null) + this.ItemsRepeater.SizeChanged -= this.TryRaiseLoadMoreRequested; + if (this.ScrollView is not null) + this.ScrollView.ViewChanged -= this.TryRaiseLoadMoreRequested; + } + + /// + /// When the data source changes or , . + /// This method reloads the data. + /// This method is intended to solve the problem of reloading data when the data source changes and the 's does not change + /// + private async void ItemsSourceOnPropertyChanged(DependencyObject sender, DependencyProperty dp) + { + if (sender is ItemsView { ItemsSource: ISupportIncrementalLoading sil }) + { + if (sil is INotifyCollectionChanged ncc) + { + if (this._lastObservableCollection is not null) + this._lastObservableCollection.CollectionChanged -= this.TryRaiseLoadMoreRequested; + + this._lastObservableCollection = ncc; + ncc.CollectionChanged += this.TryRaiseLoadMoreRequested; + } + + // On the first load, the `ScrollView` is not yet initialized and can be given to `AdvancedItemsView_OnSizeChanged` to trigger. + if (this.ScrollView is not null) + await this.TryRaiseLoadMoreRequestedAsync(); + } + } + + private async void TryRaiseLoadMoreRequested(object? sender, object e) => await this.TryRaiseLoadMoreRequestedAsync(); + + /// + /// After this method, the will be triggered + /// + /// + /// is the key to continuous loading. + /// When new data is loaded, it makes the of the larger, + /// Or when the is changed to a different source or set for the first time, becomes 0. + /// This method can reload the data. + /// + private void ScrollViewOnPropertyChanged(DependencyObject sender, DependencyProperty dp) + { + this.AssociatedObject.UnregisterPropertyChangedCallback(ItemsView.ScrollViewProperty, this._scrollViewOnPropertyChangedToken); + if (this.ScrollView is null) + return; + this.ScrollView.ViewChanged += this.TryRaiseLoadMoreRequested; + if (this.ItemsRepeater is not null) + this.ItemsRepeater.SizeChanged += this.TryRaiseLoadMoreRequested; + } + + /// + /// Determines if the scroll view has scrolled to the bottom, and if so triggers the . + /// This event will only cause the source to load at most once + /// + public async Task TryRaiseLoadMoreRequestedAsync() + { + if (this.ScrollView is null) + return; + + var loadMore = true; + // Load until a new item is loaded in + while (loadMore) + { + if (!this.IsActive || this.IsLoadingMore) + return; + + // LoadMoreRequested is only triggered when the view is not filled. + if ((this.ScrollView.ScrollableHeight is 0 && this.ScrollView.ScrollableWidth is 0) || + (this.ScrollView.ScrollableHeight > 0 && + this.ScrollView.ScrollableHeight - this.LoadingOffset < this.ScrollView.VerticalOffset) || + (this.ScrollView.ScrollableWidth > 0 && + this.ScrollView.ScrollableWidth - this.LoadingOffset < this.ScrollView.HorizontalOffset)) + { + this.IsLoadingMore = true; + var before = this.GetItemsCount(); + if (this.LoadMoreRequested is not null && await this.LoadMoreRequested(this.AssociatedObject, EventArgs.Empty)) + { + var after = this.GetItemsCount(); + // This can be set to the count of items in a row, + // so that it can continue to load even if the count of items loaded is too small. + // Generally, 20 items will be loaded at a time, + // and the count of items in a row is usually less than 10, so it is set to 10 here. + if (before + 10 <= after) + loadMore = false; + } + // No more items or ItemsSource is null + else + loadMore = false; + + this.IsLoadingMore = false; + } + else + { + // There is no need to continue loading if it fills up the view + loadMore = false; + } + } + } + + private int GetItemsCount() + { + return this.AssociatedObject.ItemsSource switch + { + ICollection list => list.Count, + IEnumerable enumerable => enumerable.Cast().Count(), + null => 0, + _ => throw new ArgumentOutOfRangeException(nameof(this.AssociatedObject.ItemsSource)) + }; + } +} + +#endif diff --git a/components/Behaviors/src/MultiTarget.props b/components/Behaviors/src/MultiTarget.props new file mode 100644 index 000000000..b11c19426 --- /dev/null +++ b/components/Behaviors/src/MultiTarget.props @@ -0,0 +1,9 @@ + + + + uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android; + + \ No newline at end of file diff --git a/components/Behaviors/src/NeedMoreItemTriggerBehavior.cs b/components/Behaviors/src/NeedMoreItemTriggerBehavior.cs new file mode 100644 index 000000000..9fb0a1515 --- /dev/null +++ b/components/Behaviors/src/NeedMoreItemTriggerBehavior.cs @@ -0,0 +1,119 @@ +#region Copyright + +// 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. + +#endregion + +#if WINAPPSDK + +using Microsoft.Xaml.Interactivity; + +namespace CommunityToolkit.WinUI.Controls; + +/// +/// A behavior that triggers actions when the is scrolled to the bottom. +/// +[TypeConstraint(typeof(ItemsView))] +public class NeedMoreItemTriggerBehavior : Trigger +{ + /// + /// Identifies the property. + /// + public static readonly DependencyProperty LoadingOffsetProperty = DependencyProperty.Register( + nameof(LoadingOffset), + typeof(double), + typeof(NeedMoreItemTriggerBehavior), + new PropertyMetadata(100d)); + + /// + /// Gets or sets Distance of content from scrolling to bottom. + /// + public double LoadingOffset + { + get => (double)this.GetValue(LoadingOffsetProperty); + set => this.SetValue(LoadingOffsetProperty, value); + } + + /// + /// Identifies the property. + /// + public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register( + nameof(IsActive), + typeof(bool), + typeof(NeedMoreItemTriggerBehavior), + new PropertyMetadata(true)); + + /// + /// Gets a value indicating whether the trigger is active. + /// + public bool IsActive + { + get => (bool)this.GetValue(IsActiveProperty); + set => this.SetValue(IsActiveProperty, value); + } + + private ItemsRepeater? ItemsRepeater => (ItemsRepeater?)this.ScrollView?.Content; + + private ScrollView? ScrollView => this.AssociatedObject.ScrollView; + + private long _scrollViewOnPropertyChangedToken; + + /// + protected override void OnAttached() + { + this._scrollViewOnPropertyChangedToken = this.AssociatedObject.RegisterPropertyChangedCallback(ItemsView.ScrollViewProperty, this.ScrollViewOnPropertyChanged); + } + + /// + protected override void OnDetaching() + { + this.AssociatedObject.UnregisterPropertyChangedCallback(ItemsView.ScrollViewProperty, this._scrollViewOnPropertyChangedToken); + if (this.ItemsRepeater is not null) + this.ItemsRepeater.SizeChanged -= this.TryRaiseLoadMoreRequested; + if (this.ScrollView is not null) + this.ScrollView.ViewChanged -= TryRaiseLoadMoreRequested; + } + + private void TryRaiseLoadMoreRequested(object? sender, object e) => this.TryRaiseLoadMoreRequested(); + + /// + /// After this method, the will be triggered + /// + /// + /// is the key to continuous loading. + /// When new data is loaded, it makes the of the larger, + /// Or when the is changed to a different source or set for the first time, becomes 0. + /// This method can reload the data. + /// + private void ScrollViewOnPropertyChanged(DependencyObject sender, DependencyProperty dp) + { + this.AssociatedObject.UnregisterPropertyChangedCallback(ItemsView.ScrollViewProperty, this._scrollViewOnPropertyChangedToken); + if (this.ScrollView is null) + return; + this.ScrollView.ViewChanged += TryRaiseLoadMoreRequested; + if (this.ItemsRepeater is not null) + this.ItemsRepeater.SizeChanged += this.TryRaiseLoadMoreRequested; + } + + /// + /// Determines if the scroll view has scrolled to the bottom, and if so triggers the . + /// This event will only cause the source to load at most once + /// + public void TryRaiseLoadMoreRequested() + { + if (this.ScrollView is null || !this.IsActive) + return; + + // Is only triggered when the view is not filled. + if ((this.ScrollView.ScrollableHeight is 0 && this.ScrollView.ScrollableWidth is 0) || + (this.ScrollView.ScrollableHeight > 0 && + this.ScrollView.ScrollableHeight - this.LoadingOffset < this.ScrollView.VerticalOffset) || + (this.ScrollView.ScrollableWidth > 0 && + this.ScrollView.ScrollableWidth - this.LoadingOffset < this.ScrollView.HorizontalOffset)) + Interaction.ExecuteActions(this.AssociatedObject, this.Actions, null); + } +} + +#endif diff --git a/components/Behaviors/src/Themes/Generic.xaml b/components/Behaviors/src/Themes/Generic.xaml new file mode 100644 index 000000000..56609e75c --- /dev/null +++ b/components/Behaviors/src/Themes/Generic.xaml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/components/Behaviors/tests/Behaviors.Tests.projitems b/components/Behaviors/tests/Behaviors.Tests.projitems new file mode 100644 index 000000000..aa9bebb6d --- /dev/null +++ b/components/Behaviors/tests/Behaviors.Tests.projitems @@ -0,0 +1,23 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 04B3CAE5-42E9-435E-B234-2C6BAC8D11C9 + + + BehaviorsExperiment.Tests + + + + + ExampleBehaviorsTestPage.xaml + + + + + Designer + MSBuild:Compile + + + \ No newline at end of file diff --git a/components/Behaviors/tests/Behaviors.Tests.shproj b/components/Behaviors/tests/Behaviors.Tests.shproj new file mode 100644 index 000000000..f43924891 --- /dev/null +++ b/components/Behaviors/tests/Behaviors.Tests.shproj @@ -0,0 +1,13 @@ + + + + 04B3CAE5-42E9-435E-B234-2C6BAC8D11C9 + 14.0 + + + + + + + + diff --git a/components/Behaviors/tests/ExampleBehaviorsTestClass.cs b/components/Behaviors/tests/ExampleBehaviorsTestClass.cs new file mode 100644 index 000000000..eab9cdb74 --- /dev/null +++ b/components/Behaviors/tests/ExampleBehaviorsTestClass.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.Tooling.TestGen; +using CommunityToolkit.Tests; +using CommunityToolkit.WinUI.Controls; + +namespace BehaviorsExperiment.Tests; + +[TestClass] +public partial class ExampleBehaviorsTestClass : VisualUITestBase +{ + // If you don't need access to UI objects directly or async code, use this pattern. + [TestMethod] + public void SimpleSynchronousExampleTest() + { + var assembly = typeof(Behaviors).Assembly; + var type = assembly.GetType(typeof(Behaviors).FullName ?? string.Empty); + + Assert.IsNotNull(type, "Could not find Behaviors type."); + Assert.AreEqual(typeof(Behaviors), type, "Type of Behaviors does not match expected type."); + } + + // If you don't need access to UI objects directly, use this pattern. + [TestMethod] + public async Task SimpleAsyncExampleTest() + { + await Task.Delay(250); + + Assert.IsTrue(true); + } + + // Example that shows how to check for exception throwing. + [TestMethod] + public void SimpleExceptionCheckTest() + { + // If you need to check exceptions occur for invalid inputs, etc... + // Use Assert.ThrowsException to limit the scope to where you expect the error to occur. + // Otherwise, using the ExpectedException attribute could swallow or + // catch other issues in setup code. + Assert.ThrowsException(() => throw new NotImplementedException()); + } + + // The UIThreadTestMethod automatically dispatches to the UI for us to work with UI objects. + [UIThreadTestMethod] + public void SimpleUIAttributeExampleTest() + { + var component = new Behaviors(); + Assert.IsNotNull(component); + } + + // The UIThreadTestMethod can also easily grab a XAML Page for us by passing its type as a parameter. + // This lets us actually test a control as it would behave within an actual application. + // The page will already be loaded by the time your test is called. + [UIThreadTestMethod] + public void SimpleUIExamplePageTest(ExampleBehaviorsTestPage page) + { + // You can use the Toolkit Visual Tree helpers here to find the component by type or name: + var component = page.FindDescendant(); + + Assert.IsNotNull(component); + + var componentByName = page.FindDescendant("BehaviorsControl"); + + Assert.IsNotNull(componentByName); + } + + // You can still do async work with a UIThreadTestMethod as well. + [UIThreadTestMethod] + public async Task SimpleAsyncUIExamplePageTest(ExampleBehaviorsTestPage page) + { + // This helper can be used to wait for a rendering pass to complete. + // Note, this is already done by loading a Page with the [UIThreadTestMethod] helper. + await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { }); + + var component = page.FindDescendant(); + + Assert.IsNotNull(component); + } + + //// ----------------------------- ADVANCED TEST SCENARIOS ----------------------------- + + // If you need to use DataRow, you can use this pattern with the UI dispatch still. + // Otherwise, checkout the UIThreadTestMethod attribute above. + // See https://github.com/CommunityToolkit/Labs-Windows/issues/186 + [TestMethod] + public async Task ComplexAsyncUIExampleTest() + { + await EnqueueAsync(() => + { + var component = new Behaviors_ClassicBinding(); + Assert.IsNotNull(component); + }); + } + + // If you want to load other content not within a XAML page using the UIThreadTestMethod above. + // Then you can do that using the Load/UnloadTestContentAsync methods. + [TestMethod] + public async Task ComplexAsyncLoadUIExampleTest() + { + await EnqueueAsync(async () => + { + var component = new Behaviors_ClassicBinding(); + Assert.IsNotNull(component); + Assert.IsFalse(component.IsLoaded); + + await LoadTestContentAsync(component); + + Assert.IsTrue(component.IsLoaded); + + await UnloadTestContentAsync(component); + + Assert.IsFalse(component.IsLoaded); + }); + } + + // You can still use the UIThreadTestMethod to remove the extra layer for the dispatcher as well: + [UIThreadTestMethod] + public async Task ComplexAsyncLoadUIExampleWithoutDispatcherTest() + { + var component = new Behaviors_ClassicBinding(); + Assert.IsNotNull(component); + Assert.IsFalse(component.IsLoaded); + + await LoadTestContentAsync(component); + + Assert.IsTrue(component.IsLoaded); + + await UnloadTestContentAsync(component); + + Assert.IsFalse(component.IsLoaded); + } +} diff --git a/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml b/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml new file mode 100644 index 000000000..b968eb346 --- /dev/null +++ b/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs b/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs new file mode 100644 index 000000000..b9bface8f --- /dev/null +++ b/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs @@ -0,0 +1,16 @@ +// 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 BehaviorsExperiment.Tests; + +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +public sealed partial class ExampleBehaviorsTestPage : Page +{ + public ExampleBehaviorsTestPage() + { + this.InitializeComponent(); + } +} diff --git a/global.json b/global.json index 91187a7c4..d413698aa 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.403", + "version": "9.0.100", "rollForward": "latestFeature" }, "msbuild-sdks": diff --git a/tooling b/tooling index d71b08b2d..819a01607 160000 --- a/tooling +++ b/tooling @@ -1 +1 @@ -Subproject commit d71b08b2dccf94c3ceaeda99526679bc0cfc3b8a +Subproject commit 819a01607310fad9bf52097423977ace4b5c372a From f9e2db6e9628b6609aa80f485851203cd72e4ffa Mon Sep 17 00:00:00 2001 From: Poker Date: Sun, 17 Nov 2024 14:16:42 +0800 Subject: [PATCH 2/7] finish template --- .../samples/Behaviors.Samples.csproj | 14 ++- .../samples/BehaviorsTemplatedSample.xaml | 16 --- .../samples/BehaviorsTemplatedSample.xaml.cs | 21 ---- .../BehaviorsTemplatedStyleCustomSample.xaml | 26 ----- ...ehaviorsTemplatedStyleCustomSample.xaml.cs | 21 ---- .../samples/BehaviorsXbindBackedSample.xaml | 16 --- .../BehaviorsXbindBackedSample.xaml.cs | 21 ---- ...BehaviorsXbindBackedStyleCustomSample.xaml | 28 ----- ...aviorsXbindBackedStyleCustomSample.xaml.cs | 21 ---- .../Behaviors/samples/ItemsView Behaviors.md | 7 +- .../samples/LoadMoreItemBehaviorSample.xaml | 45 ++++++++ .../LoadMoreItemBehaviorSample.xaml.cs | 82 +++++++++++++ .../NeedMoreItemTriggerBehaviorSample.xaml | 41 ++++--- .../NeedMoreItemTriggerBehaviorSample.xaml.cs | 56 ++------- components/Behaviors/src/Behaviors.cs | 108 ------------------ .../src/BehaviorsStyle_ClassicBinding.xaml | 62 ---------- .../Behaviors/src/BehaviorsStyle_xBind.xaml | 69 ----------- .../src/BehaviorsStyle_xBind.xaml.cs | 20 ---- .../Behaviors/src/Behaviors_ClassicBinding.cs | 94 --------------- components/Behaviors/src/Behaviors_xBind.cs | 71 ------------ .../Behaviors/src/LoadMoreItemBehavior.cs | 2 +- components/Behaviors/src/Themes/Generic.xaml | 10 -- 22 files changed, 174 insertions(+), 677 deletions(-) delete mode 100644 components/Behaviors/samples/BehaviorsTemplatedSample.xaml delete mode 100644 components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs delete mode 100644 components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml delete mode 100644 components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs delete mode 100644 components/Behaviors/samples/BehaviorsXbindBackedSample.xaml delete mode 100644 components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs delete mode 100644 components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml delete mode 100644 components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs create mode 100644 components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml create mode 100644 components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs delete mode 100644 components/Behaviors/src/Behaviors.cs delete mode 100644 components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml delete mode 100644 components/Behaviors/src/BehaviorsStyle_xBind.xaml delete mode 100644 components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs delete mode 100644 components/Behaviors/src/Behaviors_ClassicBinding.cs delete mode 100644 components/Behaviors/src/Behaviors_xBind.cs delete mode 100644 components/Behaviors/src/Themes/Generic.xaml diff --git a/components/Behaviors/samples/Behaviors.Samples.csproj b/components/Behaviors/samples/Behaviors.Samples.csproj index 6ea7b9e95..82315d71c 100644 --- a/components/Behaviors/samples/Behaviors.Samples.csproj +++ b/components/Behaviors/samples/Behaviors.Samples.csproj @@ -8,12 +8,14 @@ - - - - - - + + + + + + + + diff --git a/components/Behaviors/samples/BehaviorsTemplatedSample.xaml b/components/Behaviors/samples/BehaviorsTemplatedSample.xaml deleted file mode 100644 index 3cf1a527c..000000000 --- a/components/Behaviors/samples/BehaviorsTemplatedSample.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs b/components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs deleted file mode 100644 index cb580e742..000000000 --- a/components/Behaviors/samples/BehaviorsTemplatedSample.xaml.cs +++ /dev/null @@ -1,21 +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. - -namespace BehaviorsExperiment.Samples; - -[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] -// Single values without a colon are used for both label and value. -// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] -[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] - -[ToolkitSample(id: nameof(BehaviorsTemplatedSample), "Templated control", description: "A sample for showing how to create and use a templated control.")] -public sealed partial class BehaviorsTemplatedSample : Page -{ - public BehaviorsTemplatedSample() - { - this.InitializeComponent(); - } -} diff --git a/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml b/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml deleted file mode 100644 index 50740bdc0..000000000 --- a/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs b/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs deleted file mode 100644 index 978a76806..000000000 --- a/components/Behaviors/samples/BehaviorsTemplatedStyleCustomSample.xaml.cs +++ /dev/null @@ -1,21 +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. - -namespace BehaviorsExperiment.Samples; - -[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] -// Single values without a colon are used for both label and value. -// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, true, Title = "FontSize")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] -[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] - -[ToolkitSample(id: nameof(BehaviorsTemplatedStyleCustomSample), "Templated control (restyled)", description: "A sample for showing how to create a use and templated control with a custom style.")] -public sealed partial class BehaviorsTemplatedStyleCustomSample : Page -{ - public BehaviorsTemplatedStyleCustomSample() - { - this.InitializeComponent(); - } -} diff --git a/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml b/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml deleted file mode 100644 index 01769ca42..000000000 --- a/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs b/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs deleted file mode 100644 index 3545c6bca..000000000 --- a/components/Behaviors/samples/BehaviorsXbindBackedSample.xaml.cs +++ /dev/null @@ -1,21 +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. - -namespace BehaviorsExperiment.Samples; - -[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] -// Single values without a colon are used for both label and value. -// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] -[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] - -[ToolkitSample(id: nameof(BehaviorsXbindBackedSample), "Backed templated control", description: "A sample for showing how to create and use a templated control with a backed resource dictionary.")] -public sealed partial class BehaviorsXbindBackedSample : Page -{ - public BehaviorsXbindBackedSample() - { - this.InitializeComponent(); - } -} diff --git a/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml b/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml deleted file mode 100644 index e47a10725..000000000 --- a/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs b/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs deleted file mode 100644 index d33b57410..000000000 --- a/components/Behaviors/samples/BehaviorsXbindBackedStyleCustomSample.xaml.cs +++ /dev/null @@ -1,21 +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. - -namespace BehaviorsExperiment.Samples; - -[ToolkitSampleBoolOption("IsTextVisible", true, Title = "IsVisible")] -// Single values without a colon are used for both label and value. -// To provide a different label for the value, separate with a colon surrounded by a single space on both sides ("label : value"). -[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, true, Title = "FontSize")] -[ToolkitSampleMultiChoiceOption("TextFontFamily", "Segoe UI", "Arial", "Consolas", Title = "Font family")] -[ToolkitSampleMultiChoiceOption("TextForeground", "Teal : #0ddc8c", "Sand : #e7a676", "Dull green : #5d7577", Title = "Text foreground")] - -[ToolkitSample(id: nameof(BehaviorsXbindBackedStyleCustomSample), "Backed templated control (restyled)", description: "A sample for showing how to create and use a templated control with a backed resource dictionary and a custom style.")] -public sealed partial class BehaviorsXbindBackedStyleCustomSample : Page -{ - public BehaviorsXbindBackedStyleCustomSample() - { - this.InitializeComponent(); - } -} diff --git a/components/Behaviors/samples/ItemsView Behaviors.md b/components/Behaviors/samples/ItemsView Behaviors.md index 42d84820b..418032adb 100644 --- a/components/Behaviors/samples/ItemsView Behaviors.md +++ b/components/Behaviors/samples/ItemsView Behaviors.md @@ -45,11 +45,12 @@ The `_ClassicBinding` class shows the traditional method used to develop compone ### Implict style -> [!SAMPLE BehaviorsTemplatedSample] +> [!SAMPLE NeedMoreItemTriggerBehaviorSample] + +> [!SAMPLE LoadMoreItemBehaviorSample] ### Custom style -> [!SAMPLE BehaviorsTemplatedStyleCustomSample] ## Templated Controls with x:Bind @@ -57,9 +58,7 @@ This is an _experimental_ new way to define components which allows for the use ### Implict style -> [!SAMPLE BehaviorsXbindBackedSample] ### Custom style -> [!SAMPLE BehaviorsXbindBackedStyleCustomSample] diff --git a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml new file mode 100644 index 000000000..34fbceb85 --- /dev/null +++ b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs new file mode 100644 index 000000000..00bffa88c --- /dev/null +++ b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs @@ -0,0 +1,82 @@ +// 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 WINAPPSDK + +using CommunityToolkit.WinUI.Controls; +using Microsoft.UI; + +namespace BehaviorsExperiment.Samples; + +[ToolkitSampleNumericOption("LoadingOffset", 100d, 50d, 500d, 50d, Title = "LoadingOffset")] + +[ToolkitSample(id: nameof(LoadMoreItemBehaviorSample), "LoadMoreItemBehavior", description: $"A sample for showing how to create and use a LoadMoreItemBehavior.")] +public sealed partial class LoadMoreItemBehaviorSample : Page +{ + private static readonly Random _random = new Random(); + + public LoadMoreItemBehaviorSample() + { + this.InitializeComponent(); + } + + public static SolidColorBrush GetColor() + { + var rand = _random.Next(4); + var color = rand switch + { + 0 => Colors.Red, + 1 => Colors.Blue, + 2 => Colors.Green, + 3 => Colors.Yellow, + _ => Colors.Black + }; + return new(color); + } + + public MyCollection ViewModels { get; } = [.. Enumerable.Repeat(0, 50).Select(_ => _random.Next(5, 10) * 500)]; +} + +public partial class MyCollection : ObservableCollection, ISupportIncrementalLoading +{ + private static readonly Random _random = new Random(); + + public int Num + { + get => _num; + set + { + if (_num == value) + { + return; + } + + _num = value; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(Num))); + } + } + + private int _num; + + public bool HasMoreItems => true; + + public IAsyncOperation LoadMoreItemsAsync(uint count) + { + return LoadMoreItemsTaskAsync(count).AsAsyncOperation(); + } + + public async Task LoadMoreItemsTaskAsync(uint count) + { + await Task.Delay(1000); + foreach (var i in Enumerable.Repeat(0, (int)count).Select(_ => _random.Next(5, 10) * 500)) + { + Add(i); + } + + Num++; + return new LoadMoreItemsResult { Count = count }; + } +} + +#endif diff --git a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml index 8de1a7400..be55182e7 100644 --- a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml +++ b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml @@ -4,40 +4,45 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:CommunityToolkit.WinUI.Controls" + xmlns:core="using:Microsoft.Xaml.Interactions.Core" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:local="using:BehaviorsExperiment.Samples" - xmlns:mxuc="using:Microsoft.UI.Xaml.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mxuc="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> - + - - - + + - + MinColumnSpacing="5" + MinItemHeight="100" + MinItemWidth="100" + MinRowSpacing="5" /> + - + + + + - + - + - + - - - - - + + + + + diff --git a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs index d1945f5e4..f7946ceeb 100644 --- a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs +++ b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs @@ -2,20 +2,19 @@ // 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; +#if WINAPPSDK + using CommunityToolkit.WinUI.Controls; +using Microsoft.UI; namespace BehaviorsExperiment.Samples; -/// -/// An example sample page of a custom control inheriting from Panel. -/// -[ToolkitSampleNumericOption("LoadingOffset", 100d, 500d, 50d, Title = "LoadingOffset")] +[ToolkitSampleNumericOption("LoadingOffset", 100d, 50d, 500d, 50d, Title = "LoadingOffset")] -[ToolkitSample(id: nameof(NeedMoreItemTriggerBehaviorSample), "Custom control", description: $"A sample for showing how to create and use a {nameof(Behaviors)} custom control.")] +[ToolkitSample(id: nameof(NeedMoreItemTriggerBehaviorSample), "NeedMoreItemTriggerBehavior", description: $"A sample for showing how to create and use a NeedMoreItemTriggerBehavior.")] public sealed partial class NeedMoreItemTriggerBehaviorSample : Page { - private static readonly Random _random = new Random(); + private static readonly Random _random = new(); public NeedMoreItemTriggerBehaviorSample() { @@ -36,46 +35,15 @@ public static SolidColorBrush GetColor() return new(color); } - public MyCollection ViewModels { get; } = [.. Enumerable.Repeat(0, 50).Select(_ => _random.Next(5, 10) * 500)]; -} - -public partial class MyCollection : ObservableCollection, ISupportIncrementalLoading -{ - private static readonly Random _random = new Random(); - - public int Num - { - get => _num; - set - { - if (_num == value) - { - return; - } - - _num = value; - OnPropertyChanged(new PropertyChangedEventArgs(nameof(Num))); - } - } + public IEnumerable ViewModels { get; } = Enumerable.Repeat(0, 50).Select(_ => _random.Next(5, 10) * 500); private int _num; - public bool HasMoreItems => true; - - public IAsyncOperation LoadMoreItemsAsync(uint count) - { - return LoadMoreItemsTaskAsync(count).AsAsyncOperation(); - } - - public async Task LoadMoreItemsTaskAsync(uint count) + public void IncrementCount() { - await Task.Delay(1000); - foreach (var i in Enumerable.Repeat(0, (int)count).Select(_ => _random.Next(5, 10) * 500)) - { - Add(i); - } - - Num++; - return new LoadMoreItemsResult { Count = count }; + _num++; + MyRun.Text = _num.ToString(); } } + +#endif diff --git a/components/Behaviors/src/Behaviors.cs b/components/Behaviors/src/Behaviors.cs deleted file mode 100644 index d18cb94f8..000000000 --- a/components/Behaviors/src/Behaviors.cs +++ /dev/null @@ -1,108 +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. - -namespace CommunityToolkit.WinUI.Controls; - -/// -/// This is an example control based off of the BoxPanel sample here: https://docs.microsoft.com/windows/apps/design/layout/boxpanel-example-custom-panel. If you need this similar sort of layout component for an application, see UniformGrid in the Toolkit. -/// It is provided as an example of how to inherit from another control like . -/// You can choose to start here or from the or example components. Remove unused components and rename as appropriate. -/// -public partial class Behaviors : Panel -{ - /// - /// Identifies the property. - /// - public static readonly DependencyProperty OrientationProperty = - DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(Behaviors), new PropertyMetadata(null, OnOrientationChanged)); - - /// - /// Gets the preference of the rows/columns when there are a non-square number of children. Defaults to Vertical. - /// - public Orientation Orientation - { - get { return (Orientation)GetValue(OrientationProperty); } - set { SetValue(OrientationProperty, value); } - } - - // Invalidate our layout when the property changes. - private static void OnOrientationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) - { - if (dependencyObject is Behaviors panel) - { - panel.InvalidateMeasure(); - } - } - - // Store calculations we want to use between the Measure and Arrange methods. - int _columnCount; - double _cellWidth, _cellHeight; - - protected override Size MeasureOverride(Size availableSize) - { - // Determine the square that can contain this number of items. - var maxrc = (int)Math.Ceiling(Math.Sqrt(Children.Count)); - // Get an aspect ratio from availableSize, decides whether to trim row or column. - var aspectratio = availableSize.Width / availableSize.Height; - if (Orientation == Orientation.Vertical) { aspectratio = 1 / aspectratio; } - - int rowcount; - - // Now trim this square down to a rect, many times an entire row or column can be omitted. - if (aspectratio > 1) - { - rowcount = maxrc; - _columnCount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc; - } - else - { - rowcount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc; - _columnCount = maxrc; - } - - // Now that we have a column count, divide available horizontal, that's our cell width. - _cellWidth = (int)Math.Floor(availableSize.Width / _columnCount); - // Next get a cell height, same logic of dividing available vertical by rowcount. - _cellHeight = Double.IsInfinity(availableSize.Height) ? Double.PositiveInfinity : availableSize.Height / rowcount; - - double maxcellheight = 0; - - foreach (UIElement child in Children) - { - child.Measure(new Size(_cellWidth, _cellHeight)); - maxcellheight = (child.DesiredSize.Height > maxcellheight) ? child.DesiredSize.Height : maxcellheight; - } - - return LimitUnboundedSize(availableSize, maxcellheight); - } - - // This method limits the panel height when no limit is imposed by the panel's parent. - // That can happen to height if the panel is close to the root of main app window. - // In this case, base the height of a cell on the max height from desired size - // and base the height of the panel on that number times the #rows. - Size LimitUnboundedSize(Size input, double maxcellheight) - { - if (Double.IsInfinity(input.Height)) - { - input.Height = maxcellheight * _columnCount; - _cellHeight = maxcellheight; - } - return input; - } - - protected override Size ArrangeOverride(Size finalSize) - { - int count = 1; - double x, y; - foreach (UIElement child in Children) - { - x = (count - 1) % _columnCount * _cellWidth; - y = ((int)(count - 1) / _columnCount) * _cellHeight; - Point anchorPoint = new Point(x, y); - child.Arrange(new Rect(anchorPoint, child.DesiredSize)); - count++; - } - return finalSize; - } -} diff --git a/components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml b/components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml deleted file mode 100644 index 8a6b6dd91..000000000 --- a/components/Behaviors/src/BehaviorsStyle_ClassicBinding.xaml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - 4,4,4,4 - - - - - - - - diff --git a/components/Behaviors/src/BehaviorsStyle_xBind.xaml b/components/Behaviors/src/BehaviorsStyle_xBind.xaml deleted file mode 100644 index 09e762736..000000000 --- a/components/Behaviors/src/BehaviorsStyle_xBind.xaml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - 4,4,4,4 - - - - - - - - diff --git a/components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs b/components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs deleted file mode 100644 index 69d6f866c..000000000 --- a/components/Behaviors/src/BehaviorsStyle_xBind.xaml.cs +++ /dev/null @@ -1,20 +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. - -namespace CommunityToolkit.WinUI.Controls; - -/// -/// Backing code for this resource dictionary. -/// -public sealed partial class BehaviorsStyle_xBind : ResourceDictionary -{ - // NOTICE - // This file only exists to enable x:Bind in the resource dictionary. - // Do not add code here. - // Instead, add code-behind to your templated control. - public BehaviorsStyle_xBind() - { - this.InitializeComponent(); - } -} diff --git a/components/Behaviors/src/Behaviors_ClassicBinding.cs b/components/Behaviors/src/Behaviors_ClassicBinding.cs deleted file mode 100644 index c23b76d26..000000000 --- a/components/Behaviors/src/Behaviors_ClassicBinding.cs +++ /dev/null @@ -1,94 +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. - -namespace CommunityToolkit.WinUI.Controls; - -/// -/// An example templated control. -/// -[TemplatePart(Name = nameof(PART_HelloWorld), Type = typeof(TextBlock))] -public partial class Behaviors_ClassicBinding : Control -{ - /// - /// Creates a new instance of the class. - /// - public Behaviors_ClassicBinding() - { - this.DefaultStyleKey = typeof(Behaviors_ClassicBinding); - } - - /// - /// The primary text block that displays "Hello world". - /// - protected TextBlock? PART_HelloWorld { get; private set; } - - /// - protected override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - // Detach all attached events when a new template is applied. - if (PART_HelloWorld is not null) - { - PART_HelloWorld.PointerEntered -= Element_PointerEntered; - } - - // Attach events when the template is applied and the control is loaded. - PART_HelloWorld = GetTemplateChild(nameof(PART_HelloWorld)) as TextBlock; - - if (PART_HelloWorld is not null) - { - PART_HelloWorld.PointerEntered += Element_PointerEntered; - } - } - - /// - /// The backing for the property. - /// - public static readonly DependencyProperty ItemPaddingProperty = DependencyProperty.Register( - nameof(ItemPadding), - typeof(Thickness), - typeof(Behaviors_ClassicBinding), - new PropertyMetadata(defaultValue: new Thickness(0))); - - /// - /// The backing for the property. - /// - public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register( - nameof(MyProperty), - typeof(string), - typeof(Behaviors_ClassicBinding), - new PropertyMetadata(defaultValue: string.Empty, (d, e) => ((Behaviors_ClassicBinding)d).OnMyPropertyChanged((string)e.OldValue, (string)e.NewValue))); - - /// - /// Gets or sets an example string. A basic DependencyProperty example. - /// - public string MyProperty - { - get => (string)GetValue(MyPropertyProperty); - set => SetValue(MyPropertyProperty, value); - } - - /// - /// Gets or sets a padding for an item. A basic DependencyProperty example. - /// - public Thickness ItemPadding - { - get => (Thickness)GetValue(ItemPaddingProperty); - set => SetValue(ItemPaddingProperty, value); - } - - protected virtual void OnMyPropertyChanged(string oldValue, string newValue) - { - // Do something with the changed value. - } - - public void Element_PointerEntered(object sender, PointerRoutedEventArgs e) - { - if (sender is TextBlock text) - { - text.Opacity = 1; - } - } -} diff --git a/components/Behaviors/src/Behaviors_xBind.cs b/components/Behaviors/src/Behaviors_xBind.cs deleted file mode 100644 index 46f213ae7..000000000 --- a/components/Behaviors/src/Behaviors_xBind.cs +++ /dev/null @@ -1,71 +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. - -namespace CommunityToolkit.WinUI.Controls; - -/// -/// An example templated control. -/// -public partial class Behaviors_xBind: Control -{ - /// - /// Creates a new instance of the class. - /// - public Behaviors_xBind() - { - this.DefaultStyleKey = typeof(Behaviors_xBind); - - // Allows directly using this control as the x:DataType in the template. - this.DataContext = this; - } - - /// - /// The backing for the property. - /// - public static readonly DependencyProperty ItemPaddingProperty = DependencyProperty.Register( - nameof(ItemPadding), - typeof(Thickness), - typeof(Behaviors_xBind), - new PropertyMetadata(defaultValue: new Thickness(0))); - - /// - /// The backing for the property. - /// - public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register( - nameof(MyProperty), - typeof(string), - typeof(Behaviors_xBind), - new PropertyMetadata(defaultValue: string.Empty, (d, e) => ((Behaviors_xBind)d).OnMyPropertyChanged((string)e.OldValue, (string)e.NewValue))); - - /// - /// Gets or sets an example string. A basic DependencyProperty example. - /// - public string MyProperty - { - get => (string)GetValue(MyPropertyProperty); - set => SetValue(MyPropertyProperty, value); - } - - /// - /// Gets or sets a padding for an item. A basic DependencyProperty example. - /// - public Thickness ItemPadding - { - get => (Thickness)GetValue(ItemPaddingProperty); - set => SetValue(ItemPaddingProperty, value); - } - - protected virtual void OnMyPropertyChanged(string oldValue, string newValue) - { - // Do something with the changed value. - } - - public void Element_PointerEntered(object sender, PointerRoutedEventArgs e) - { - if (sender is TextBlock text) - { - text.Opacity = 1; - } - } -} diff --git a/components/Behaviors/src/LoadMoreItemBehavior.cs b/components/Behaviors/src/LoadMoreItemBehavior.cs index fdc814e40..617edd121 100644 --- a/components/Behaviors/src/LoadMoreItemBehavior.cs +++ b/components/Behaviors/src/LoadMoreItemBehavior.cs @@ -186,7 +186,7 @@ private void ScrollViewOnPropertyChanged(DependencyObject sender, DependencyProp /// public async Task TryRaiseLoadMoreRequestedAsync() { - if (this.ScrollView is null) + if (this.AssociatedObject is null || this.ScrollView is null) return; var loadMore = true; diff --git a/components/Behaviors/src/Themes/Generic.xaml b/components/Behaviors/src/Themes/Generic.xaml deleted file mode 100644 index 56609e75c..000000000 --- a/components/Behaviors/src/Themes/Generic.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - From 3d6b334f8a7dc246fcd42054db7efeb73149e0f1 Mon Sep 17 00:00:00 2001 From: Poker Date: Sun, 17 Nov 2024 15:25:23 +0800 Subject: [PATCH 3/7] update samples --- .../Behaviors/samples/ItemsView Behaviors.md | 53 ++++--------------- .../samples/LoadMoreItemBehaviorSample.xaml | 5 +- .../LoadMoreItemBehaviorSample.xaml.cs | 4 +- .../NeedMoreItemTriggerBehaviorSample.xaml | 2 +- .../NeedMoreItemTriggerBehaviorSample.xaml.cs | 3 +- .../Behaviors/src/LoadMoreItemBehavior.cs | 10 ++-- 6 files changed, 25 insertions(+), 52 deletions(-) diff --git a/components/Behaviors/samples/ItemsView Behaviors.md b/components/Behaviors/samples/ItemsView Behaviors.md index 418032adb..0ccc7054b 100644 --- a/components/Behaviors/samples/ItemsView Behaviors.md +++ b/components/Behaviors/samples/ItemsView Behaviors.md @@ -11,54 +11,21 @@ discussion-id: 532 issue-id: 0 icon: assets/icon.png --- +The new control `ItemsView` which can replace `ListView`&`GridView`, does not support `ISupportIncrementalLoading`. - - - - - +Here are some Behaviors to help you make `ItemsView` support `ISupportIncrementalLoading`. - +## NeedMoreItemTriggerBehaviorSample -# ItemsView Behaviors - -TODO: Fill in information about this experiment and how to get started here... - -## Custom Control - -You can inherit from an existing component as well, like `Panel`, this example shows a control without a -XAML Style that will be more light-weight to consume by an app developer: - -> - -## Templated Controls - -The Toolkit is built with templated controls. This provides developers a flexible way to restyle components -easily while still inheriting the general functionality a control provides. The examples below show -how a component can use a default style and then get overridden by the end developer. - -TODO: Two types of templated control building methods are shown. Delete these if you're building a custom component. -Otherwise, pick one method for your component and delete the files related to the unchosen `_ClassicBinding` or `_xBind` -classes (and the custom non-suffixed one as well). Then, rename your component to just be your component name. - -The `_ClassicBinding` class shows the traditional method used to develop components with best practices. - -### Implict style +This trigger behavior can excute actions when the `ItemsView` scrolling to bottom. +You can customize the loading behavior through actions, but note that you may need to debounce manually. > [!SAMPLE NeedMoreItemTriggerBehaviorSample] -> [!SAMPLE LoadMoreItemBehaviorSample] - -### Custom style - - -## Templated Controls with x:Bind - -This is an _experimental_ new way to define components which allows for the use of x:Bind within the style. - -### Implict style - - -### Custom style +## LoadMoreItemBehaviorSample +If you don't have complex loading requirements, you can use this behavior. +It automatically calls `ISupportIncrementalLoading.LoadMoreItemsAsync` when the `ItemsSource` changes, in addition to when `ItemsView` scrolls to the bottom. +Besides, this behavior has a built-in debounce function. +> [!SAMPLE LoadMoreItemBehaviorSample] diff --git a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml index 34fbceb85..dfc55e6e8 100644 --- a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml +++ b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml @@ -26,7 +26,10 @@ MinRowSpacing="5" /> - + diff --git a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs index 00bffa88c..e322b0373 100644 --- a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs +++ b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml.cs @@ -9,7 +9,9 @@ namespace BehaviorsExperiment.Samples; -[ToolkitSampleNumericOption("LoadingOffset", 100d, 50d, 500d, 50d, Title = "LoadingOffset")] +[ToolkitSampleBoolOption(nameof(LoadMoreItemBehavior.IsActive), true, Title = nameof(LoadMoreItemBehavior.IsActive))] +[ToolkitSampleNumericOption(nameof(LoadMoreItemBehavior.LoadingOffset), 100d, 50d, 500d, 50d, Title = nameof(LoadMoreItemBehavior.LoadingOffset))] +[ToolkitSampleNumericOption(nameof(LoadMoreItemBehavior.LoadCount), 20, 1, 50, 1, Title = nameof(LoadMoreItemBehavior.LoadCount))] [ToolkitSample(id: nameof(LoadMoreItemBehaviorSample), "LoadMoreItemBehavior", description: $"A sample for showing how to create and use a LoadMoreItemBehavior.")] public sealed partial class LoadMoreItemBehaviorSample : Page diff --git a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml index be55182e7..b3c5c402b 100644 --- a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml +++ b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml @@ -27,7 +27,7 @@ MinRowSpacing="5" /> - + diff --git a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs index f7946ceeb..090597d43 100644 --- a/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs +++ b/components/Behaviors/samples/NeedMoreItemTriggerBehaviorSample.xaml.cs @@ -9,7 +9,8 @@ namespace BehaviorsExperiment.Samples; -[ToolkitSampleNumericOption("LoadingOffset", 100d, 50d, 500d, 50d, Title = "LoadingOffset")] +[ToolkitSampleBoolOption(nameof(NeedMoreItemTriggerBehavior.IsActive), true, Title = nameof(NeedMoreItemTriggerBehavior.IsActive))] +[ToolkitSampleNumericOption(nameof(NeedMoreItemTriggerBehavior.LoadingOffset), 100d, 50d, 500d, 50d, Title = nameof(NeedMoreItemTriggerBehavior.LoadingOffset))] [ToolkitSample(id: nameof(NeedMoreItemTriggerBehaviorSample), "NeedMoreItemTriggerBehavior", description: $"A sample for showing how to create and use a NeedMoreItemTriggerBehavior.")] public sealed partial class NeedMoreItemTriggerBehaviorSample : Page diff --git a/components/Behaviors/src/LoadMoreItemBehavior.cs b/components/Behaviors/src/LoadMoreItemBehavior.cs index 617edd121..7d3ee7c89 100644 --- a/components/Behaviors/src/LoadMoreItemBehavior.cs +++ b/components/Behaviors/src/LoadMoreItemBehavior.cs @@ -96,9 +96,9 @@ public int LoadCount /// public event Func>? LoadMoreRequested; - private MUXC.ItemsRepeater? ItemsRepeater => (MUXC.ItemsRepeater?)this.ScrollView?.Content; + private ItemsRepeater? ItemsRepeater => (ItemsRepeater?)this.ScrollView?.Content; - private ScrollView? ScrollView => this.AssociatedObject.ScrollView; + private ScrollView? ScrollView => this.AssociatedObject?.ScrollView; private long _scrollViewOnPropertyChangedToken; private long _itemsSourceOnPropertyChangedToken; @@ -166,7 +166,7 @@ private async void ItemsSourceOnPropertyChanged(DependencyObject sender, Depende /// /// /// is the key to continuous loading. - /// When new data is loaded, it makes the of the larger, + /// When new data is loaded, it makes the of the larger, /// Or when the is changed to a different source or set for the first time, becomes 0. /// This method can reload the data. /// @@ -193,7 +193,7 @@ public async Task TryRaiseLoadMoreRequestedAsync() // Load until a new item is loaded in while (loadMore) { - if (!this.IsActive || this.IsLoadingMore) + if (this.AssociatedObject is null || !this.IsActive || this.IsLoadingMore) return; // LoadMoreRequested is only triggered when the view is not filled. @@ -231,7 +231,7 @@ public async Task TryRaiseLoadMoreRequestedAsync() private int GetItemsCount() { - return this.AssociatedObject.ItemsSource switch + return this.AssociatedObject?.ItemsSource switch { ICollection list => list.Count, IEnumerable enumerable => enumerable.Cast().Count(), From d93661e85c88a9e8419c8f77d32e2f1e397a2016 Mon Sep 17 00:00:00 2001 From: Poker Date: Sun, 17 Nov 2024 15:30:54 +0800 Subject: [PATCH 4/7] remove shproj --- .../Behaviors/tests/Behaviors.Tests.projitems | 12 -- .../tests/ExampleBehaviorsTestClass.cs | 134 ------------------ .../tests/ExampleBehaviorsTestPage.xaml | 14 -- .../tests/ExampleBehaviorsTestPage.xaml.cs | 16 --- 4 files changed, 176 deletions(-) delete mode 100644 components/Behaviors/tests/ExampleBehaviorsTestClass.cs delete mode 100644 components/Behaviors/tests/ExampleBehaviorsTestPage.xaml delete mode 100644 components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs diff --git a/components/Behaviors/tests/Behaviors.Tests.projitems b/components/Behaviors/tests/Behaviors.Tests.projitems index aa9bebb6d..67c5e0c76 100644 --- a/components/Behaviors/tests/Behaviors.Tests.projitems +++ b/components/Behaviors/tests/Behaviors.Tests.projitems @@ -8,16 +8,4 @@ BehaviorsExperiment.Tests - - - - ExampleBehaviorsTestPage.xaml - - - - - Designer - MSBuild:Compile - - \ No newline at end of file diff --git a/components/Behaviors/tests/ExampleBehaviorsTestClass.cs b/components/Behaviors/tests/ExampleBehaviorsTestClass.cs deleted file mode 100644 index eab9cdb74..000000000 --- a/components/Behaviors/tests/ExampleBehaviorsTestClass.cs +++ /dev/null @@ -1,134 +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 CommunityToolkit.Tooling.TestGen; -using CommunityToolkit.Tests; -using CommunityToolkit.WinUI.Controls; - -namespace BehaviorsExperiment.Tests; - -[TestClass] -public partial class ExampleBehaviorsTestClass : VisualUITestBase -{ - // If you don't need access to UI objects directly or async code, use this pattern. - [TestMethod] - public void SimpleSynchronousExampleTest() - { - var assembly = typeof(Behaviors).Assembly; - var type = assembly.GetType(typeof(Behaviors).FullName ?? string.Empty); - - Assert.IsNotNull(type, "Could not find Behaviors type."); - Assert.AreEqual(typeof(Behaviors), type, "Type of Behaviors does not match expected type."); - } - - // If you don't need access to UI objects directly, use this pattern. - [TestMethod] - public async Task SimpleAsyncExampleTest() - { - await Task.Delay(250); - - Assert.IsTrue(true); - } - - // Example that shows how to check for exception throwing. - [TestMethod] - public void SimpleExceptionCheckTest() - { - // If you need to check exceptions occur for invalid inputs, etc... - // Use Assert.ThrowsException to limit the scope to where you expect the error to occur. - // Otherwise, using the ExpectedException attribute could swallow or - // catch other issues in setup code. - Assert.ThrowsException(() => throw new NotImplementedException()); - } - - // The UIThreadTestMethod automatically dispatches to the UI for us to work with UI objects. - [UIThreadTestMethod] - public void SimpleUIAttributeExampleTest() - { - var component = new Behaviors(); - Assert.IsNotNull(component); - } - - // The UIThreadTestMethod can also easily grab a XAML Page for us by passing its type as a parameter. - // This lets us actually test a control as it would behave within an actual application. - // The page will already be loaded by the time your test is called. - [UIThreadTestMethod] - public void SimpleUIExamplePageTest(ExampleBehaviorsTestPage page) - { - // You can use the Toolkit Visual Tree helpers here to find the component by type or name: - var component = page.FindDescendant(); - - Assert.IsNotNull(component); - - var componentByName = page.FindDescendant("BehaviorsControl"); - - Assert.IsNotNull(componentByName); - } - - // You can still do async work with a UIThreadTestMethod as well. - [UIThreadTestMethod] - public async Task SimpleAsyncUIExamplePageTest(ExampleBehaviorsTestPage page) - { - // This helper can be used to wait for a rendering pass to complete. - // Note, this is already done by loading a Page with the [UIThreadTestMethod] helper. - await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { }); - - var component = page.FindDescendant(); - - Assert.IsNotNull(component); - } - - //// ----------------------------- ADVANCED TEST SCENARIOS ----------------------------- - - // If you need to use DataRow, you can use this pattern with the UI dispatch still. - // Otherwise, checkout the UIThreadTestMethod attribute above. - // See https://github.com/CommunityToolkit/Labs-Windows/issues/186 - [TestMethod] - public async Task ComplexAsyncUIExampleTest() - { - await EnqueueAsync(() => - { - var component = new Behaviors_ClassicBinding(); - Assert.IsNotNull(component); - }); - } - - // If you want to load other content not within a XAML page using the UIThreadTestMethod above. - // Then you can do that using the Load/UnloadTestContentAsync methods. - [TestMethod] - public async Task ComplexAsyncLoadUIExampleTest() - { - await EnqueueAsync(async () => - { - var component = new Behaviors_ClassicBinding(); - Assert.IsNotNull(component); - Assert.IsFalse(component.IsLoaded); - - await LoadTestContentAsync(component); - - Assert.IsTrue(component.IsLoaded); - - await UnloadTestContentAsync(component); - - Assert.IsFalse(component.IsLoaded); - }); - } - - // You can still use the UIThreadTestMethod to remove the extra layer for the dispatcher as well: - [UIThreadTestMethod] - public async Task ComplexAsyncLoadUIExampleWithoutDispatcherTest() - { - var component = new Behaviors_ClassicBinding(); - Assert.IsNotNull(component); - Assert.IsFalse(component.IsLoaded); - - await LoadTestContentAsync(component); - - Assert.IsTrue(component.IsLoaded); - - await UnloadTestContentAsync(component); - - Assert.IsFalse(component.IsLoaded); - } -} diff --git a/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml b/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml deleted file mode 100644 index b968eb346..000000000 --- a/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs b/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs deleted file mode 100644 index b9bface8f..000000000 --- a/components/Behaviors/tests/ExampleBehaviorsTestPage.xaml.cs +++ /dev/null @@ -1,16 +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. - -namespace BehaviorsExperiment.Tests; - -/// -/// An empty page that can be used on its own or navigated to within a Frame. -/// -public sealed partial class ExampleBehaviorsTestPage : Page -{ - public ExampleBehaviorsTestPage() - { - this.InitializeComponent(); - } -} From e35d445e20d6388212bbc1eeca2da634d4e16317 Mon Sep 17 00:00:00 2001 From: Poker Date: Sun, 17 Nov 2024 15:33:52 +0800 Subject: [PATCH 5/7] revert sdk version --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index d413698aa..91187a7c4 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "8.0.403", "rollForward": "latestFeature" }, "msbuild-sdks": From 09a46bba57f1d63bf8d1cd32ec3675b42bb6e22d Mon Sep 17 00:00:00 2001 From: Poker Date: Thu, 28 Nov 2024 09:05:26 +0800 Subject: [PATCH 6/7] apply xaml styler --- .../samples/LoadMoreItemBehaviorSample.xaml | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml index dfc55e6e8..c10cccc11 100644 --- a/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml +++ b/components/Behaviors/samples/LoadMoreItemBehaviorSample.xaml @@ -1,45 +1,46 @@ - - + + - + - + - + - + - + - + From a8059fa8230e22ce5581c7498d1b1f56a67e05f5 Mon Sep 17 00:00:00 2001 From: Poker Date: Thu, 28 Nov 2024 16:28:46 +0800 Subject: [PATCH 7/7] revert submodules --- tooling | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling b/tooling index 819a01607..d71b08b2d 160000 --- a/tooling +++ b/tooling @@ -1 +1 @@ -Subproject commit 819a01607310fad9bf52097423977ace4b5c372a +Subproject commit d71b08b2dccf94c3ceaeda99526679bc0cfc3b8a