I want to bind the CommandBar on NavigationView Header to a collection to dynamically display the AppBarButtons and I used the method given in this answer. However, when I set a behavior on a CommandBar in DataTemplate and run the program, it throw an exception showing that AssociatedObject is null. Here is the code:
xaml:
<i:Interaction.Behaviors>
<behaviors:NavigationViewHeaderBehavior
DefaultHeader="{x:Bind ViewModel.Selected.Content, Mode=OneWay}">
<behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>
<DataTemplate>
<Grid>
<CommandBar Name="headerCommandBar">
<i:Interaction.Behaviors>
<behaviors:BindableCommandBarBehavior
PrimaryCommands="{Binding Path=Content.ViewModel.AppBarButtonList,ElementName=shellFrame, Mode=OneWay}" />
</i:Interaction.Behaviors>
</CommandBar>
</Grid>
</DataTemplate>
</behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>
</behaviors:NavigationViewHeaderBehavior>
</i:Interaction.Behaviors>
C# code in the ViewModel:
public ObservableCollection<AppBarButton> AppBarButtonList { get; set; }
= new ObservableCollection<AppBarButton>
{
new AppBarButton { Icon = new SymbolIcon(Symbol.Accept), Label="Accept" },
new AppBarButton { Icon = new SymbolIcon(Symbol.Add), Label="Add" }
};
The code of behavior:
public class BindableCommandBarBehavior : Behavior<CommandBar>
{
public ObservableCollection<AppBarButton> PrimaryCommands
{
get { return (ObservableCollection<AppBarButton>)GetValue(PrimaryCommandsProperty); }
set { SetValue(PrimaryCommandsProperty, value); }
}
public static readonly DependencyProperty PrimaryCommandsProperty
= DependencyProperty.Register(
"PrimaryCommands",
typeof(ObservableCollection<AppBarButton>),
typeof(BindableCommandBarBehavior),
new PropertyMetadata(default(ObservableCollection<AppBarButton>), UpdateCommands));
private static void UpdateCommands(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (!(dependencyObject is BindableCommandBarBehavior behavior)) return;
var oldList = dependencyPropertyChangedEventArgs.OldValue as ObservableCollection<AppBarButton>;
if (dependencyPropertyChangedEventArgs.OldValue != null)
{
oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged;
}
var newList = dependencyPropertyChangedEventArgs.NewValue as ObservableCollection<AppBarButton>;
if (dependencyPropertyChangedEventArgs.NewValue != null)
{
newList.CollectionChanged += behavior.PrimaryCommandsCollectionChanged;
}
behavior.UpdatePrimaryCommands();
}
private void PrimaryCommandsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
UpdatePrimaryCommands();
}
/// <summary>
/// Update the primary commands for CommandBar.
/// </summary>
private void UpdatePrimaryCommands()
{
if (PrimaryCommands != null)
{
AssociatedObject.PrimaryCommands.Clear();
foreach (var command in PrimaryCommands)
{
AssociatedObject.PrimaryCommands.Add(command);
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
if (PrimaryCommands != null)
{
PrimaryCommands.CollectionChanged -= PrimaryCommandsCollectionChanged;
}
}
}
The exception is thrown in UpdatePrimaryCommands method. And I'm wondering why it happens.
Your commands are updated before AssociatedObject
is actually attached. Add test for AssociatedObject != null
in UpdateCommands
and create method OnAttached
that will call UpdateCommands
again:
/// <summary>
/// Update the primary commands for CommandBar.
/// </summary>
private void UpdatePrimaryCommands()
{
if (PrimaryCommands != null && AssociatedObject != null)
{
AssociatedObject.PrimaryCommands.Clear();
foreach (var command in PrimaryCommands)
{
AssociatedObject.PrimaryCommands.Add(command);
}
}
}
protected override void OnAttached()
{
UpdatePrimaryCommands();
}
That solves the problem, thanks for your reply ^_^