Browse Source

Switch Expander.IsExpanded to a StyledProperty

pull/9979/head
robloo 4 years ago
parent
commit
bc9f753749
  1. 114
      src/Avalonia.Controls/Expander.cs

114
src/Avalonia.Controls/Expander.cs

@ -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.
@ -99,7 +98,6 @@ namespace Avalonia.Controls
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>
@ -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)
{
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 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;
}
}
}

Loading…
Cancel
Save