diff --git a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml
index 97e649dd4..f36260f91 100644
--- a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml
+++ b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml
@@ -42,6 +42,7 @@
FooterMenuItemsSource="{Binding ViewModel.FooterMenuItems, Mode=OneWay}"
FrameMargin="0"
IsBackButtonVisible="Visible"
+ IsGridSplitterEnabled="True"
IsPaneToggleVisible="True"
MenuItemsSource="{Binding ViewModel.MenuItems, Mode=OneWay}"
OpenPaneLength="310"
diff --git a/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs b/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs
index df33384c5..681d3c92e 100644
--- a/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs
+++ b/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs
@@ -166,6 +166,11 @@ public interface INavigationView
///
Thickness FrameMargin { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the GridSplitter is enabled in the LeftNavigationViewTemplate.
+ ///
+ bool IsGridSplitterEnabled { get; set; }
+
///
/// Occurs when the NavigationView pane is opened.
///
diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Events.cs b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Events.cs
index ef2af06f2..226148db6 100644
--- a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Events.cs
+++ b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Events.cs
@@ -125,6 +125,7 @@ public event TypedEventHandler Navigated
protected virtual void OnPaneOpened()
{
RaiseEvent(new RoutedEventArgs(PaneOpenedEvent, this));
+ UpdatePaneColumnWidthForToggle();
}
///
@@ -133,6 +134,7 @@ protected virtual void OnPaneOpened()
protected virtual void OnPaneClosed()
{
RaiseEvent(new RoutedEventArgs(PaneClosedEvent, this));
+ UpdatePaneColumnWidthForToggle();
}
///
diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs
index 43086aa16..30290cdc9 100644
--- a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs
+++ b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs
@@ -259,6 +259,14 @@ public partial class NavigationView
new FrameworkPropertyMetadata(default(Thickness))
);
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IsGridSplitterEnabledProperty = DependencyProperty.Register(
+ nameof(IsGridSplitterEnabled),
+ typeof(bool),
+ typeof(NavigationView),
+ new FrameworkPropertyMetadata(false)
+ );
+
///
/// Gets or sets a value indicating whether debugging messages for this control are enabled
///
@@ -480,6 +488,13 @@ public Thickness FrameMargin
set => SetValue(FrameMarginProperty, value);
}
+ ///
+ public bool IsGridSplitterEnabled
+ {
+ get => (bool)GetValue(IsGridSplitterEnabledProperty);
+ set => SetValue(IsGridSplitterEnabledProperty, value);
+ }
+
private void OnMenuItemsSource_CollectionChanged(
object? sender,
IList collection,
diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationView.TemplateParts.cs b/src/Wpf.Ui/Controls/NavigationView/NavigationView.TemplateParts.cs
index b0aa2436f..e22a5f934 100644
--- a/src/Wpf.Ui/Controls/NavigationView/NavigationView.TemplateParts.cs
+++ b/src/Wpf.Ui/Controls/NavigationView/NavigationView.TemplateParts.cs
@@ -6,6 +6,10 @@
// Based on Windows UI Library
// Copyright(c) Microsoft Corporation.All rights reserved.
+using System;
+using System.ComponentModel;
+using System.Windows;
+
// ReSharper disable once CheckNamespace
namespace Wpf.Ui.Controls;
@@ -30,6 +34,7 @@ namespace Wpf.Ui.Controls;
Name = TemplateElementAutoSuggestBoxSymbolButton,
Type = typeof(System.Windows.Controls.Button)
)]
+[TemplatePart(Name = TemplateElementPaneColumn, Type = typeof(System.Windows.Controls.ColumnDefinition))]
public partial class NavigationView
{
///
@@ -63,6 +68,11 @@ public partial class NavigationView
///
private const string TemplateElementAutoSuggestBoxSymbolButton = "PART_AutoSuggestBoxSymbolButton";
+ ///
+ /// Template element represented by the PART_PaneColumn name.
+ ///
+ private const string TemplateElementPaneColumn = "PART_PaneColumn";
+
///
/// Gets or sets the control responsible for rendering the content.
///
@@ -93,6 +103,15 @@ public partial class NavigationView
///
protected System.Windows.Controls.Button? AutoSuggestBoxSymbolButton { get; set; }
+ ///
+ /// Gets or sets the pane column definition for GridSplitter support.
+ ///
+ protected System.Windows.Controls.ColumnDefinition? PaneColumn { get; set; }
+
+ private DependencyPropertyDescriptor? _openPaneLengthDescriptor;
+ private DependencyPropertyDescriptor? _isPaneOpenDescriptor;
+ private double _lastOpenPaneWidth = double.NaN;
+
///
public override void OnApplyTemplate()
{
@@ -149,6 +168,170 @@ is System.Windows.Controls.Button autoSuggestBoxSymbolButton
ToggleButton.Click -= OnToggleButtonClick;
ToggleButton.Click += OnToggleButtonClick;
}
+
+ // Clean up previous event handlers
+ if (_openPaneLengthDescriptor != null)
+ {
+ _openPaneLengthDescriptor.RemoveValueChanged(this, OnPanePropertyChanged);
+ _openPaneLengthDescriptor = null;
+ }
+
+ if (_isPaneOpenDescriptor != null)
+ {
+ _isPaneOpenDescriptor.RemoveValueChanged(this, OnPanePropertyChanged);
+ _isPaneOpenDescriptor = null;
+ }
+
+ // Initialize PaneColumn width for GridSplitter support
+ var paneColumn = GetTemplateChild(TemplateElementPaneColumn) as System.Windows.Controls.ColumnDefinition;
+ if (paneColumn != null)
+ {
+ PaneColumn = paneColumn;
+
+ // Set initial width using OpenPaneLength or saved width
+ if (IsPaneOpen && OpenPaneLength > 0)
+ {
+ var initialWidth = !double.IsNaN(_lastOpenPaneWidth) && _lastOpenPaneWidth > 0
+ ? _lastOpenPaneWidth
+ : OpenPaneLength;
+ PaneColumn.Width = new GridLength(initialWidth);
+ }
+ else if (!IsPaneOpen)
+ {
+ PaneColumn.Width = new GridLength(40.0);
+ }
+
+ // Update on next render pass to avoid issues
+ Dispatcher.BeginInvoke(new Action(UpdatePaneColumnWidth), System.Windows.Threading.DispatcherPriority.Loaded);
+
+ // Listen to OpenPaneLength and IsPaneOpen changes
+ _openPaneLengthDescriptor = DependencyPropertyDescriptor.FromProperty(OpenPaneLengthProperty, typeof(NavigationView));
+ if (_openPaneLengthDescriptor != null)
+ {
+ _openPaneLengthDescriptor.AddValueChanged(this, OnPanePropertyChanged);
+ }
+
+ _isPaneOpenDescriptor = DependencyPropertyDescriptor.FromProperty(IsPaneOpenProperty, typeof(NavigationView));
+ if (_isPaneOpenDescriptor != null)
+ {
+ _isPaneOpenDescriptor.AddValueChanged(this, OnPanePropertyChanged);
+ }
+ }
+ else
+ {
+ PaneColumn = null;
+ }
+ }
+
+ private void OnPanePropertyChanged(object? sender, EventArgs e)
+ {
+ if (Dispatcher.CheckAccess())
+ {
+ UpdatePaneColumnWidth();
+ }
+ else
+ {
+ Dispatcher.BeginInvoke(new Action(UpdatePaneColumnWidth), System.Windows.Threading.DispatcherPriority.Normal);
+ }
+ }
+
+ private void UpdatePaneColumnWidth()
+ {
+ if (PaneColumn is null)
+ {
+ return;
+ }
+
+ try
+ {
+ // If pane is closed, don't update the width - maintain closed state
+ // This prevents the pane from automatically opening when window size changes
+ if (!IsPaneOpen)
+ {
+ // Only ensure it's set to 40.0 if it's not already
+ if (!PaneColumn.Width.IsAbsolute || Math.Abs(PaneColumn.Width.Value - 40.0) > 0.1)
+ {
+ PaneColumn.SetCurrentValue(System.Windows.Controls.ColumnDefinition.WidthProperty, new GridLength(40.0));
+ }
+ return;
+ }
+
+ // Only update width when pane is open
+ var currentWidth = PaneColumn.Width.IsAbsolute ? PaneColumn.Width.Value : OpenPaneLength;
+ var targetWidth = !double.IsNaN(_lastOpenPaneWidth) && _lastOpenPaneWidth > 0
+ ? _lastOpenPaneWidth
+ : OpenPaneLength;
+
+ // Don't update if GridSplitter is enabled and user has manually resized
+ // This allows GridSplitter to control the width
+ if (IsGridSplitterEnabled && Math.Abs(currentWidth - targetWidth) > 1.0)
+ {
+ // User has manually resized, save the current width
+ if (PaneColumn.Width.IsAbsolute)
+ {
+ _lastOpenPaneWidth = PaneColumn.Width.Value;
+ }
+ return;
+ }
+
+ // Update to target width
+ if (Math.Abs(currentWidth - targetWidth) > 0.1)
+ {
+ PaneColumn.SetCurrentValue(System.Windows.Controls.ColumnDefinition.WidthProperty, new GridLength(targetWidth));
+ }
+ }
+ catch
+ {
+ // Ignore errors during initialization
+ }
+ }
+
+ ///
+ /// Updates PaneColumn width when pane is toggled open/closed.
+ /// This is called from OnPaneOpened/OnPaneClosed to ensure width updates even if user has resized.
+ ///
+ private void UpdatePaneColumnWidthForToggle()
+ {
+ if (PaneColumn is null)
+ {
+ return;
+ }
+
+ try
+ {
+ if (IsPaneOpen)
+ {
+ // When opening, use the last saved width if available, otherwise use OpenPaneLength
+ var targetWidth = !double.IsNaN(_lastOpenPaneWidth) && _lastOpenPaneWidth > 0
+ ? _lastOpenPaneWidth
+ : OpenPaneLength;
+
+ if (targetWidth > 0)
+ {
+ PaneColumn.SetCurrentValue(System.Windows.Controls.ColumnDefinition.WidthProperty, new GridLength(targetWidth));
+ }
+ }
+ else
+ {
+ // When closing, save the current width if GridSplitter is enabled and pane was resized
+ if (IsGridSplitterEnabled && PaneColumn.Width.IsAbsolute)
+ {
+ var currentWidth = PaneColumn.Width.Value;
+ // Only save if it's different from the default OpenPaneLength (user has resized)
+ if (Math.Abs(currentWidth - OpenPaneLength) > 1.0)
+ {
+ _lastOpenPaneWidth = currentWidth;
+ }
+ }
+
+ // Always set to 40.0 when closing
+ PaneColumn.SetCurrentValue(System.Windows.Controls.ColumnDefinition.WidthProperty, new GridLength(40.0));
+ }
+ }
+ catch
+ {
+ // Ignore errors during initialization
+ }
}
protected T GetTemplateChild(string name)
diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationViewCompact.xaml b/src/Wpf.Ui/Controls/NavigationView/NavigationViewCompact.xaml
index 6ea15a786..408ca4177 100644
--- a/src/Wpf.Ui/Controls/NavigationView/NavigationViewCompact.xaml
+++ b/src/Wpf.Ui/Controls/NavigationView/NavigationViewCompact.xaml
@@ -282,14 +282,18 @@
-
+
+
@@ -436,8 +440,33 @@
-
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+