1

是否可以将 UWP 绑定CommandBar到类似 a 的东西ObservableCollection

CommandBar我想要实现的是将我的我的我绑定NavigationView到一个特定的对象,Page以便AppBarButton根据当前的动态变化Page

我尝试了什么:

主页.xaml

    <NavigationView.HeaderTemplate>
        <DataTemplate>
            <Grid>
                <CommandBar Grid.Column="1"
                        HorizontalAlignment="Right"
                        VerticalAlignment="Top"
                        DefaultLabelPosition="Right"
                        Background="{ThemeResource SystemControlBackgroundAltHighBrush}"  Content="{Binding Path=Content.AppBarButtonList, ElementName=rootFrame}">
                </CommandBar>
            </Grid>
        </DataTemplate>
    </NavigationView.HeaderTemplate>

SomePage.xaml.cs

    public ObservableCollection<AppBarButton> AppBarButtonList = new ObservableCollection<AppBarButton> {
        new AppBarButton { Icon = new SymbolIcon(Symbol.Accept), Label="Bla" },
        new AppBarButton{Icon=new SymbolIcon(Symbol.Add),Label="Add"}
    };

CommandBar显示什么。

谢谢。

4

2 回答 2

3

我最初的解决方案是使用该PrimaryCommands属性来绑定命令,但事实证明该属性是只读的。

我对这个问题的解决方案是使用行为。

首先添加对Microsoft.Xaml.Behaviors.Uwp.Managed来自 NuGet 的引用。

然后将以下行为添加到您的项目中:

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();
    }


    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;
        }
    }
}

这种行为本质上创建了一个PrimaryCommands可绑定的假属性,并且还观察集合更改事件。每当发生更改时,都会重新构建命令。

最后,您的代码中的问题是您AppBarButtonList只是一个字段,而不是一个属性。像这样改变它:

public ObservableCollection<AppBarButton> AppBarButtonList { get; } = new ObservableCollection<AppBarButton> {
    new AppBarButton { Icon = new SymbolIcon(Symbol.Accept), Label="Bla" },
    new AppBarButton{Icon=new SymbolIcon(Symbol.Add),Label="Add"}
};

请注意在{get ;}赋值运算符之前添加的。

现在您可以像这样使用 XAML 中的行为:

<CommandBar>
    <interactivity:Interaction.Behaviors>
        <local:BindableCommandBarBehavior PrimaryCommands="{Binding Path=Content.AppBarButtonList, ElementName=rootFrame}" />
    </interactivity:Interaction.Behaviors>
</CommandBar>

这绝不是一个完美的解决方案,可以改进以允许不同的集合类型绑定等等,但它应该涵盖您的场景。另一种解决方案是实现自定义版本的命令栏,直接在类型上使用新的附加依赖属性,但我使用行为让用户更清楚这是“添加”功能,而不是内置功能.

于 2018-03-22T08:29:55.890 回答
0

我发现这个答案很有帮助。我做了更多的调整,比如使用 DataTemplateSelector 从可绑定数据源中删除 UI 引用,如“AppBarButton”。

public class BindableCommandBarBehavior : Behavior<CommandBar>
{
    public static readonly DependencyProperty PrimaryCommandsProperty = DependencyProperty.Register(
        "PrimaryCommands", typeof(object), typeof(BindableCommandBarBehavior),
        new PropertyMetadata(null, UpdateCommands));

    public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register(
        "ItemTemplateSelector", typeof(DataTemplateSelector), typeof(BindableCommandBarBehavior),
        new PropertyMetadata(null, null));

    public DataTemplateSelector ItemTemplateSelector
    {
        get { return (DataTemplateSelector)GetValue(ItemTemplateSelectorProperty); }
        set { SetValue(ItemTemplateSelectorProperty, value); }
    }

    public object PrimaryCommands
    {
        get { return  GetValue(PrimaryCommandsProperty); }
        set { SetValue(PrimaryCommandsProperty, value); }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (PrimaryCommands is INotifyCollectionChanged notifyCollectionChanged)
        {
            notifyCollectionChanged.CollectionChanged -= PrimaryCommandsCollectionChanged;
        }
    }

    private void UpdatePrimaryCommands()
    {
        if (AssociatedObject == null)
            return;

        if (PrimaryCommands == null)
            return;

        AssociatedObject.PrimaryCommands.Clear();

        if (!(PrimaryCommands is IEnumerable enumerable))
        {
            AssociatedObject.PrimaryCommands.Clear();
            return;
        }


        foreach (var command in enumerable)
        {
            var template = ItemTemplateSelector.SelectTemplate(command, AssociatedObject);

            if (!(template?.LoadContent() is FrameworkElement dependencyObject))
                continue;

            dependencyObject.DataContext = command;

            if (dependencyObject is ICommandBarElement icommandBarElement)
                AssociatedObject.PrimaryCommands.Add(icommandBarElement);
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        UpdatePrimaryCommands();
    }

    private void PrimaryCommandsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        UpdatePrimaryCommands();
    }

    private static void UpdateCommands(DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        if (!(dependencyObject is BindableCommandBarBehavior behavior)) return;
        if (dependencyPropertyChangedEventArgs.OldValue is INotifyCollectionChanged oldList)
        {
            oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged;
        }

        if (dependencyPropertyChangedEventArgs.NewValue is INotifyCollectionChanged newList)
        {
            newList.CollectionChanged += behavior.PrimaryCommandsCollectionChanged;
        }

        behavior.UpdatePrimaryCommands();
    }
}

数据模板选择器:

public class CommandBarMenuItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate CbMenuItemTemplate { get; set; }


    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (item is ContextAction)
        {
            return CbMenuItemTemplate;
        }

        return base.SelectTemplateCore(item, container);
    }
}

用于模板的 Xaml:

 <DataTemplate x:Key="CbMenuItemTemplate">
    <AppBarButton
        Command="{Binding Command}"
        Icon="Add"
        Label="{Binding Text}" />
</DataTemplate>

<viewLogic:CommandBarMenuItemTemplateSelector x:Key="CommandBarMenuItemTemplateSelector" 
                                              CbMenuItemTemplate="{StaticResource CbMenuItemTemplate}" />

用法:

  <CommandBar>
    <interactivity:Interaction.Behaviors>
       <viewLogic:BindableCommandBarBehavior ItemTemplateSelector="{StaticResource CommandBarMenuItemTemplateSelector}" PrimaryCommands="{Binding ContextActions}" />
    </interactivity:Interaction.Behaviors>
  </CommandBar>

其中 ContextActions 是我的类 ContextAction 的 ObservableCollection。

于 2018-06-22T14:16:26.077 回答