|
@@ -59,12 +59,11 @@ namespace Avalonia.Controls
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Defines the <see cref="IsExpanded"/> property.
|
|
/// Defines the <see cref="IsExpanded"/> property.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- public static readonly DirectProperty<Expander, bool> IsExpandedProperty =
|
|
|
|
|
- AvaloniaProperty.RegisterDirect<Expander, bool>(
|
|
|
|
|
|
|
+ public static readonly StyledProperty<bool> IsExpandedProperty =
|
|
|
|
|
+ AvaloniaProperty.Register<Expander, bool>(
|
|
|
nameof(IsExpanded),
|
|
nameof(IsExpanded),
|
|
|
- o => o.IsExpanded,
|
|
|
|
|
- (o, v) => o.IsExpanded = v,
|
|
|
|
|
- defaultBindingMode: Data.BindingMode.TwoWay);
|
|
|
|
|
|
|
+ defaultBindingMode: BindingMode.TwoWay,
|
|
|
|
|
+ coerce: CoerceIsExpanded);
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Defines the <see cref="Collapsed"/> event.
|
|
/// Defines the <see cref="Collapsed"/> event.
|
|
@@ -77,8 +76,8 @@ namespace Avalonia.Controls
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Defines the <see cref="Collapsing"/> event.
|
|
/// Defines the <see cref="Collapsing"/> event.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- public static readonly RoutedEvent<RoutedEventArgs> CollapsingEvent =
|
|
|
|
|
- RoutedEvent.Register<Expander, RoutedEventArgs>(
|
|
|
|
|
|
|
+ public static readonly RoutedEvent<CancelRoutedEventArgs> CollapsingEvent =
|
|
|
|
|
+ RoutedEvent.Register<Expander, CancelRoutedEventArgs>(
|
|
|
nameof(Collapsing),
|
|
nameof(Collapsing),
|
|
|
RoutingStrategies.Bubble);
|
|
RoutingStrategies.Bubble);
|
|
|
|
|
|
|
@@ -93,13 +92,12 @@ namespace Avalonia.Controls
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Defines the <see cref="Expanding"/> event.
|
|
/// Defines the <see cref="Expanding"/> event.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- public static readonly RoutedEvent<RoutedEventArgs> ExpandingEvent =
|
|
|
|
|
- RoutedEvent.Register<Expander, RoutedEventArgs>(
|
|
|
|
|
|
|
+ public static readonly RoutedEvent<CancelRoutedEventArgs> ExpandingEvent =
|
|
|
|
|
+ RoutedEvent.Register<Expander, CancelRoutedEventArgs>(
|
|
|
nameof(Expanding),
|
|
nameof(Expanding),
|
|
|
RoutingStrategies.Bubble);
|
|
RoutingStrategies.Bubble);
|
|
|
|
|
|
|
|
private bool _ignorePropertyChanged = false;
|
|
private bool _ignorePropertyChanged = false;
|
|
|
- private bool _isExpanded;
|
|
|
|
|
private CancellationTokenSource? _lastTransitionCts;
|
|
private CancellationTokenSource? _lastTransitionCts;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -134,50 +132,8 @@ namespace Avalonia.Controls
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
public bool IsExpanded
|
|
public bool IsExpanded
|
|
|
{
|
|
{
|
|
|
- get => _isExpanded;
|
|
|
|
|
- set
|
|
|
|
|
- {
|
|
|
|
|
- // It is important here that IsExpanded is a direct property so events can be invoked
|
|
|
|
|
- // BEFORE the property system gets notified of updated values. This is because events
|
|
|
|
|
- // may be canceled by external code.
|
|
|
|
|
- if (_isExpanded != value)
|
|
|
|
|
- {
|
|
|
|
|
- RoutedEventArgs eventArgs;
|
|
|
|
|
-
|
|
|
|
|
- if (value)
|
|
|
|
|
- {
|
|
|
|
|
- eventArgs = new RoutedEventArgs(ExpandingEvent, this);
|
|
|
|
|
- OnExpanding(eventArgs);
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- eventArgs = new RoutedEventArgs(CollapsingEvent, this);
|
|
|
|
|
- OnCollapsing(eventArgs);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (eventArgs.Handled)
|
|
|
|
|
- {
|
|
|
|
|
- // If the event was externally handled (canceled) we must still notify the value has changed.
|
|
|
|
|
- // This property changed notification will update any external code observing this property that itself may have set the new value.
|
|
|
|
|
- // We are essentially reverted any external state change along with ignoring the IsExpanded property set.
|
|
|
|
|
- // Remember IsExpanded is usually controlled by a ToggleButton in the control theme.
|
|
|
|
|
- _ignorePropertyChanged = true;
|
|
|
|
|
-
|
|
|
|
|
- RaisePropertyChanged(
|
|
|
|
|
- IsExpandedProperty,
|
|
|
|
|
- oldValue: value,
|
|
|
|
|
- newValue: _isExpanded,
|
|
|
|
|
- BindingPriority.LocalValue,
|
|
|
|
|
- isEffectiveValue: true);
|
|
|
|
|
-
|
|
|
|
|
- _ignorePropertyChanged = false;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- SetAndRaise(IsExpandedProperty, ref _isExpanded, value);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ get => GetValue(IsExpandedProperty);
|
|
|
|
|
+ set => SetValue(IsExpandedProperty, value);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -193,10 +149,10 @@ namespace Avalonia.Controls
|
|
|
/// Occurs as the content area is closing.
|
|
/// Occurs as the content area is closing.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <remarks>
|
|
/// <remarks>
|
|
|
- /// The event args <see cref="RoutedEventArgs.Handled"/> property may be set to true to cancel the event
|
|
|
|
|
|
|
+ /// The event args <see cref="CancelRoutedEventArgs.Cancel"/> property may be set to true to cancel the event
|
|
|
/// and keep the control open (expanded).
|
|
/// and keep the control open (expanded).
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
|
- public event EventHandler<RoutedEventArgs>? Collapsing
|
|
|
|
|
|
|
+ public event EventHandler<CancelRoutedEventArgs>? Collapsing
|
|
|
{
|
|
{
|
|
|
add => AddHandler(CollapsingEvent, value);
|
|
add => AddHandler(CollapsingEvent, value);
|
|
|
remove => RemoveHandler(CollapsingEvent, value);
|
|
remove => RemoveHandler(CollapsingEvent, value);
|
|
@@ -215,10 +171,10 @@ namespace Avalonia.Controls
|
|
|
/// Occurs as the content area is opening.
|
|
/// Occurs as the content area is opening.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <remarks>
|
|
/// <remarks>
|
|
|
- /// The event args <see cref="RoutedEventArgs.Handled"/> property may be set to true to cancel the event
|
|
|
|
|
|
|
+ /// The event args <see cref="CancelRoutedEventArgs.Cancel"/> property may be set to true to cancel the event
|
|
|
/// and keep the control closed (collapsed).
|
|
/// and keep the control closed (collapsed).
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
|
- public event EventHandler<RoutedEventArgs>? Expanding
|
|
|
|
|
|
|
+ public event EventHandler<CancelRoutedEventArgs>? Expanding
|
|
|
{
|
|
{
|
|
|
add => AddHandler(ExpandingEvent, value);
|
|
add => AddHandler(ExpandingEvent, value);
|
|
|
remove => RemoveHandler(ExpandingEvent, value);
|
|
remove => RemoveHandler(ExpandingEvent, value);
|
|
@@ -332,5 +288,63 @@ namespace Avalonia.Controls
|
|
|
|
|
|
|
|
PseudoClasses.Set(":expanded", IsExpanded);
|
|
PseudoClasses.Set(":expanded", IsExpanded);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Called when the <see cref="IsExpanded"/> property has to be coerced.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="value">The value to coerce.</param>
|
|
|
|
|
+ protected virtual bool OnCoerceIsExpanded(bool value)
|
|
|
|
|
+ {
|
|
|
|
|
+ CancelRoutedEventArgs eventArgs;
|
|
|
|
|
+
|
|
|
|
|
+ if (value)
|
|
|
|
|
+ {
|
|
|
|
|
+ eventArgs = new CancelRoutedEventArgs(ExpandingEvent, this);
|
|
|
|
|
+ OnExpanding(eventArgs);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ eventArgs = new CancelRoutedEventArgs(CollapsingEvent, this);
|
|
|
|
|
+ OnCollapsing(eventArgs);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (eventArgs.Cancel)
|
|
|
|
|
+ {
|
|
|
|
|
+ // If the event was externally canceled we must still notify the value has changed.
|
|
|
|
|
+ // This property changed notification will update any external code observing this property that itself may have set the new value.
|
|
|
|
|
+ // We are essentially reverted any external state change along with ignoring the IsExpanded property set.
|
|
|
|
|
+ // Remember IsExpanded is usually controlled by a ToggleButton in the control theme and is also used for animations.
|
|
|
|
|
+ _ignorePropertyChanged = true;
|
|
|
|
|
+
|
|
|
|
|
+ RaisePropertyChanged(
|
|
|
|
|
+ IsExpandedProperty,
|
|
|
|
|
+ oldValue: value,
|
|
|
|
|
+ newValue: !value,
|
|
|
|
|
+ BindingPriority.LocalValue,
|
|
|
|
|
+ isEffectiveValue: true);
|
|
|
|
|
+
|
|
|
|
|
+ _ignorePropertyChanged = false;
|
|
|
|
|
+
|
|
|
|
|
+ return !value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Coerces/validates the <see cref="IsExpanded"/> property value.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="instance">The <see cref="Expander"/> instance.</param>
|
|
|
|
|
+ /// <param name="value">The value to coerce.</param>
|
|
|
|
|
+ /// <returns>The coerced/validated value.</returns>
|
|
|
|
|
+ private static bool CoerceIsExpanded(AvaloniaObject instance, bool value)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (instance is Expander expander)
|
|
|
|
|
+ {
|
|
|
|
|
+ return expander.OnCoerceIsExpanded(value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|