diff --git a/components/LayoutTransformControl/src/LayoutTransformControl.Properties.cs b/components/LayoutTransformControl/src/LayoutTransformControl.Properties.cs index 692476e4..0cbb79a4 100644 --- a/components/LayoutTransformControl/src/LayoutTransformControl.Properties.cs +++ b/components/LayoutTransformControl/src/LayoutTransformControl.Properties.cs @@ -127,6 +127,7 @@ private void UnsubscribeFromTransformPropertyChanges(Transform transform) foreach (var propertyChangeEventSource in propertyChangeEventSources) { propertyChangeEventSource.ValueChanged -= OnTransformPropertyChanged; + propertyChangeEventSource.Unregister(); } _transformPropertyChangeEventSources.Remove(transform); @@ -153,7 +154,7 @@ private void SubscribeToTransformPropertyChanges(Transform transform) if (rotateTransform != null) { - var anglePropertyChangeEventSource = new PropertyChangeEventSource(rotateTransform, "Angle"); + var anglePropertyChangeEventSource = new PropertyChangeEventSource(rotateTransform, RotateTransform.AngleProperty); anglePropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged; propertyChangeEventSources.Add(anglePropertyChangeEventSource); return; @@ -163,11 +164,11 @@ private void SubscribeToTransformPropertyChanges(Transform transform) if (scaleTransform != null) { - var scaleXPropertyChangeEventSource = new PropertyChangeEventSource(scaleTransform, "ScaleX"); + var scaleXPropertyChangeEventSource = new PropertyChangeEventSource(scaleTransform, ScaleTransform.ScaleXProperty); scaleXPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged; propertyChangeEventSources.Add(scaleXPropertyChangeEventSource); - var scaleYPropertyChangeEventSource = new PropertyChangeEventSource(scaleTransform, "ScaleY"); + var scaleYPropertyChangeEventSource = new PropertyChangeEventSource(scaleTransform, ScaleTransform.ScaleYProperty); scaleYPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged; propertyChangeEventSources.Add(scaleYPropertyChangeEventSource); return; @@ -177,11 +178,11 @@ private void SubscribeToTransformPropertyChanges(Transform transform) if (skewTransform != null) { - var angleXPropertyChangeEventSource = new PropertyChangeEventSource(skewTransform, "AngleX"); + var angleXPropertyChangeEventSource = new PropertyChangeEventSource(skewTransform, SkewTransform.AngleXProperty); angleXPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged; propertyChangeEventSources.Add(angleXPropertyChangeEventSource); - var angleYPropertyChangeEventSource = new PropertyChangeEventSource(skewTransform, "AngleY"); + var angleYPropertyChangeEventSource = new PropertyChangeEventSource(skewTransform, SkewTransform.AngleYProperty); angleYPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged; propertyChangeEventSources.Add(angleYPropertyChangeEventSource); return; @@ -194,7 +195,7 @@ private void SubscribeToTransformPropertyChanges(Transform transform) var matrixPropertyChangeEventSource = new PropertyChangeEventSource( matrixTransform, - "Matrix"); + MatrixTransform.MatrixProperty); matrixPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged; propertyChangeEventSources.Add(matrixPropertyChangeEventSource); } diff --git a/components/LayoutTransformControl/src/LayoutTransformControl.cs b/components/LayoutTransformControl/src/LayoutTransformControl.cs index 85879e09..fd6f489e 100644 --- a/components/LayoutTransformControl/src/LayoutTransformControl.cs +++ b/components/LayoutTransformControl/src/LayoutTransformControl.cs @@ -9,9 +9,12 @@ namespace CommunityToolkit.WinUI.Controls; /// Control that implements support for transformations as if applied by LayoutTransform. /// [ContentProperty(Name = "Child")] - +[TemplatePart(Name = "LayoutRoot", Type = typeof(Panel))] +[TemplatePart(Name = "MatrixTransform", Type = typeof(MatrixTransform))] public partial class LayoutTransformControl : Control { + private static Size EmptySize => new Size(); + /// /// Value used to work around double arithmetic rounding issues. /// @@ -45,7 +48,7 @@ public partial class LayoutTransformControl : Control /// /// Actual DesiredSize of Child element. /// - private Size _childActualSize = Size.Empty; + private Size _childActualSize = EmptySize; /// /// Initializes a new instance of the class. @@ -235,11 +238,11 @@ protected override Size MeasureOverride(Size availableSize) if (_layoutRoot == null || child == null) { // No content, no size - return Size.Empty; + return EmptySize; } Size measureSize; - if (_childActualSize == Size.Empty) + if (_childActualSize == EmptySize) { // Determine the largest size after the transformation measureSize = ComputeLargestTransformedSize(availableSize); @@ -301,7 +304,7 @@ protected override Size ArrangeOverride(Size finalSize) _layoutRoot.Arrange(finalRect); // This is the first opportunity to find out the Child's true DesiredSize - if (IsSizeSmaller(finalSizeTransformed, child.RenderSize) && (Size.Empty == _childActualSize)) + if (IsSizeSmaller(finalSizeTransformed, child.RenderSize) && (EmptySize == _childActualSize)) { // Unfortunately, all the work so far is invalid because the wrong DesiredSize was used // Make a note of the actual DesiredSize @@ -313,7 +316,7 @@ protected override Size ArrangeOverride(Size finalSize) else { // Clear the "need to measure/arrange again" flag - _childActualSize = Size.Empty; + _childActualSize = EmptySize; } // Return result to perform the transformation @@ -329,7 +332,7 @@ protected override Size ArrangeOverride(Size finalSize) private Size ComputeLargestTransformedSize(Size arrangeBounds) { // Computed largest transformed size - Size computedSize = Size.Empty; + Size computedSize = EmptySize; // Detect infinite bounds and constrain the scenario bool infiniteWidth = double.IsInfinity(arrangeBounds.Width); diff --git a/components/LayoutTransformControl/src/PropertyChangeEventSource.cs b/components/LayoutTransformControl/src/PropertyChangeEventSource.cs index 9ea384c6..ac141b06 100644 --- a/components/LayoutTransformControl/src/PropertyChangeEventSource.cs +++ b/components/LayoutTransformControl/src/PropertyChangeEventSource.cs @@ -8,87 +8,38 @@ namespace CommunityToolkit.WinUI.Controls; /// Allows raise an event when the value of a dependency property changes when a view model is otherwise not necessary. /// /// Type of the DependencyProperty -internal partial class PropertyChangeEventSource : FrameworkElement +internal partial class PropertyChangeEventSource { private readonly DependencyObject _source; + private readonly DependencyProperty _property; + private readonly long _token; /// /// Occurs when the value changes. /// public event EventHandler ValueChanged; - /// - /// Value Dependency Property - /// - public static readonly DependencyProperty ValueProperty = - DependencyProperty.Register( - "Value", - typeof(TPropertyType), - typeof(PropertyChangeEventSource), - new PropertyMetadata(default(TPropertyType), OnValueChanged)); - - /// - /// Gets or sets the Value property. This dependency property - /// indicates the value. - /// - public TPropertyType Value - { - get { return (TPropertyType)GetValue(ValueProperty); } - set { SetValue(ValueProperty, value); } - } - - /// - /// Handles changes to the Value property. - /// - /// - /// The on which the property has changed value. - /// - /// - /// Event data that is issued by any event that - /// tracks changes to the effective value of this property. - /// - private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var target = (PropertyChangeEventSource)d; - TPropertyType oldValue = (TPropertyType)e.OldValue; - TPropertyType newValue = target.Value; - target.OnValueChanged(oldValue, newValue); - } - - /// - /// Provides derived classes an opportunity to handle changes to the Value property. - /// - /// The old Value value - /// The new Value value - private void OnValueChanged(TPropertyType oldValue, TPropertyType newValue) - { - ValueChanged?.Invoke(_source, newValue); - } - /// /// Initializes a new instance of the class. /// /// The source. - /// Name of the property. - /// The binding mode. + /// Monitor this dependency property for changes. #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. public PropertyChangeEventSource( #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. DependencyObject source, - string propertyName, - BindingMode bindingMode = BindingMode.TwoWay) + DependencyProperty property) { _source = source; + _property = property; + _token = source.RegisterPropertyChangedCallback(property, (_, _) => + { + ValueChanged?.Invoke(this, (TPropertyType)source.GetValue(property)); + }); + } - // Bind to the property to be able to get its changes relayed as events through the ValueChanged event. - var binding = - new Binding - { - Source = source, - Path = new PropertyPath(propertyName), - Mode = bindingMode - }; - - SetBinding(ValueProperty, binding); + public void Unregister() + { + _source.UnregisterPropertyChangedCallback(_property, _token); } }