Skip to content

Fix the issue causing crashes with LayoutTransformControl in AOT publishing. #692

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ private void UnsubscribeFromTransformPropertyChanges(Transform transform)
foreach (var propertyChangeEventSource in propertyChangeEventSources)
{
propertyChangeEventSource.ValueChanged -= OnTransformPropertyChanged;
propertyChangeEventSource.Unregister();
}

_transformPropertyChangeEventSources.Remove(transform);
Expand All @@ -153,7 +154,7 @@ private void SubscribeToTransformPropertyChanges(Transform transform)

if (rotateTransform != null)
{
var anglePropertyChangeEventSource = new PropertyChangeEventSource<double>(rotateTransform, "Angle");
var anglePropertyChangeEventSource = new PropertyChangeEventSource<double>(rotateTransform, RotateTransform.AngleProperty);
anglePropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged;
propertyChangeEventSources.Add(anglePropertyChangeEventSource);
return;
Expand All @@ -163,11 +164,11 @@ private void SubscribeToTransformPropertyChanges(Transform transform)

if (scaleTransform != null)
{
var scaleXPropertyChangeEventSource = new PropertyChangeEventSource<double>(scaleTransform, "ScaleX");
var scaleXPropertyChangeEventSource = new PropertyChangeEventSource<double>(scaleTransform, ScaleTransform.ScaleXProperty);
scaleXPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged;
propertyChangeEventSources.Add(scaleXPropertyChangeEventSource);

var scaleYPropertyChangeEventSource = new PropertyChangeEventSource<double>(scaleTransform, "ScaleY");
var scaleYPropertyChangeEventSource = new PropertyChangeEventSource<double>(scaleTransform, ScaleTransform.ScaleYProperty);
scaleYPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged;
propertyChangeEventSources.Add(scaleYPropertyChangeEventSource);
return;
Expand All @@ -177,11 +178,11 @@ private void SubscribeToTransformPropertyChanges(Transform transform)

if (skewTransform != null)
{
var angleXPropertyChangeEventSource = new PropertyChangeEventSource<double>(skewTransform, "AngleX");
var angleXPropertyChangeEventSource = new PropertyChangeEventSource<double>(skewTransform, SkewTransform.AngleXProperty);
angleXPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged;
propertyChangeEventSources.Add(angleXPropertyChangeEventSource);

var angleYPropertyChangeEventSource = new PropertyChangeEventSource<double>(skewTransform, "AngleY");
var angleYPropertyChangeEventSource = new PropertyChangeEventSource<double>(skewTransform, SkewTransform.AngleYProperty);
angleYPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged;
propertyChangeEventSources.Add(angleYPropertyChangeEventSource);
return;
Expand All @@ -194,7 +195,7 @@ private void SubscribeToTransformPropertyChanges(Transform transform)
var matrixPropertyChangeEventSource =
new PropertyChangeEventSource<double>(
matrixTransform,
"Matrix");
MatrixTransform.MatrixProperty);
matrixPropertyChangeEventSource.ValueChanged += OnTransformPropertyChanged;
propertyChangeEventSources.Add(matrixPropertyChangeEventSource);
}
Expand Down
17 changes: 10 additions & 7 deletions components/LayoutTransformControl/src/LayoutTransformControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ namespace CommunityToolkit.WinUI.Controls;
/// Control that implements support for transformations as if applied by LayoutTransform.
/// </summary>
[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();

/// <summary>
/// Value used to work around double arithmetic rounding issues.
/// </summary>
Expand Down Expand Up @@ -45,7 +48,7 @@ public partial class LayoutTransformControl : Control
/// <summary>
/// Actual DesiredSize of Child element.
/// </summary>
private Size _childActualSize = Size.Empty;
private Size _childActualSize = EmptySize;

/// <summary>
/// Initializes a new instance of the <see cref="LayoutTransformControl"/> class.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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);
Expand Down
77 changes: 14 additions & 63 deletions components/LayoutTransformControl/src/PropertyChangeEventSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </summary>
/// <typeparam name="TPropertyType">Type of the DependencyProperty</typeparam>
internal partial class PropertyChangeEventSource<TPropertyType> : FrameworkElement
internal partial class PropertyChangeEventSource<TPropertyType>
{
private readonly DependencyObject _source;
private readonly DependencyProperty _property;
private readonly long _token;

/// <summary>
/// Occurs when the value changes.
/// </summary>
public event EventHandler<TPropertyType> ValueChanged;

/// <summary>
/// Value Dependency Property
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value",
typeof(TPropertyType),
typeof(PropertyChangeEventSource<TPropertyType>),
new PropertyMetadata(default(TPropertyType), OnValueChanged));

/// <summary>
/// Gets or sets the Value property. This dependency property
/// indicates the value.
/// </summary>
public TPropertyType Value
{
get { return (TPropertyType)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}

/// <summary>
/// Handles changes to the Value property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (PropertyChangeEventSource<TPropertyType>)d;
TPropertyType oldValue = (TPropertyType)e.OldValue;
TPropertyType newValue = target.Value;
target.OnValueChanged(oldValue, newValue);
}

/// <summary>
/// Provides derived classes an opportunity to handle changes to the Value property.
/// </summary>
/// <param name="oldValue">The old Value value</param>
/// <param name="newValue">The new Value value</param>
private void OnValueChanged(TPropertyType oldValue, TPropertyType newValue)
{
ValueChanged?.Invoke(_source, newValue);
}

/// <summary>
/// Initializes a new instance of the <see cref="PropertyChangeEventSource{TPropertyType}"/> class.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="propertyName">Name of the property.</param>
/// <param name="bindingMode">The binding mode.</param>
/// <param name="property">Monitor this dependency property for changes.</param>
#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);
}
}