From d0b3df8774cbcf2eadfb0f028a1bc404c0663f7b Mon Sep 17 00:00:00 2001 From: Pedro Jesus Date: Sat, 15 May 2021 22:42:27 -0300 Subject: [PATCH 01/20] merge main -> dev (#1300) * Automated dotnet-format update (#1296) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * https://github.com/xamarin/XamarinCommunityToolkit/issues/1292 (#1301) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Andrei --- .../Converters/BaseConverter.shared.cs | 2 +- .../Converters/BaseConverterOneWay.shared.cs | 2 +- .../Effects/Touch/TouchEffect.shared.cs | 4 ++-- .../Views/BaseTemplatedView.shared.cs | 13 +++++-------- .../Views/SideMenuView/SideMenuView.shared.cs | 8 ++++---- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverter.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverter.shared.cs index a767a787f..a9925a8ec 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverter.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverter.shared.cs @@ -36,4 +36,4 @@ public abstract class BaseConverter : BaseConverterOneWayAn object of type . public abstract TFrom ConvertBackTo(TTo value); } -} +} \ No newline at end of file diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverterOneWay.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverterOneWay.shared.cs index 97bceae80..04320728b 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverterOneWay.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/BaseConverterOneWay.shared.cs @@ -43,4 +43,4 @@ public abstract class BaseConverterOneWay : ValueConverterExtension, public virtual object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException("Impossible to revert to original value. Consider setting BindingMode to OneWay."); } -} +} \ No newline at end of file diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Effects/Touch/TouchEffect.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Effects/Touch/TouchEffect.shared.cs index 07bbb2cc5..8274d040b 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Effects/Touch/TouchEffect.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Effects/Touch/TouchEffect.shared.cs @@ -470,9 +470,9 @@ public event EventHandler LongPressCompleted default(bool), propertyChanged: TryGenerateEffect); - readonly GestureManager gestureManager = new (); + readonly GestureManager gestureManager = new(); - readonly WeakEventManager weakEventManager = new (); + readonly WeakEventManager weakEventManager = new(); VisualElement? element; diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/BaseTemplatedView.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/BaseTemplatedView.shared.cs index cce546d57..69b81dfa5 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/BaseTemplatedView.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/BaseTemplatedView.shared.cs @@ -1,5 +1,4 @@ -using System; -using Xamarin.Forms; +using Xamarin.Forms; namespace Xamarin.CommunityToolkit.UI.Views.Internals { @@ -9,9 +8,7 @@ namespace Xamarin.CommunityToolkit.UI.Views.Internals /// The type of the control that this template will be used for public abstract class BaseTemplatedView : TemplatedView where TControl : View, new() { - TControl? control; - - protected TControl Control => control ?? throw new NullReferenceException(); + protected TControl? Control { get; private set; } /// /// Constructor of @@ -23,15 +20,15 @@ protected override void OnBindingContextChanged() { base.OnBindingContextChanged(); - if (control != null) + if (Control != null) Control.BindingContext = BindingContext; } protected override void OnChildAdded(Element child) { - if (control == null && child is TControl content) + if (Control == null && child is TControl control) { - control = content; + Control = control; OnControlInitialized(Control); } diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/SideMenuView/SideMenuView.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/SideMenuView/SideMenuView.shared.cs index e6200a308..845b758f4 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/SideMenuView/SideMenuView.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/SideMenuView/SideMenuView.shared.cs @@ -637,7 +637,7 @@ void RemoveChild(View view) void RaiseMenuIfNeeded(View? menuView) { if (menuView != null && GetMenuAppearanceType(menuView) == SideMenuAppearanceType.SlideIn) - Control.RaiseChild(menuView); + Control?.RaiseChild(menuView); } void OnLayoutChanged(object? sender, EventArgs e) @@ -645,10 +645,10 @@ void OnLayoutChanged(object? sender, EventArgs e) if (mainView == null) return; - using (Control.Batch()) + using (Control?.Batch()) { - Control.RaiseChild(mainView); - Control.RaiseChild(overlayView); + Control?.RaiseChild(mainView); + Control?.RaiseChild(overlayView); RaiseMenuIfNeeded(leftMenu); RaiseMenuIfNeeded(rightMenu); From 4711e715df0ca4f559f3ec6e51c3949ac3a8308d Mon Sep 17 00:00:00 2001 From: Andrei Date: Wed, 2 Jun 2021 14:10:11 +0200 Subject: [PATCH 02/20] Expander: Add touch capture view + common animation length / easing properties (#1349) * Added base animation props * Added TouchCaptureView property --- .../XCT.Sample/Pages/Views/ExpanderPage.xaml | 11 +++- .../Views/Expander/Expander.shared.cs | 59 ++++++++++++++++--- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/samples/XCT.Sample/Pages/Views/ExpanderPage.xaml b/samples/XCT.Sample/Pages/Views/ExpanderPage.xaml index e43a31342..7543ec79a 100644 --- a/samples/XCT.Sample/Pages/Views/ExpanderPage.xaml +++ b/samples/XCT.Sample/Pages/Views/ExpanderPage.xaml @@ -27,9 +27,16 @@ - + - diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Expander/Expander.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Expander/Expander.shared.cs index 4bed59dc9..59d174c95 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Expander/Expander.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Expander/Expander.shared.cs @@ -51,11 +51,20 @@ public static readonly BindableProperty IsExpandedProperty public static readonly BindableProperty DirectionProperty = BindableProperty.Create(nameof(Direction), typeof(ExpandDirection), typeof(Expander), default(ExpandDirection), propertyChanged: OnDirectionPropertyChanged); + public static readonly BindableProperty TouchCaptureViewProperty + = BindableProperty.Create(nameof(TouchCaptureView), typeof(View), typeof(Expander), propertyChanged: OnTouchCaptureViewPropertyChanged); + + public static readonly BindableProperty AnimationLengthProperty + = BindableProperty.Create(nameof(AnimationLength), typeof(uint), typeof(Expander), defaultAnimationLength); + public static readonly BindableProperty ExpandAnimationLengthProperty - = BindableProperty.Create(nameof(ExpandAnimationLength), typeof(uint), typeof(Expander), defaultAnimationLength); + = BindableProperty.Create(nameof(ExpandAnimationLength), typeof(uint), typeof(Expander), uint.MaxValue); public static readonly BindableProperty CollapseAnimationLengthProperty - = BindableProperty.Create(nameof(CollapseAnimationLength), typeof(uint), typeof(Expander), defaultAnimationLength); + = BindableProperty.Create(nameof(CollapseAnimationLength), typeof(uint), typeof(Expander), uint.MaxValue); + + public static readonly BindableProperty AnimationEasingProperty + = BindableProperty.Create(nameof(AnimationEasing), typeof(Easing), typeof(Expander)); public static readonly BindableProperty ExpandAnimationEasingProperty = BindableProperty.Create(nameof(ExpandAnimationEasing), typeof(Easing), typeof(Expander)); @@ -146,6 +155,18 @@ public ExpandDirection Direction set => SetValue(DirectionProperty, value); } + public View? TouchCaptureView + { + get => (View?)GetValue(TouchCaptureViewProperty); + set => SetValue(TouchCaptureViewProperty, value); + } + + public uint AnimationLength + { + get => (uint)GetValue(AnimationLengthProperty); + set => SetValue(AnimationLengthProperty, value); + } + public uint ExpandAnimationLength { get => (uint)GetValue(ExpandAnimationLengthProperty); @@ -158,6 +179,12 @@ public uint CollapseAnimationLength set => SetValue(CollapseAnimationLengthProperty, value); } + public Easing AnimationEasing + { + get => (Easing)GetValue(AnimationEasingProperty); + set => SetValue(AnimationEasingProperty, value); + } + public Easing ExpandAnimationEasing { get => (Easing)GetValue(ExpandAnimationEasingProperty); @@ -256,6 +283,9 @@ static void OnIsExpandedPropertyChanged(BindableObject bindable, object oldValue static void OnDirectionPropertyChanged(BindableObject bindable, object oldValue, object newValue) => ((Expander)bindable).OnDirectionPropertyChanged((ExpandDirection)oldValue); + static void OnTouchCaptureViewPropertyChanged(BindableObject bindable, object oldValue, object newValue) + => ((Expander)bindable).OnTouchCaptureViewPropertyChanged((View?)oldValue); + static object GetDefaultForceUpdateSizeCommand(BindableObject bindable) => new Command(((Expander)bindable).ForceUpdateSize); @@ -271,8 +301,11 @@ void OnContentTemplatePropertyChanged() void OnIsExpandedPropertyChanged() => SetContent(false); - void OnDirectionPropertyChanged(ExpandDirection olddirection) - => SetDirection(olddirection); + void OnDirectionPropertyChanged(ExpandDirection oldDirection) + => SetDirection(oldDirection); + + void OnTouchCaptureViewPropertyChanged(View? oldView) + => SetTouchCaptureView(oldView); void OnIsExpandedChanged(bool shouldIgnoreAnimation = false) { @@ -319,7 +352,6 @@ void SetHeader(View? oldHeader) { if (oldHeader != null) { - oldHeader.GestureRecognizers.Remove(headerTapGestureRecognizer); Control?.Children.Remove(oldHeader); } @@ -329,9 +361,9 @@ void SetHeader(View? oldHeader) Control?.Children.Insert(0, Header); else Control?.Children.Add(Header); - - Header.GestureRecognizers.Add(headerTapGestureRecognizer); } + + SetTouchCaptureView(oldHeader); } void SetContent(bool isForceUpdate, bool shouldIgnoreAnimation = false, bool isForceContentReset = false) @@ -417,6 +449,14 @@ void SetDirection(ExpandDirection oldDirection) SetContent(true, true, true); } + void SetTouchCaptureView(View? oldView) + { + oldView?.GestureRecognizers.Remove(headerTapGestureRecognizer); + TouchCaptureView?.GestureRecognizers?.Remove(headerTapGestureRecognizer); + Header?.GestureRecognizers.Remove(headerTapGestureRecognizer); + (TouchCaptureView ?? Header)?.GestureRecognizers.Add(headerTapGestureRecognizer); + } + void InvokeAnimation(double startSize, double endSize, bool shouldIgnoreAnimation) { State = IsExpanded @@ -443,6 +483,11 @@ void InvokeAnimation(double startSize, double endSize, bool shouldIgnoreAnimatio easing = ExpandAnimationEasing; } + if (length == uint.MaxValue) + length = AnimationLength; + + easing ??= AnimationEasing; + if (lastVisibleSize > 0) length = (uint)(length * (Abs(endSize - startSize) / lastVisibleSize)); From e9805fd4800c8a5c5528176b09fe93cd03fd6e69 Mon Sep 17 00:00:00 2001 From: Andrew Hoefling Date: Sat, 5 Jun 2021 05:34:21 -0400 Subject: [PATCH 03/20] Updated light dismiss xml docs (#1366) --- .../Xamarin.CommunityToolkit/Views/Popup/BasePopup.shared.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Popup/BasePopup.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Popup/BasePopup.shared.cs index 2e7047c0f..f14bd0d3f 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Popup/BasePopup.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/Popup/BasePopup.shared.cs @@ -110,6 +110,7 @@ public Size Size /// /// /// When true and the user taps outside of the popup it will dismiss. + /// On Android - when false the hardware back button is disabled. /// public bool IsLightDismissEnabled { get; set; } From 6dfc3d2f6dcae15ac4887c9d07ee4e2f043a48c5 Mon Sep 17 00:00:00 2001 From: Gustavo Oliveira Date: Sun, 20 Jun 2021 19:19:45 +1000 Subject: [PATCH 04/20] [Converter] Added IsInRangeConverter (#1158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added IsBetweenConverter * Renamed to IsInRangeConverter * Added Sample Page * Added IsInRangeConverterViewModel * Updated UnitTests * Cleanup * Removed RevertResult * Implemented BindableObjectExtension * Made ValueConverterExtension inherited from BindableObject Co-authored-by: Brandon Minnick <13558917+brminnick@users.noreply.github.com> Co-authored-by: Javier Suárez Co-authored-by: Pedro Jesus Co-authored-by: Andrei Co-authored-by: Gerald Versluis --- .../Converters/IsInRangeConverterPage.xaml | 70 ++++++++++++++++++ .../Converters/IsInRangeConverterPage.xaml.cs | 7 ++ .../Converters/ConvertersGalleryViewModel.cs | 4 + .../Converters/IsInRangeConverterViewModel.cs | 36 +++++++++ .../Converters/IsInRangeConverter_Tests.cs | 53 +++++++++++++ .../Converters/IsInRangeConverter.shared.cs | 74 +++++++++++++++++++ .../ValueConverterExtension.shared.cs | 2 +- 7 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml create mode 100644 samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml.cs create mode 100644 samples/XCT.Sample/ViewModels/Converters/IsInRangeConverterViewModel.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit.UnitTests/Converters/IsInRangeConverter_Tests.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/IsInRangeConverter.shared.cs diff --git a/samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml b/samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml new file mode 100644 index 000000000..bf0c2f05b --- /dev/null +++ b/samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml.cs b/samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml.cs new file mode 100644 index 000000000..cb7f47840 --- /dev/null +++ b/samples/XCT.Sample/Pages/Converters/IsInRangeConverterPage.xaml.cs @@ -0,0 +1,7 @@ +namespace Xamarin.CommunityToolkit.Sample.Pages.Converters +{ + public partial class IsInRangeConverterPage + { + public IsInRangeConverterPage() => InitializeComponent(); + } +} diff --git a/samples/XCT.Sample/ViewModels/Converters/ConvertersGalleryViewModel.cs b/samples/XCT.Sample/ViewModels/Converters/ConvertersGalleryViewModel.cs index a60cf6b1f..b18452a3d 100644 --- a/samples/XCT.Sample/ViewModels/Converters/ConvertersGalleryViewModel.cs +++ b/samples/XCT.Sample/ViewModels/Converters/ConvertersGalleryViewModel.cs @@ -95,6 +95,10 @@ protected override IEnumerable CreateItems() => new[] typeof(ImageResourceConverterPage), nameof(ImageResourceConverter), "A converter that allows you to convert embeded ressource image id to an ImageSource"), + new SectionModel( + typeof(IsInRangeConverterPage), + nameof(IsInRangeConverter), + "A converter that allows you to check if a value is in a range"), new SectionModel( typeof(MathExpressionConverterPage), nameof(MathExpressionConverter), diff --git a/samples/XCT.Sample/ViewModels/Converters/IsInRangeConverterViewModel.cs b/samples/XCT.Sample/ViewModels/Converters/IsInRangeConverterViewModel.cs new file mode 100644 index 000000000..70517119b --- /dev/null +++ b/samples/XCT.Sample/ViewModels/Converters/IsInRangeConverterViewModel.cs @@ -0,0 +1,36 @@ +using System; + +namespace Xamarin.CommunityToolkit.Sample.ViewModels.Converters +{ + public class IsInRangeConverterViewModel : BaseViewModel + { + DateTime date = DateTime.Now.Date; + DateTime startDate = DateTime.Now.Date.AddDays(-7); + DateTime endDate = DateTime.Now.Date.AddDays(7); + + public DateTime StartDate + { + get => startDate; + set => SetProperty(ref startDate, value, onChanged: RaisePropertiesChanged); + } + + public DateTime EndDate + { + get => endDate; + set => SetProperty(ref endDate, value, onChanged: RaisePropertiesChanged); + } + + public DateTime Date + { + get => date; + set => SetProperty(ref date, value); + } + + void RaisePropertiesChanged() + { + OnPropertyChanged(nameof(StartDate)); + OnPropertyChanged(nameof(EndDate)); + OnPropertyChanged(nameof(Date)); + } + } +} \ No newline at end of file diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit.UnitTests/Converters/IsInRangeConverter_Tests.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit.UnitTests/Converters/IsInRangeConverter_Tests.cs new file mode 100644 index 000000000..3594ee96e --- /dev/null +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit.UnitTests/Converters/IsInRangeConverter_Tests.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using NUnit.Framework; +using Xamarin.CommunityToolkit.Converters; + +namespace Xamarin.CommunityToolkit.UnitTests.Converters +{ + public class IsInRangeConverter_Tests + { + public static IEnumerable GetData() => new List + { + new object[] { 2, 1, 3, true }, + new object[] { 1, 2, 3, false }, + new object[] { new DateTime(2000, 1, 15), new DateTime(2000, 1, 1), new DateTime(2000, 2, 1), true }, + new object[] { new DateTime(2000, 1, 1), new DateTime(2000, 1, 15), new DateTime(2000, 2, 1), false }, + new object[] { "b", "a", "d", true }, + new object[] { "a", "b", "d", false }, + }; + + public static IEnumerable GetDataForException() => new List + { + new object?[] { null, 2, 3 }, + new object?[] { 1, null, 3 }, + new object?[] { 1, 2, null }, + }; + + [TestCaseSource(nameof(GetData))] + public void IsInRangeConverter(object value, object minValue, object maxValue, bool expectedResult) + { + var isInRangeConverter = new IsInRangeConverter + { + MinValue = minValue, + MaxValue = maxValue + }; + + var result = isInRangeConverter.Convert(value, typeof(IsInRangeConverter_Tests), null, CultureInfo.CurrentCulture); + + Assert.AreEqual(result, expectedResult); + } + + [TestCaseSource(nameof(GetDataForException))] + public void IsInRangeConverterInvalidValuesThrowArgumenException(object value, object minValue, object maxValue) + { + var isInRangeConverter = new IsInRangeConverter + { + MinValue = minValue, + MaxValue = maxValue + }; + Assert.Throws(() => isInRangeConverter.Convert(value, typeof(IsInRangeConverter_Tests), null, CultureInfo.CurrentCulture)); + } + } +} diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/IsInRangeConverter.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/IsInRangeConverter.shared.cs new file mode 100644 index 000000000..082a3fdf2 --- /dev/null +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Converters/IsInRangeConverter.shared.cs @@ -0,0 +1,74 @@ +using System; +using System.Globalization; +using Xamarin.CommunityToolkit.Extensions.Internals; +using Xamarin.Forms; + +namespace Xamarin.CommunityToolkit.Converters +{ + /// + /// Checks if the value is between minValue and maxValue, returning true if the value is within the range and false if the value is out of the range. + /// + public class IsInRangeConverter : ValueConverterExtension, IValueConverter + { + /// + /// Backing BindableProperty for the property. + /// + public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(object), typeof(IsInRangeConverter)); + + /// + /// Gets or sets the minimum value of the range for the . This is a bindable property. + /// + public object MinValue + { + get => GetValue(MinValueProperty); + set => SetValue(MinValueProperty, value); + } + + /// + /// Backing BindableProperty for the property. + /// + public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(object), typeof(IsInRangeConverter)); + + /// + /// Gets or sets the maximum value of the range for the . This is a bindable property. + /// + public object MaxValue + { + get => GetValue(MaxValueProperty); + set => SetValue(MaxValueProperty, value); + } + + /// + /// Checks if the value is between minValue and maxValue, returning true if the value is within the range and false if the value is out of the range. + /// + /// The object to compare. + /// The type of the binding target property. This is not implemented. + /// Additional parameter for the converter to handle. This is not implemented. + /// The culture to use in the converter. This is not implemented. + /// True if and are equal, False if they are not equal. + public object Convert(object value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is not IComparable comparable) + throw new ArgumentException("is expected to implement IComparable interface.", nameof(value)); + + if (MinValue is not IComparable) + throw new ArgumentException("is expected to implement IComparable interface.", nameof(MinValue)); + + if (MaxValue is not IComparable) + throw new ArgumentException("is expected to implement IComparable interface.", nameof(MaxValue)); + + return comparable.CompareTo(MinValue) >= 0 && comparable.CompareTo(MaxValue) <= 0; + } + + /// + /// This method is not implemented and will throw a . + /// + /// N/A + /// N/A + /// N/A + /// N/A + /// N/A + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => + throw new NotImplementedException(); + } +} diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Extensions/ValueConverterExtension.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Extensions/ValueConverterExtension.shared.cs index 2603b2743..50e782919 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Extensions/ValueConverterExtension.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Extensions/ValueConverterExtension.shared.cs @@ -4,7 +4,7 @@ namespace Xamarin.CommunityToolkit.Extensions.Internals { - public abstract class ValueConverterExtension : IMarkupExtension + public abstract class ValueConverterExtension : BindableObject, IMarkupExtension { public IValueConverter ProvideValue(IServiceProvider serviceProvider) => (IValueConverter)this; From a762932a09e76e998b0115705552563dec66eaf5 Mon Sep 17 00:00:00 2001 From: Pedro Jesus Date: Sun, 20 Jun 2021 06:34:40 -0300 Subject: [PATCH 05/20] Drawing View (#740) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DrawingView (#468) * DrawingView control, samples, documentation * fix build issues Co-authored-by: Vladislav Antonyuk Co-authored-by: Pedro Jesus * fixed codestyle on Android renderer * GTK renderer adjustments * iOS renderer adjustments * MacOS renderer adjustments * TIzen rendere adjustments * UWP renderer adjustments * WPF tests * Fixed merge conflict * Fixed wpf stackoverflow exception * fixed iOS leaks * revert toast change * removed wpf implementations * root sample page to WelcomePage * removed DrawingView ctor from MainWindow * removed drawing instructions from readme * Update README.md * Drawing View - Fix nullable, add doc to the public method (#1293) * Fix nullable, add doc to the public method. add WPF * Remove WPF renderer from the library. * Change exception message for small images, fix nullable * return null instead of exception (#1339) Co-authored-by: Vladislav Antonyuk <33021114+VladislavAntonyuk@users.noreply.github.com> Co-authored-by: Vladislav Antonyuk Co-authored-by: Brandon Minnick <13558917+brminnick@users.noreply.github.com> Co-authored-by: Javier Suárez Co-authored-by: Gerald Versluis --- samples/XCT.Sample.Android/MainActivity.cs | 3 +- ...rin.CommunityToolkit.Sample.Android.csproj | 1 + ...Xamarin.CommunityToolkit.Sample.GTK.csproj | 2 +- samples/XCT.Sample.UWP/App.xaml.cs | 10 - ...Xamarin.CommunityToolkit.Sample.UWP.csproj | 2 +- .../XCT.Sample.WPF/DrawingViewRenderer.wpf.cs | 102 +++++++++ samples/XCT.Sample.WPF/MainWindow.xaml.cs | 17 +- ...Xamarin.CommunityToolkit.Sample.WPF.csproj | 3 + samples/XCT.Sample.iOS/AppDelegate.cs | 5 +- ...Xamarin.CommunityToolkit.Sample.iOS.csproj | 2 +- samples/XCT.Sample/App.xaml.cs | 1 + .../Pages/Views/DrawingViewPage.xaml | 67 ++++++ .../Pages/Views/DrawingViewPage.xaml.cs | 56 +++++ .../ViewModels/Views/DrawingViewViewModel.cs | 59 +++++ .../ViewModels/Views/ViewsGalleryViewModel.cs | 3 + ...tor.android.ios.macos.uwp.wpf.gtk.tizen.cs | 2 +- .../Views/DrawingView/DrawingView.shared.cs | 93 ++++++++ .../DrawingView/Extensions/Extensions.ios.cs | 24 ++ .../Extensions/Extensions.macos.cs | 24 ++ .../Renderer/DrawingViewRenderer.android.cs | 172 ++++++++++++++ .../Renderer/DrawingViewRenderer.gtk.cs | 190 ++++++++++++++++ .../Renderer/DrawingViewRenderer.ios.cs | 215 ++++++++++++++++++ .../Renderer/DrawingViewRenderer.macos.cs | 201 ++++++++++++++++ .../Renderer/DrawingViewRenderer.tizen.cs | 154 +++++++++++++ .../Renderer/DrawingViewRenderer.uwp.cs | 125 ++++++++++ .../Service/DrawingViewService.android.cs | 106 +++++++++ .../Service/DrawingViewService.gtk.cs | 127 +++++++++++ .../Service/DrawingViewService.ios.cs | 102 +++++++++ .../Service/DrawingViewService.macos.cs | 80 +++++++ .../Service/DrawingViewService.shared.cs | 29 +++ .../Service/DrawingViewService.tizen.cs | 114 ++++++++++ .../Service/DrawingViewService.uwp.cs | 90 ++++++++ .../Service/DrawingViewService.wpf.cs | 115 ++++++++++ .../Helpers/SnackBarLayout.uwp.wpf.cs | 4 +- .../Xamarin.CommunityToolkit.csproj | 6 +- 35 files changed, 2269 insertions(+), 37 deletions(-) create mode 100644 samples/XCT.Sample.WPF/DrawingViewRenderer.wpf.cs create mode 100644 samples/XCT.Sample/Pages/Views/DrawingViewPage.xaml create mode 100644 samples/XCT.Sample/Pages/Views/DrawingViewPage.xaml.cs create mode 100644 samples/XCT.Sample/ViewModels/Views/DrawingViewViewModel.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/DrawingView.shared.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Extensions/Extensions.ios.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Extensions/Extensions.macos.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Renderer/DrawingViewRenderer.android.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Renderer/DrawingViewRenderer.gtk.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Renderer/DrawingViewRenderer.ios.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Renderer/DrawingViewRenderer.macos.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Renderer/DrawingViewRenderer.tizen.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Renderer/DrawingViewRenderer.uwp.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.android.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.gtk.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.ios.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.macos.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.shared.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.tizen.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.uwp.cs create mode 100644 src/CommunityToolkit/Xamarin.CommunityToolkit/Views/DrawingView/Service/DrawingViewService.wpf.cs diff --git a/samples/XCT.Sample.Android/MainActivity.cs b/samples/XCT.Sample.Android/MainActivity.cs index d2392c841..a062874e0 100644 --- a/samples/XCT.Sample.Android/MainActivity.cs +++ b/samples/XCT.Sample.Android/MainActivity.cs @@ -14,8 +14,7 @@ protected override void OnCreate(Bundle savedInstanceState) ToolbarResource = Resource.Layout.Toolbar; base.OnCreate(savedInstanceState); - - global::Xamarin.Forms.Forms.SetFlags("CollectionView_Experimental"); + Essentials.Platform.Init(this, savedInstanceState); global::Xamarin.Forms.Forms.Init(this, savedInstanceState); LoadApplication(new App()); diff --git a/samples/XCT.Sample.Android/Xamarin.CommunityToolkit.Sample.Android.csproj b/samples/XCT.Sample.Android/Xamarin.CommunityToolkit.Sample.Android.csproj index 447965c53..84a7c7d5d 100644 --- a/samples/XCT.Sample.Android/Xamarin.CommunityToolkit.Sample.Android.csproj +++ b/samples/XCT.Sample.Android/Xamarin.CommunityToolkit.Sample.Android.csproj @@ -20,6 +20,7 @@ true true Xamarin.Android.Net.AndroidClientHandler + true diff --git a/samples/XCT.Sample.GTK/Xamarin.CommunityToolkit.Sample.GTK.csproj b/samples/XCT.Sample.GTK/Xamarin.CommunityToolkit.Sample.GTK.csproj index 9c4c97cc5..3a870fd0c 100644 --- a/samples/XCT.Sample.GTK/Xamarin.CommunityToolkit.Sample.GTK.csproj +++ b/samples/XCT.Sample.GTK/Xamarin.CommunityToolkit.Sample.GTK.csproj @@ -102,4 +102,4 @@ - \ No newline at end of file + diff --git a/samples/XCT.Sample.UWP/App.xaml.cs b/samples/XCT.Sample.UWP/App.xaml.cs index 29871212c..30486cbdb 100644 --- a/samples/XCT.Sample.UWP/App.xaml.cs +++ b/samples/XCT.Sample.UWP/App.xaml.cs @@ -1,18 +1,8 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; -using Windows.Foundation; -using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; namespace Xamarin.CommunityToolkit.Sample.UWP diff --git a/samples/XCT.Sample.UWP/Xamarin.CommunityToolkit.Sample.UWP.csproj b/samples/XCT.Sample.UWP/Xamarin.CommunityToolkit.Sample.UWP.csproj index 988b8fbea..78f93a01d 100644 --- a/samples/XCT.Sample.UWP/Xamarin.CommunityToolkit.Sample.UWP.csproj +++ b/samples/XCT.Sample.UWP/Xamarin.CommunityToolkit.Sample.UWP.csproj @@ -164,4 +164,4 @@ 14.0 - \ No newline at end of file + diff --git a/samples/XCT.Sample.WPF/DrawingViewRenderer.wpf.cs b/samples/XCT.Sample.WPF/DrawingViewRenderer.wpf.cs new file mode 100644 index 000000000..8ae128901 --- /dev/null +++ b/samples/XCT.Sample.WPF/DrawingViewRenderer.wpf.cs @@ -0,0 +1,102 @@ +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Windows.Controls; +using System.Windows.Ink; +using System.Windows.Input; +using Xamarin.CommunityToolkit.Sample.WPF; +using Xamarin.CommunityToolkit.UI.Views; +using Xamarin.Forms; +using Xamarin.Forms.Platform.WPF; + +[assembly: ExportRenderer(typeof(DrawingView), typeof(DrawingViewRenderer))] +namespace Xamarin.CommunityToolkit.Sample.WPF +{ + public class DrawingViewRenderer : ViewRenderer + { + InkCanvas? canvas; + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + if (e.PropertyName == DrawingView.PointsProperty.PropertyName) + { + canvas!.Strokes.StrokesChanged -= OnStrokesChanged; + canvas.Strokes.Clear(); + LoadPoints(); + canvas.Strokes.StrokesChanged += OnStrokesChanged; + } + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + if (Control == null && Element != null) + { + canvas = new InkCanvas + { + DefaultDrawingAttributes = + { + Color = Element.LineColor.ToMediaColor(), + Width = Element.LineWidth, + Height = Element.LineWidth + }, + Background = Element.BackgroundColor.ToBrush() + }; + Element.Points.CollectionChanged += OnCollectionChanged; + SetNativeControl(canvas); + + canvas.Strokes.StrokesChanged += OnStrokesChanged; + Control!.PreviewMouseDown += OnPreviewMouseDown; + } + + if (e.OldElement != null) + { + canvas!.Strokes.StrokesChanged -= OnStrokesChanged; + Element!.Points.CollectionChanged -= OnCollectionChanged; + if (Control != null) + Control.PreviewMouseDown -= OnPreviewMouseDown; + } + } + + void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) => LoadPoints(); + + void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) + { + canvas!.Strokes.Clear(); + Element.Points.Clear(); + } + + void OnStrokesChanged(object sender, StrokeCollectionChangedEventArgs e) + { + Element.Points.CollectionChanged -= OnCollectionChanged; + if (e.Added.Count > 0) + { + var points = e.Added.First().StylusPoints.Select(point => new Point(point.X, point.Y)); + Element.Points.Clear(); + foreach (var point in points) + Element.Points.Add(point); + + if (Element.Points.Count > 0) + { + if (Element.DrawingCompletedCommand.CanExecute(null)) + Element.DrawingCompletedCommand.Execute(Element.Points); + } + + if (Element.ClearOnFinish) + Element.Points.Clear(); + } + Element.Points.CollectionChanged += OnCollectionChanged; + } + + void LoadPoints() + { + var stylusPoints = Element?.Points.Select(point => new StylusPoint(point.X, point.Y)).ToList(); + if (stylusPoints is { Count: > 0 }) + { + var stroke = new Stroke(new StylusPointCollection(stylusPoints), canvas!.DefaultDrawingAttributes); + canvas.Strokes.Add(stroke); + } + } + } +} \ No newline at end of file diff --git a/samples/XCT.Sample.WPF/MainWindow.xaml.cs b/samples/XCT.Sample.WPF/MainWindow.xaml.cs index c5595dcb7..8fbfa29f4 100644 --- a/samples/XCT.Sample.WPF/MainWindow.xaml.cs +++ b/samples/XCT.Sample.WPF/MainWindow.xaml.cs @@ -1,17 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; +using Xamarin.CommunityToolkit.UI.Views; using Xamarin.Forms.PancakeView.Platforms.WPF; using Xamarin.Forms.Platform.WPF; @@ -25,7 +12,7 @@ public partial class MainWindow : FormsApplicationPage public MainWindow() { InitializeComponent(); - Xamarin.Forms.Forms.Init(); + Forms.Forms.Init(); PancakeViewRenderer.Init(); LoadApplication(new Xamarin.CommunityToolkit.Sample.App()); } diff --git a/samples/XCT.Sample.WPF/Xamarin.CommunityToolkit.Sample.WPF.csproj b/samples/XCT.Sample.WPF/Xamarin.CommunityToolkit.Sample.WPF.csproj index e8be79785..ba3765509 100644 --- a/samples/XCT.Sample.WPF/Xamarin.CommunityToolkit.Sample.WPF.csproj +++ b/samples/XCT.Sample.WPF/Xamarin.CommunityToolkit.Sample.WPF.csproj @@ -65,6 +65,9 @@ App.xaml Code + + Code + MainWindow.xaml Code diff --git a/samples/XCT.Sample.iOS/AppDelegate.cs b/samples/XCT.Sample.iOS/AppDelegate.cs index 5b3a3540c..bf5ca64ed 100644 --- a/samples/XCT.Sample.iOS/AppDelegate.cs +++ b/samples/XCT.Sample.iOS/AppDelegate.cs @@ -6,8 +6,8 @@ namespace Xamarin.CommunityToolkit.Sample.iOS // The UIApplicationDelegate for the application. This class is responsible for launching the // User Interface of the application, as well as listening (and optionally responding) to // application events from iOS. - [Register("AppDelegate")] - public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate + [Register(nameof(AppDelegate))] + public class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate { // This method is invoked when the application has loaded and is ready to run. In this // method you should instantiate the window, load the UI into it and then make the window @@ -16,7 +16,6 @@ public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsAppli // You have 17 seconds to return from this method, or iOS will terminate your application. public override bool FinishedLaunching(UIApplication app, NSDictionary options) { - global::Xamarin.Forms.Forms.SetFlags("CollectionView_Experimental"); global::Xamarin.Forms.Forms.Init(); LoadApplication(new App()); diff --git a/samples/XCT.Sample.iOS/Xamarin.CommunityToolkit.Sample.iOS.csproj b/samples/XCT.Sample.iOS/Xamarin.CommunityToolkit.Sample.iOS.csproj index 8bd3a9310..5575cff4e 100644 --- a/samples/XCT.Sample.iOS/Xamarin.CommunityToolkit.Sample.iOS.csproj +++ b/samples/XCT.Sample.iOS/Xamarin.CommunityToolkit.Sample.iOS.csproj @@ -183,4 +183,4 @@ - \ No newline at end of file + diff --git a/samples/XCT.Sample/App.xaml.cs b/samples/XCT.Sample/App.xaml.cs index 6e2137b3b..2074232e5 100644 --- a/samples/XCT.Sample/App.xaml.cs +++ b/samples/XCT.Sample/App.xaml.cs @@ -1,6 +1,7 @@ using System.Globalization; using Xamarin.CommunityToolkit.Helpers; using Xamarin.CommunityToolkit.Sample.Pages; +using Xamarin.CommunityToolkit.Sample.Pages.Views; using Xamarin.CommunityToolkit.Sample.Resx; using Xamarin.Forms.PlatformConfiguration; using Xamarin.Forms.PlatformConfiguration.WindowsSpecific; diff --git a/samples/XCT.Sample/Pages/Views/DrawingViewPage.xaml b/samples/XCT.Sample/Pages/Views/DrawingViewPage.xaml new file mode 100644 index 000000000..12ee78764 --- /dev/null +++ b/samples/XCT.Sample/Pages/Views/DrawingViewPage.xaml @@ -0,0 +1,67 @@ + + + + + + + + + + +