diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs
index 2ad6a58d38d..668de5bca94 100644
--- a/src/Avalonia.Controls/Expander.cs
+++ b/src/Avalonia.Controls/Expander.cs
@@ -191,7 +191,7 @@ protected virtual void OnCollapsed(RoutedEventArgs eventArgs)
///
/// Invoked just before the event.
///
- protected virtual void OnCollapsing(RoutedEventArgs eventArgs)
+ protected virtual void OnCollapsing(CancelRoutedEventArgs eventArgs)
{
RaiseEvent(eventArgs);
}
@@ -207,7 +207,7 @@ protected virtual void OnExpanded(RoutedEventArgs eventArgs)
///
/// Invoked just before the event.
///
- protected virtual void OnExpanding(RoutedEventArgs eventArgs)
+ protected virtual void OnExpanding(CancelRoutedEventArgs eventArgs)
{
RaiseEvent(eventArgs);
}
diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
similarity index 56%
rename from src/Avalonia.Controls/SplitView.cs
rename to src/Avalonia.Controls/SplitView/SplitView.cs
index 35b135e1521..1099a40f085 100644
--- a/src/Avalonia.Controls/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -1,77 +1,16 @@
-using Avalonia.Controls.Metadata;
+using System;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
+using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
-using Avalonia.VisualTree;
-using System;
-using Avalonia.Reactive;
-using Avalonia.Controls.Presenters;
-using Avalonia.Controls.Templates;
-using Avalonia.LogicalTree;
namespace Avalonia.Controls
{
- ///
- /// Defines constants for how the SplitView Pane should display
- ///
- public enum SplitViewDisplayMode
- {
- ///
- /// Pane is displayed next to content, and does not auto collapse
- /// when tapped outside
- ///
- Inline,
- ///
- /// Pane is displayed next to content. When collapsed, pane is still
- /// visible according to CompactPaneLength. Pane does not auto collapse
- /// when tapped outside
- ///
- CompactInline,
- ///
- /// Pane is displayed above content. Pane collapses when tapped outside
- ///
- Overlay,
- ///
- /// Pane is displayed above content. When collapsed, pane is still
- /// visible according to CompactPaneLength. Pane collapses when tapped outside
- ///
- CompactOverlay
- }
-
- ///
- /// Defines constants for where the Pane should appear
- ///
- public enum SplitViewPanePlacement
- {
- Left,
- Right
- }
-
- public class SplitViewTemplateSettings : AvaloniaObject
- {
- internal SplitViewTemplateSettings() { }
-
- public static readonly StyledProperty ClosedPaneWidthProperty =
- AvaloniaProperty.Register(nameof(ClosedPaneWidth), 0d);
-
- public static readonly StyledProperty PaneColumnGridLengthProperty =
- AvaloniaProperty.Register(nameof(PaneColumnGridLength));
-
- public double ClosedPaneWidth
- {
- get => GetValue(ClosedPaneWidthProperty);
- internal set => SetValue(ClosedPaneWidthProperty, value);
- }
-
- public GridLength PaneColumnGridLength
- {
- get => GetValue(PaneColumnGridLengthProperty);
- internal set => SetValue(PaneColumnGridLengthProperty, value);
- }
- }
-
///
/// A control with two views: A collapsible pane and an area for content
///
@@ -93,26 +32,34 @@ Pseudo classes & combos
/// Defines the property
///
public static readonly StyledProperty CompactPaneLengthProperty =
- AvaloniaProperty.Register(nameof(CompactPaneLength), defaultValue: 48);
+ AvaloniaProperty.Register(
+ nameof(CompactPaneLength),
+ defaultValue: 48);
///
/// Defines the property
///
public static readonly StyledProperty DisplayModeProperty =
- AvaloniaProperty.Register(nameof(DisplayMode), defaultValue: SplitViewDisplayMode.Overlay);
+ AvaloniaProperty.Register(
+ nameof(DisplayMode),
+ defaultValue: SplitViewDisplayMode.Overlay);
///
/// Defines the property
///
- public static readonly DirectProperty IsPaneOpenProperty =
- AvaloniaProperty.RegisterDirect(nameof(IsPaneOpen),
- x => x.IsPaneOpen, (x, v) => x.IsPaneOpen = v);
+ public static readonly StyledProperty IsPaneOpenProperty =
+ AvaloniaProperty.Register(
+ nameof(IsPaneOpen),
+ defaultValue: false,
+ coerce: CoerceIsPaneOpen);
///
/// Defines the property
///
public static readonly StyledProperty OpenPaneLengthProperty =
- AvaloniaProperty.Register(nameof(OpenPaneLength), defaultValue: 320);
+ AvaloniaProperty.Register(
+ nameof(OpenPaneLength),
+ defaultValue: 320);
///
/// Defines the property
@@ -150,7 +97,38 @@ Pseudo classes & combos
public static readonly StyledProperty TemplateSettingsProperty =
AvaloniaProperty.Register(nameof(TemplateSettings));
- private bool _isPaneOpen;
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent PaneClosedEvent =
+ RoutedEvent.Register(
+ nameof(PaneClosed),
+ RoutingStrategies.Bubble);
+
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent PaneClosingEvent =
+ RoutedEvent.Register(
+ nameof(PaneClosing),
+ RoutingStrategies.Bubble);
+
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent PaneOpenedEvent =
+ RoutedEvent.Register(
+ nameof(PaneOpened),
+ RoutingStrategies.Bubble);
+
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent PaneOpeningEvent =
+ RoutedEvent.Register(
+ nameof(PaneOpening),
+ RoutingStrategies.Bubble);
+
private Panel? _pane;
private IDisposable? _pointerDisposable;
@@ -164,12 +142,6 @@ public SplitView()
static SplitView()
{
- UseLightDismissOverlayModeProperty.Changed.AddClassHandler((x, v) => x.OnUseLightDismissChanged(v));
- CompactPaneLengthProperty.Changed.AddClassHandler((x, v) => x.OnCompactPaneLengthChanged(v));
- PanePlacementProperty.Changed.AddClassHandler((x, v) => x.OnPanePlacementChanged(v));
- DisplayModeProperty.Changed.AddClassHandler((x, v) => x.OnDisplayModeChanged(v));
-
- PaneProperty.Changed.AddClassHandler((x, e) => x.PaneChanged(e));
}
///
@@ -196,37 +168,8 @@ public SplitViewDisplayMode DisplayMode
///
public bool IsPaneOpen
{
- get => _isPaneOpen;
- set
- {
- if (value == _isPaneOpen)
- {
- return;
- }
-
- if (value)
- {
- OnPaneOpening(this, EventArgs.Empty);
- SetAndRaise(IsPaneOpenProperty, ref _isPaneOpen, value);
-
- PseudoClasses.Add(":open");
- PseudoClasses.Remove(":closed");
- OnPaneOpened(this, EventArgs.Empty);
- }
- else
- {
- SplitViewPaneClosingEventArgs args = new SplitViewPaneClosingEventArgs(false);
- OnPaneClosing(this, args);
- if (!args.Cancel)
- {
- SetAndRaise(IsPaneOpenProperty, ref _isPaneOpen, value);
-
- PseudoClasses.Add(":closed");
- PseudoClasses.Remove(":open");
- OnPaneClosed(this, EventArgs.Empty);
- }
- }
- }
+ get => GetValue(IsPaneOpenProperty);
+ set => SetValue(IsPaneOpenProperty, value);
}
///
@@ -297,24 +240,48 @@ public SplitViewTemplateSettings TemplateSettings
}
///
- /// Fired when the pane is closed
+ /// Fired when the pane is closed.
///
- public event EventHandler? PaneClosed;
+ public event EventHandler? PaneClosed
+ {
+ add => AddHandler(PaneClosedEvent, value);
+ remove => RemoveHandler(PaneClosedEvent, value);
+ }
///
- /// Fired when the pane is closing
+ /// Fired when the pane is closing.
///
- public event EventHandler? PaneClosing;
+ ///
+ /// The event args property may be set to true to cancel the event
+ /// and keep the pane open.
+ ///
+ public event EventHandler? PaneClosing
+ {
+ add => AddHandler(PaneClosingEvent, value);
+ remove => RemoveHandler(PaneClosingEvent, value);
+ }
///
- /// Fired when the pane is opened
+ /// Fired when the pane is opened.
///
- public event EventHandler? PaneOpened;
+ public event EventHandler? PaneOpened
+ {
+ add => AddHandler(PaneOpenedEvent, value);
+ remove => RemoveHandler(PaneOpenedEvent, value);
+ }
///
- /// Fired when the pane is opening
+ /// Fired when the pane is opening.
///
- public event EventHandler? PaneOpening;
+ ///
+ /// The event args property may be set to true to cancel the event
+ /// and keep the pane closed.
+ ///
+ public event EventHandler? PaneOpening
+ {
+ add => AddHandler(PaneOpeningEvent, value);
+ remove => RemoveHandler(PaneOpeningEvent, value);
+ }
protected override bool RegisterContentPresenter(IContentPresenter presenter)
{
@@ -351,6 +318,89 @@ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e
_pointerDisposable?.Dispose();
}
+ ///
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == CompactPaneLengthProperty)
+ {
+ var newLen = change.GetNewValue();
+ var displayMode = DisplayMode;
+ if (displayMode == SplitViewDisplayMode.CompactInline)
+ {
+ TemplateSettings.ClosedPaneWidth = newLen;
+ }
+ else if (displayMode == SplitViewDisplayMode.CompactOverlay)
+ {
+ TemplateSettings.ClosedPaneWidth = newLen;
+ TemplateSettings.PaneColumnGridLength = new GridLength(newLen, GridUnitType.Pixel);
+ }
+ }
+ else if (change.Property == DisplayModeProperty)
+ {
+ var oldState = GetPseudoClass(change.GetOldValue());
+ var newState = GetPseudoClass(change.GetNewValue());
+
+ PseudoClasses.Remove($":{oldState}");
+ PseudoClasses.Add($":{newState}");
+
+ var (closedPaneWidth, paneColumnGridLength) = change.GetNewValue() switch
+ {
+ SplitViewDisplayMode.Overlay => (0, new GridLength(0, GridUnitType.Pixel)),
+ SplitViewDisplayMode.CompactOverlay => (CompactPaneLength, new GridLength(CompactPaneLength, GridUnitType.Pixel)),
+ SplitViewDisplayMode.Inline => (0, new GridLength(0, GridUnitType.Auto)),
+ SplitViewDisplayMode.CompactInline => (CompactPaneLength, new GridLength(0, GridUnitType.Auto)),
+ _ => throw new NotImplementedException(),
+ };
+ TemplateSettings.ClosedPaneWidth = closedPaneWidth;
+ TemplateSettings.PaneColumnGridLength = paneColumnGridLength;
+ }
+ else if (change.Property == IsPaneOpenProperty)
+ {
+ bool isPaneOpen = change.GetNewValue();
+
+ if (isPaneOpen)
+ {
+ PseudoClasses.Add(":open");
+ PseudoClasses.Remove(":closed");
+
+ OnPaneOpened(new RoutedEventArgs(PaneOpenedEvent, this));
+ }
+ else
+ {
+ PseudoClasses.Add(":closed");
+ PseudoClasses.Remove(":open");
+
+ OnPaneClosed(new RoutedEventArgs(PaneClosedEvent, this));
+ }
+ }
+ else if (change.Property == PaneProperty)
+ {
+ if (change.OldValue is ILogical oldChild)
+ {
+ LogicalChildren.Remove(oldChild);
+ }
+
+ if (change.NewValue is ILogical newChild)
+ {
+ LogicalChildren.Add(newChild);
+ }
+ }
+ else if (change.Property == PanePlacementProperty)
+ {
+ var oldState = GetPseudoClass(change.GetOldValue());
+ var newState = GetPseudoClass(change.GetNewValue());
+ PseudoClasses.Remove($":{oldState}");
+ PseudoClasses.Add($":{newState}");
+ }
+ else if (change.Property == UseLightDismissOverlayModeProperty)
+ {
+ var mode = change.GetNewValue();
+ PseudoClasses.Set(":lightdismiss", mode);
+ }
+ }
+
private void PointerPressedOutside(object? sender, PointerPressedEventArgs e)
{
if (!IsPaneOpen)
@@ -384,7 +434,7 @@ private void PointerPressedOutside(object? sender, PointerPressedEventArgs e)
}
if (closePane)
{
- IsPaneOpen = false;
+ SetCurrentValue(IsPaneOpenProperty, false);
e.Handled = true;
}
}
@@ -394,41 +444,29 @@ private bool ShouldClosePane()
return (DisplayMode == SplitViewDisplayMode.CompactOverlay || DisplayMode == SplitViewDisplayMode.Overlay);
}
- protected virtual void OnPaneOpening(SplitView sender, EventArgs args)
+ protected virtual void OnPaneOpening(CancelRoutedEventArgs args)
{
- PaneOpening?.Invoke(sender, args);
+ RaiseEvent(args);
}
- protected virtual void OnPaneOpened(SplitView sender, EventArgs args)
+ protected virtual void OnPaneOpened(RoutedEventArgs args)
{
- PaneOpened?.Invoke(sender, args);
+ RaiseEvent(args);
}
- protected virtual void OnPaneClosing(SplitView sender, SplitViewPaneClosingEventArgs args)
+ protected virtual void OnPaneClosing(CancelRoutedEventArgs args)
{
- PaneClosing?.Invoke(sender, args);
+ RaiseEvent(args);
}
- protected virtual void OnPaneClosed(SplitView sender, EventArgs args)
+ protected virtual void OnPaneClosed(RoutedEventArgs args)
{
- PaneClosed?.Invoke(sender, args);
- }
-
- private void OnCompactPaneLengthChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var newLen = (double)e.NewValue!;
- var displayMode = DisplayMode;
- if (displayMode == SplitViewDisplayMode.CompactInline)
- {
- TemplateSettings.ClosedPaneWidth = newLen;
- }
- else if (displayMode == SplitViewDisplayMode.CompactOverlay)
- {
- TemplateSettings.ClosedPaneWidth = newLen;
- TemplateSettings.PaneColumnGridLength = new GridLength(newLen, GridUnitType.Pixel);
- }
+ RaiseEvent(args);
}
+ ///
+ /// Gets the appropriate PseudoClass for the given .
+ ///
private static string GetPseudoClass(SplitViewDisplayMode mode)
{
return mode switch
@@ -441,6 +479,9 @@ private static string GetPseudoClass(SplitViewDisplayMode mode)
};
}
+ ///
+ /// Gets the appropriate PseudoClass for the given .
+ ///
private static string GetPseudoClass(SplitViewPanePlacement placement)
{
return placement switch
@@ -451,51 +492,47 @@ private static string GetPseudoClass(SplitViewPanePlacement placement)
};
}
- private void OnPanePlacementChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var oldState = GetPseudoClass(e.GetOldValue());
- var newState = GetPseudoClass(e.GetNewValue());
- PseudoClasses.Remove($":{oldState}");
- PseudoClasses.Add($":{newState}");
- }
-
- private void OnDisplayModeChanged(AvaloniaPropertyChangedEventArgs e)
+ ///
+ /// Called when the property has to be coerced.
+ ///
+ /// The value to coerce.
+ protected virtual bool OnCoerceIsPaneOpen(bool value)
{
- var oldState = GetPseudoClass(e.GetOldValue());
- var newState = GetPseudoClass(e.GetNewValue());
+ CancelRoutedEventArgs eventArgs;
- PseudoClasses.Remove($":{oldState}");
- PseudoClasses.Add($":{newState}");
+ if (value)
+ {
+ eventArgs = new CancelRoutedEventArgs(PaneOpeningEvent, this);
+ OnPaneOpening(eventArgs);
+ }
+ else
+ {
+ eventArgs = new CancelRoutedEventArgs(PaneClosingEvent, this);
+ OnPaneClosing(eventArgs);
+ }
- var (closedPaneWidth, paneColumnGridLength) = e.GetNewValue() switch
+ if (eventArgs.Cancel)
{
- SplitViewDisplayMode.Overlay => (0, new GridLength(0, GridUnitType.Pixel)),
- SplitViewDisplayMode.CompactOverlay => (CompactPaneLength, new GridLength(CompactPaneLength, GridUnitType.Pixel)),
- SplitViewDisplayMode.Inline => (0, new GridLength(0, GridUnitType.Auto)),
- SplitViewDisplayMode.CompactInline => (CompactPaneLength, new GridLength(0, GridUnitType.Auto)),
- _ => throw new NotImplementedException(),
- };
- TemplateSettings.ClosedPaneWidth = closedPaneWidth;
- TemplateSettings.PaneColumnGridLength = paneColumnGridLength;
- }
+ return !value;
+ }
- private void OnUseLightDismissChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var mode = (bool)e.NewValue!;
- PseudoClasses.Set(":lightdismiss", mode);
+ return value;
}
- private void PaneChanged(AvaloniaPropertyChangedEventArgs e)
+ ///
+ /// Coerces/validates the property value.
+ ///
+ /// The instance.
+ /// The value to coerce.
+ /// The coerced/validated value.
+ private static bool CoerceIsPaneOpen(AvaloniaObject instance, bool value)
{
- if (e.OldValue is ILogical oldChild)
+ if (instance is SplitView splitView)
{
- LogicalChildren.Remove(oldChild);
+ return splitView.OnCoerceIsPaneOpen(value);
}
- if (e.NewValue is ILogical newChild)
- {
- LogicalChildren.Add(newChild);
- }
+ return value;
}
}
}
diff --git a/src/Avalonia.Controls/SplitView/SplitViewDisplayMode.cs b/src/Avalonia.Controls/SplitView/SplitViewDisplayMode.cs
new file mode 100644
index 00000000000..6333f96f863
--- /dev/null
+++ b/src/Avalonia.Controls/SplitView/SplitViewDisplayMode.cs
@@ -0,0 +1,29 @@
+namespace Avalonia.Controls
+{
+ ///
+ /// Defines constants for how the SplitView Pane should display
+ ///
+ public enum SplitViewDisplayMode
+ {
+ ///
+ /// Pane is displayed next to content, and does not auto collapse
+ /// when tapped outside
+ ///
+ Inline,
+ ///
+ /// Pane is displayed next to content. When collapsed, pane is still
+ /// visible according to CompactPaneLength. Pane does not auto collapse
+ /// when tapped outside
+ ///
+ CompactInline,
+ ///
+ /// Pane is displayed above content. Pane collapses when tapped outside
+ ///
+ Overlay,
+ ///
+ /// Pane is displayed above content. When collapsed, pane is still
+ /// visible according to CompactPaneLength. Pane collapses when tapped outside
+ ///
+ CompactOverlay
+ }
+}
diff --git a/src/Avalonia.Controls/SplitView/SplitViewPanePlacement.cs b/src/Avalonia.Controls/SplitView/SplitViewPanePlacement.cs
new file mode 100644
index 00000000000..62c53871921
--- /dev/null
+++ b/src/Avalonia.Controls/SplitView/SplitViewPanePlacement.cs
@@ -0,0 +1,18 @@
+namespace Avalonia.Controls
+{
+ ///
+ /// Defines constants for where the Pane should appear
+ ///
+ public enum SplitViewPanePlacement
+ {
+ ///
+ /// The pane is shown to the left of content.
+ ///
+ Left,
+
+ ///
+ /// The pane is shown to the right of content.
+ ///
+ Right
+ }
+}
diff --git a/src/Avalonia.Controls/SplitView/SplitViewTemplateSettings.cs b/src/Avalonia.Controls/SplitView/SplitViewTemplateSettings.cs
new file mode 100644
index 00000000000..f2cbf559860
--- /dev/null
+++ b/src/Avalonia.Controls/SplitView/SplitViewTemplateSettings.cs
@@ -0,0 +1,32 @@
+namespace Avalonia.Controls.Primitives
+{
+ ///
+ /// Provides calculated values for use with the 's control theme or template.
+ /// This class is NOT intended for general use.
+ ///
+ public class SplitViewTemplateSettings : AvaloniaObject
+ {
+ internal SplitViewTemplateSettings() { }
+
+ public static readonly StyledProperty ClosedPaneWidthProperty =
+ AvaloniaProperty.Register(nameof(ClosedPaneWidth),
+ 0d);
+
+ public static readonly StyledProperty PaneColumnGridLengthProperty =
+ AvaloniaProperty.Register(
+ nameof(PaneColumnGridLength));
+
+ public double ClosedPaneWidth
+ {
+ get => GetValue(ClosedPaneWidthProperty);
+ internal set => SetValue(ClosedPaneWidthProperty, value);
+ }
+
+ public GridLength PaneColumnGridLength
+ {
+ get => GetValue(PaneColumnGridLengthProperty);
+ internal set => SetValue(PaneColumnGridLengthProperty, value);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/SplitViewPaneClosingEventArgs.cs b/src/Avalonia.Controls/SplitViewPaneClosingEventArgs.cs
deleted file mode 100644
index 46fb2d161b3..00000000000
--- a/src/Avalonia.Controls/SplitViewPaneClosingEventArgs.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace Avalonia.Controls
-{
- public class SplitViewPaneClosingEventArgs : EventArgs
- {
- public bool Cancel { get; set; }
-
- public SplitViewPaneClosingEventArgs(bool cancel)
- {
- Cancel = cancel;
- }
- }
-}