|
|
|
@ -59,12 +59,11 @@ namespace Avalonia.Controls |
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="IsExpanded"/> property.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly DirectProperty<Expander, bool> IsExpandedProperty = |
|
|
|
AvaloniaProperty.RegisterDirect<Expander, bool>( |
|
|
|
public static readonly StyledProperty<bool> IsExpandedProperty = |
|
|
|
AvaloniaProperty.Register<Expander, bool>( |
|
|
|
nameof(IsExpanded), |
|
|
|
o => o.IsExpanded, |
|
|
|
(o, v) => o.IsExpanded = v, |
|
|
|
defaultBindingMode: Data.BindingMode.TwoWay); |
|
|
|
defaultBindingMode: BindingMode.TwoWay, |
|
|
|
coerce: CoerceIsExpanded); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="Collapsed"/> event.
|
|
|
|
@ -77,8 +76,8 @@ namespace Avalonia.Controls |
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="Collapsing"/> event.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly RoutedEvent<RoutedEventArgs> CollapsingEvent = |
|
|
|
RoutedEvent.Register<Expander, RoutedEventArgs>( |
|
|
|
public static readonly RoutedEvent<CancelRoutedEventArgs> CollapsingEvent = |
|
|
|
RoutedEvent.Register<Expander, CancelRoutedEventArgs>( |
|
|
|
nameof(Collapsing), |
|
|
|
RoutingStrategies.Bubble); |
|
|
|
|
|
|
|
@ -93,13 +92,12 @@ namespace Avalonia.Controls |
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="Expanding"/> event.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly RoutedEvent<RoutedEventArgs> ExpandingEvent = |
|
|
|
RoutedEvent.Register<Expander, RoutedEventArgs>( |
|
|
|
public static readonly RoutedEvent<CancelRoutedEventArgs> ExpandingEvent = |
|
|
|
RoutedEvent.Register<Expander, CancelRoutedEventArgs>( |
|
|
|
nameof(Expanding), |
|
|
|
RoutingStrategies.Bubble); |
|
|
|
|
|
|
|
private bool _ignorePropertyChanged = false; |
|
|
|
private bool _isExpanded; |
|
|
|
private CancellationTokenSource? _lastTransitionCts; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -134,50 +132,8 @@ namespace Avalonia.Controls |
|
|
|
/// </summary>
|
|
|
|
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>
|
|
|
|
@ -193,10 +149,10 @@ namespace Avalonia.Controls |
|
|
|
/// Occurs as the content area is closing.
|
|
|
|
/// </summary>
|
|
|
|
/// <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).
|
|
|
|
/// </remarks>
|
|
|
|
public event EventHandler<RoutedEventArgs>? Collapsing |
|
|
|
public event EventHandler<CancelRoutedEventArgs>? Collapsing |
|
|
|
{ |
|
|
|
add => AddHandler(CollapsingEvent, value); |
|
|
|
remove => RemoveHandler(CollapsingEvent, value); |
|
|
|
@ -215,10 +171,10 @@ namespace Avalonia.Controls |
|
|
|
/// Occurs as the content area is opening.
|
|
|
|
/// </summary>
|
|
|
|
/// <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).
|
|
|
|
/// </remarks>
|
|
|
|
public event EventHandler<RoutedEventArgs>? Expanding |
|
|
|
public event EventHandler<CancelRoutedEventArgs>? Expanding |
|
|
|
{ |
|
|
|
add => AddHandler(ExpandingEvent, value); |
|
|
|
remove => RemoveHandler(ExpandingEvent, value); |
|
|
|
@ -332,5 +288,63 @@ namespace Avalonia.Controls |
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|