【问题标题】:Why doesn't the Windows 8.1 MenuFlyout have ItemsSource property?为什么 Windows 8.1 MenuFlyout 没有 ItemsSource 属性?
【发布时间】:2016-02-12 03:36:40
【问题描述】:

我正在阅读this article 并且不禁想知道同样的事情。

有没有办法对菜单Flyout控件进行数据绑定?

【问题讨论】:

标签: windows-8 winrt-xaml


【解决方案1】:

是的。

我为需要此功能的开发人员提供了一个简单的解决方案。它使用附加属性来标识 Flyout 控件的 ItemsSource 和 ItemTemplate。如果开发人员选择使用MenuFlyoutItem 或其他方式,则取决于他们。

这是附加的属性:

public class BindableFlyout : DependencyObject
{
    #region ItemsSource

    public static IEnumerable GetItemsSource(DependencyObject obj)
    {
        return obj.GetValue(ItemsSourceProperty) as IEnumerable;
    }
    public static void SetItemsSource(DependencyObject obj, IEnumerable value)
    {
        obj.SetValue(ItemsSourceProperty, value);
    }
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.RegisterAttached("ItemsSource", typeof(IEnumerable),
        typeof(BindableFlyout), new PropertyMetadata(null, ItemsSourceChanged));
    private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    { Setup(d as Windows.UI.Xaml.Controls.Flyout); }

    #endregion

    #region ItemTemplate

    public static DataTemplate GetItemTemplate(DependencyObject obj)
    {
        return (DataTemplate)obj.GetValue(ItemTemplateProperty);
    }
    public static void SetItemTemplate(DependencyObject obj, DataTemplate value)
    {
        obj.SetValue(ItemTemplateProperty, value);
    }
    public static readonly DependencyProperty ItemTemplateProperty =
        DependencyProperty.RegisterAttached("ItemTemplate", typeof(DataTemplate),
        typeof(BindableFlyout), new PropertyMetadata(null, ItemsTemplateChanged));
    private static void ItemsTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    { Setup(d as Windows.UI.Xaml.Controls.Flyout); }

    #endregion

    private static async void Setup(Windows.UI.Xaml.Controls.Flyout m)
    {
        if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
            return;
        var s = GetItemsSource(m);
        if (s == null)
            return;
        var t = GetItemTemplate(m);
        if (t == null)
            return;
        var c = new Windows.UI.Xaml.Controls.ItemsControl
        {
            ItemsSource = s,
            ItemTemplate = t,
        };
        var n = Windows.UI.Core.CoreDispatcherPriority.Normal;
        Windows.UI.Core.DispatchedHandler h = () => m.Content = c;
        await m.Dispatcher.RunAsync(n, h);
    }
}

还有,这里是示例用法。

<Page.BottomAppBar>
    <CommandBar>
        <AppBarButton Label="AppBarButton">
            <AppBarButton.Flyout>
                <Flyout local:BindableFlyout.ItemsSource="{Binding MenuItems}">
                    <local:BindableFlyout.ItemTemplate>
                        <DataTemplate>
                            <MenuFlyoutItem Text="{Binding Text}" />
                        </DataTemplate>
                    </local:BindableFlyout.ItemTemplate>
                </Flyout>
            </AppBarButton.Flyout>
            <AppBarButton.Icon>
                <SymbolIcon/>
            </AppBarButton.Icon>
        </AppBarButton>
    </CommandBar>
</Page.BottomAppBar>

我将维护此代码here。

看起来像这样:

希望对你有帮助。

祝你好运!

【讨论】:

  • 我正在使用您的示例代码,但我发现单击项目后菜单不再自行关闭。有什么想法吗?
  • 哦,我想这可能是因为这是一个 Flyout 而不是 MenuFlyout,我会看看我是否可以从您的博客中解决一些问题。谢谢
  • 使用 ChangePropertyAction 混合行为,您只需在单击 MenuItem 时将 Flyout.IsOpen 属性设置为 false。说得通?当然,您也可以在代码隐藏中执行此操作。无论哪种方式都有效。如果你想在那里做的话,你也可以在你的 ViewModel 中有一个 MenuOpen 属性。很多选择。
  • ChangePropertyAction 不起作用,因为它实际上是一个名为 Hide 的方法。 CallMethodAction 不起作用,因为您无法在 BindableFlyout 的 DataTemplate 中进行 ElementName 绑定,因此您无法调用父 Flyout 的 Hide 方法。最后我选择了使用代码隐藏和视图模型中的事件。
  • 我怎样才能让它与SelectedItem一起工作?这意味着 MenuItem 会根据我在此可绑定 Flyout 中选择的内容触发调用控件来更改?
【解决方案2】:

即使最初的问题是很久以前提出的,我也会发布我找到的解决方案,因为其他人可能会觉得它有用。

Jerry 的解决方案有一个严重的缺陷:当您单击一个项目时,MenuFlyout 没有关闭,我发现这样做非常困难,因为似乎(几乎?)不可能获得对 Flyout 的引用从 DataTemplate 内部关闭它。

我想出了这个子类 MenuFlyout 的解决方案:

public class BindableFlyout : MenuFlyout
{
    public ICollection<ContextMenuCommand> ItemsSource
    {
        get { return (ICollection<ContextMenuCommand>)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(ICollection<ContextMenuCommand>), typeof(BindableFlyout), new PropertyMetadata(null, (DependencyObject o, DependencyPropertyChangedEventArgs args) =>
        {
            Setup(o as BindableFlyout);
        }
    ));

    private static async void Setup(BindableFlyout menuFlyout)
    {
        if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
            return;
        if (menuFlyout.ItemsSource == null)
            return;

        await menuFlyout.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            menuFlyout.Items.Clear();
            foreach (var menuItem in menuFlyout.ItemsSource)
            {
                menuFlyout.Items.Add(new MenuFlyoutItem()
                {
                    Text = menuItem.Text,
                    Command = menuItem.Command
                });
            }
        });
    }
}

public class ContextMenuCommand
{
    public ContextMenuCommand(ICommand command, string text)
    {
        Command = command;
        Text = text;
    }

    public string Text
    {
        get; private set;
    }

    public ICommand Command
    {
        get; private set;
    }
}

上面的sn-p没有监听ItemsSource的变化,但是你可以很方便的适配这个类。

【讨论】:

    【解决方案3】:

    它对我有用。我希望我没有错过任何事情。

     class CustomCommand : ICommand
        {
            public ICommand CommandObject { get { return this; } }
            public String CommandName { get; private set; }
    
            public CustomCommand(String name):base()
            {
                this.CommandName = name;
            }
        }
    
        class EncapsulateOrDecoratorObjectForContextMenu
        {
            private object baseObject;
            // chaned properties to the baseObject
    
            public List<CustomCommand> AvailableCommands { get; set; }
    
            public EncapsulateOrDecoratorObjectForContextMenu(object baseObject, List<CustomCommand> commands)
            {
                this.baseObject = baseObject;
                this.AvailableCommands = commands;
            }
    
        }
    
        class SomePage: Page
        {
            private MenuFlyout mFlyout;
    
            public SomePage()
            {
                // I don't know why, but it's to be here... unless UI/design go crazy
                this.mFlyout = new MenuFlyout();
            }
    
            private void Grid_Holding(object sender, HoldingRoutedEventArgs e)
            {
                if (e.OriginalSource is FrameworkElement && (e.OriginalSource as FrameworkElement).DataContext is EncapsulateOrDecoratorObjectForContextMenu)
                {
                    // Only the property is 'readonly', not the List<menuItem> itself, so...
                    this.mFlyout.Items.Clear();
    
                    MenuFlyoutItem menuItem;
                    foreach (CustomCommand command in ((e.OriginalSource as FrameworkElement).DataContext as EncapsulateOrDecoratorObjectForContextMenu).AvailableCommands)
                    {
                        menuItem = new MenuFlyoutItem();
                        menuItem.Text = command.CommandName;
                        menuItem.Command = command.CommandObject;
    
                        this.mFlyout.Items.Add(menuItem);
                    }
    
                    FrameworkElement senderElement = sender as FrameworkElement;
                    this.mFlyout.ShowAt(senderElement);
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-28
      • 2012-12-11
      • 2017-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多