Skip to content

Commit

Permalink
Merge pull request #9307 from robloo/control-sizechanged
Browse files Browse the repository at this point in the history
Add Control.SizeChanged Event
  • Loading branch information
maxkatz6 authored Nov 1, 2022
2 parents af1c316 + 66595ba commit 7b5b167
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 2 deletions.
30 changes: 30 additions & 0 deletions src/Avalonia.Base/Interactivity/RoutedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,59 @@

namespace Avalonia.Interactivity
{
/// <summary>
/// Provides state information and data specific to a routed event.
/// </summary>
public class RoutedEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="RoutedEventArgs"/> class.
/// </summary>
public RoutedEventArgs()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RoutedEventArgs"/> class.
/// </summary>
/// <param name="routedEvent">The routed event associated with these event args.</param>
public RoutedEventArgs(RoutedEvent? routedEvent)
{
RoutedEvent = routedEvent;
}

/// <summary>
/// Initializes a new instance of the <see cref="RoutedEventArgs"/> class.
/// </summary>
/// <param name="routedEvent">The routed event associated with these event args.</param>
/// <param name="source">The source object that raised the routed event.</param>
public RoutedEventArgs(RoutedEvent? routedEvent, IInteractive? source)
{
RoutedEvent = routedEvent;
Source = source;
}

/// <summary>
/// Gets or sets a value indicating whether the routed event has already been handled.
/// </summary>
/// <remarks>
/// Once handled, a routed event should be ignored.
/// </remarks>
public bool Handled { get; set; }

/// <summary>
/// Gets or sets the routed event associated with these event args.
/// </summary>
public RoutedEvent? RoutedEvent { get; set; }

/// <summary>
/// Gets or sets the routing strategy (direct, bubbling, or tunneling) of the routed event.
/// </summary>
public RoutingStrategies Route { get; set; }

/// <summary>
/// Gets or sets the source object that raised the routed event.
/// </summary>
public IInteractive? Source { get; set; }
}
}
41 changes: 39 additions & 2 deletions src/Avalonia.Controls/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ public class Control : InputElement, IControl, INamed, IVisualBrushInitialize, I
nameof(Unloaded),
RoutingStrategies.Direct);

/// <summary>
/// Defines the <see cref="SizeChanged"/> event.
/// </summary>
public static readonly RoutedEvent<SizeChangedEventArgs> SizeChangedEvent =
RoutedEvent.Register<Control, SizeChangedEventArgs>(
nameof(SizeChanged), RoutingStrategies.Direct);

/// <summary>
/// Defines the <see cref="FlowDirection"/> property.
/// </summary>
Expand Down Expand Up @@ -211,6 +218,15 @@ public event EventHandler<RoutedEventArgs>? Unloaded
remove => RemoveHandler(UnloadedEvent, value);
}

/// <summary>
/// Occurs when the bounds (actual size) of the control have changed.
/// </summary>
public event EventHandler<SizeChangedEventArgs>? SizeChanged
{
add => AddHandler(SizeChangedEvent, value);
remove => RemoveHandler(SizeChangedEvent, value);
}

public new IControl? Parent => (IControl?)base.Parent;

/// <summary>
Expand Down Expand Up @@ -530,14 +546,35 @@ protected override void OnKeyUp(KeyEventArgs e)
}
}

/// <inheritdoc/>
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

if (change.Property == FlowDirectionProperty)
if (change.Property == BoundsProperty)
{
var oldValue = change.GetOldValue<Rect>();
var newValue = change.GetNewValue<Rect>();

// Bounds is a Rect with an X/Y Position as well as Height/Width.
// This means it is possible for the Rect to change position but not size.
// Therefore, we want to explicity check only the size and raise an event
// only when that size has changed.
if (newValue.Size != oldValue.Size)
{
var sizeChangedEventArgs = new SizeChangedEventArgs(
SizeChangedEvent,
source: this,
previousSize: new Size(oldValue.Width, oldValue.Height),
newSize: new Size(newValue.Width, newValue.Height));

RaiseEvent(sizeChangedEventArgs);
}
}
else if (change.Property == FlowDirectionProperty)
{
InvalidateMirrorTransform();

foreach (var visual in VisualChildren)
{
if (visual is Control child)
Expand Down
83 changes: 83 additions & 0 deletions src/Avalonia.Controls/SizeChangedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Utilities;

namespace Avalonia.Controls
{
/// <summary>
/// Provides data specific to a SizeChanged event.
/// </summary>
public class SizeChangedEventArgs : RoutedEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="SizeChangedEventArgs"/> class.
/// </summary>
/// <param name="routedEvent">The routed event associated with these event args.</param>
public SizeChangedEventArgs(RoutedEvent? routedEvent)
: base (routedEvent)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SizeChangedEventArgs"/> class.
/// </summary>
/// <param name="routedEvent">The routed event associated with these event args.</param>
/// <param name="source">The source object that raised the routed event.</param>
public SizeChangedEventArgs(RoutedEvent? routedEvent, IInteractive? source)
: base(routedEvent, source)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SizeChangedEventArgs"/> class.
/// </summary>
/// <param name="routedEvent">The routed event associated with these event args.</param>
/// <param name="source">The source object that raised the routed event.</param>
/// <param name="previousSize">The previous size (or bounds) of the object.</param>
/// <param name="newSize">The new size (or bounds) of the object.</param>
public SizeChangedEventArgs(
RoutedEvent? routedEvent,
IInteractive? source,
Size previousSize,
Size newSize)
: base(routedEvent, source)
{
PreviousSize = previousSize;
NewSize = newSize;
}

/// <summary>
/// Gets a value indicating whether the height of the new size is considered
/// different than the previous size height.
/// </summary>
/// <remarks>
/// This will take into account layout epsilon and will not be true if both
/// heights are considered equivalent for layout purposes. Remember there can
/// be small variations in the calculations between layout cycles due to
/// rounding and precision even when the size has not otherwise changed.
/// </remarks>
public bool HeightChanged => !MathUtilities.AreClose(NewSize.Height, PreviousSize.Height, LayoutHelper.LayoutEpsilon);

/// <summary>
/// Gets the new size (or bounds) of the object.
/// </summary>
public Size NewSize { get; init; }

/// <summary>
/// Gets the previous size (or bounds) of the object.
/// </summary>
public Size PreviousSize { get; init; }

/// <summary>
/// Gets a value indicating whether the width of the new size is considered
/// different than the previous size width.
/// </summary>
/// <remarks>
/// This will take into account layout epsilon and will not be true if both
/// widths are considered equivalent for layout purposes. Remember there can
/// be small variations in the calculations between layout cycles due to
/// rounding and precision even when the size has not otherwise changed.
/// </remarks>
public bool WidthChanged => !MathUtilities.AreClose(NewSize.Width, PreviousSize.Width, LayoutHelper.LayoutEpsilon);
}
}

0 comments on commit 7b5b167

Please sign in to comment.