NavigationControl - Part 3
-
So I am continuing to work on my Navigation Control[^]. See this[^] and this[^]. The code is in this repo[^]. Special thanks to Richard Deeming for all your help so far. I've learned alot from you. If you look at the image of the control in the first link above, you 'll see 3 sections, with the 3rd still loading. The expanders can be openend either by the user, or by saving & restoring a bool. On the NavigationPane control there's a DP called IsPaneExpanded. This get called with from the user or by the restoring during load. When the control is being initialized I store the value of the DP. Fields
// Stores the value of the DP IsPaneExpanded during initialization. See
// comments in the DP IsPaneExpanded
private bool _isPaneExpanded = false;// Set to true after the control has finished initializing
private bool _isInitialized = false;IsPaneExpanded DP
public static readonly DependencyProperty IsPaneExpandedProperty =
DependencyProperty.Register("IsPaneExpanded",
typeof(bool),
typeof(NavigationPane),
new PropertyMetadata(false, new PropertyChangedCallback(OnIsPaneExpandedChanged)));public bool IsPaneExpanded
{
get { return (bool)GetValue(IsPaneExpandedProperty); }
set { SetValue(IsPaneExpandedProperty, value); }
}private static async void OnIsPaneExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (NavigationPane)d;if (!control.\_isInitialized) { // If here, the control is still being initialized. Store the // IsPaneExpanded setting for later use when the control's init // has finished control.\_isPaneExpanded = control.IsPaneExpanded; // Since the control is not loaded, there is nothing to show. But
-
So I am continuing to work on my Navigation Control[^]. See this[^] and this[^]. The code is in this repo[^]. Special thanks to Richard Deeming for all your help so far. I've learned alot from you. If you look at the image of the control in the first link above, you 'll see 3 sections, with the 3rd still loading. The expanders can be openend either by the user, or by saving & restoring a bool. On the NavigationPane control there's a DP called IsPaneExpanded. This get called with from the user or by the restoring during load. When the control is being initialized I store the value of the DP. Fields
// Stores the value of the DP IsPaneExpanded during initialization. See
// comments in the DP IsPaneExpanded
private bool _isPaneExpanded = false;// Set to true after the control has finished initializing
private bool _isInitialized = false;IsPaneExpanded DP
public static readonly DependencyProperty IsPaneExpandedProperty =
DependencyProperty.Register("IsPaneExpanded",
typeof(bool),
typeof(NavigationPane),
new PropertyMetadata(false, new PropertyChangedCallback(OnIsPaneExpandedChanged)));public bool IsPaneExpanded
{
get { return (bool)GetValue(IsPaneExpandedProperty); }
set { SetValue(IsPaneExpandedProperty, value); }
}private static async void OnIsPaneExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (NavigationPane)d;if (!control.\_isInitialized) { // If here, the control is still being initialized. Store the // IsPaneExpanded setting for later use when the control's init // has finished control.\_isPaneExpanded = control.IsPaneExpanded; // Since the control is not loaded, there is nothing to show. But
Problem 1: The change event for the
IsPaneExpanded
property sets theIsPaneExpanded
property tofalse
if_isInitialized
is false. If the property has just changed totrue
, that will cause the change event to fire again. As a result, the_isPaneExpanded
field will be set totrue
, then immediately set back tofalse
. Thankfully, WPF won't fire the event if the property is set to the same value; otherwise, you'd end up with infinite recursion. It would probably be better to use a CoerceValueCallback[^] instead:public static readonly DependencyProperty IsPaneExpandedProperty =
DependencyProperty.Register("IsPaneExpanded",
typeof(bool),
typeof(NavigationPane),
new PropertyMetadata(
defaultValue: false,
propertyChangedCallback: new PropertyChangedCallback(OnIsPaneExpandedChanged),
coerceValueCallback: new CoerceValueCallback(CoerceIsPaneExpanded)
));private static void OnIsPaneExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (NavigationPane)d;
control._isPaneExpanded = (bool)e.NewValue;
control.CoerceValue(IsPaneExpandedProperty);if (control.\_isInitialized && control.\_isPaneExpanded) { \_ = control.Load(); }
}
private static object CoerceIsPaneExpanded(DependencyObject d, object baseValue)
{
var control = (NavigationPane)d;
return control._isInitialized ? baseValue : (object)false;
}Problem 2: You need to change the
_isInitialized
field, trigger the change event for theIsPaneExpanded
property, then load the data.private void NavigationPane_Initialized(object? sender, EventArgs e)
{
_isInitialized = true;
CoerceValue(IsPaneExpandedProperty);if (\_isPaneExpanded) { \_ = Load(); }
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - H
-
Problem 1: The change event for the
IsPaneExpanded
property sets theIsPaneExpanded
property tofalse
if_isInitialized
is false. If the property has just changed totrue
, that will cause the change event to fire again. As a result, the_isPaneExpanded
field will be set totrue
, then immediately set back tofalse
. Thankfully, WPF won't fire the event if the property is set to the same value; otherwise, you'd end up with infinite recursion. It would probably be better to use a CoerceValueCallback[^] instead:public static readonly DependencyProperty IsPaneExpandedProperty =
DependencyProperty.Register("IsPaneExpanded",
typeof(bool),
typeof(NavigationPane),
new PropertyMetadata(
defaultValue: false,
propertyChangedCallback: new PropertyChangedCallback(OnIsPaneExpandedChanged),
coerceValueCallback: new CoerceValueCallback(CoerceIsPaneExpanded)
));private static void OnIsPaneExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (NavigationPane)d;
control._isPaneExpanded = (bool)e.NewValue;
control.CoerceValue(IsPaneExpandedProperty);if (control.\_isInitialized && control.\_isPaneExpanded) { \_ = control.Load(); }
}
private static object CoerceIsPaneExpanded(DependencyObject d, object baseValue)
{
var control = (NavigationPane)d;
return control._isInitialized ? baseValue : (object)false;
}Problem 2: You need to change the
_isInitialized
field, trigger the change event for theIsPaneExpanded
property, then load the data.private void NavigationPane_Initialized(object? sender, EventArgs e)
{
_isInitialized = true;
CoerceValue(IsPaneExpandedProperty);if (\_isPaneExpanded) { \_ = Load(); }
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - H
Thanks, I understand the change you recommened, but it doesn't seem to change anything. I have two different issues. I beleive I solved the first one. 1. The Projects pane should be expanded on startup BEFORE it loads so the user seees the wait indicator. This bit of code in the MainWindow simulates storing and resetting it from settings later on. if there was an ExpandedExpanded (past-tense) then I could call Load() from there. Main Window.cs
NavigationPaneInfos = new List
{
new NavigationPaneModel
{
Header = "Projects",
NavigationItemType = NavigationItemType.Project,
DataSource = Functional.Apply(Repository.GetNavigationItems, NavigationItemType.Project),
IsExpanded = true // <====== 1. This should force the Projects pane to expand on startup, and that should trigger Load() after the wait indicator is visible
},new NavigationPaneModel { Header = "Inventory", NavigationItemType = NavigationItemType.Inventory, DataSource = Functional.Apply(Repository.GetNavigationItems, NavigationItemType.Inventory), }, new NavigationPaneModel { Header = "Companies" , NavigationItemType = NavigationItemType.Company, DataSource = Functional.Apply(Repository.GetNavigationItems, NavigationItemType.Company), }, new NavigationPaneModel { Header = "Employees", NavigationItemType = NavigationItemType.Employee, DataSource = Functional.Apply(Repository.GetNavigationItems, NavigationItemType.Employee), }
};
so, that caused this
private static object CoerceIsPaneExpanded(DependencyObject d, object baseValue)
{
var control = (NavigationPane)d;
var newVal = control._isInitialized ? baseValue : (object)false;
return newVal; //<======= THIS IS NEVER TRUE
}and
private void NavigationPane\_Initialized(object? sender, EventArgs e) { \_isInitialized = true; CoerceValue(IsPaneExpandedProperty); if (\_isPaneExpanded) //<====== 2. THIS IS NEVER TRUE { \_ = Load(); } }
so, I added this, which correctly sets _isPaneExpanded based off the value set in the main window
private static async void OnNavigationPaneModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (NavigationPane)d;
var model = e.NewValue as N