Windows 8 为什么';Windows 8.1菜单是否没有ItemsSource属性?

Windows 8 为什么';Windows 8.1菜单是否没有ItemsSource属性?,windows-8,winrt-xaml,Windows 8,Winrt Xaml,我在看书,不禁想知道同样的事情 是否有方法将菜单弹出按钮控件数据绑定?是 我为希望使用此功能的开发人员提供了一个简单的解决方案。它使用附加属性来标识弹出控件的ItemsSource和ItemTemplate。如果开发人员选择使用MenuFlyoutItem或其他东西,则由他们决定 这是随附的财产: public class BindableFlyout : DependencyObject { #region ItemsSource public static IEnumera

我在看书,不禁想知道同样的事情

是否有方法将菜单
弹出按钮
控件数据绑定?

我为希望使用此功能的开发人员提供了一个简单的解决方案。它使用附加属性来标识弹出控件的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>

看起来像这样:

我希望这对你有帮助


祝你好运

这对我很管用。我希望我没有错过任何东西

 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);
            }
        }
    }
class自定义命令:ICommand
{
公共ICommand命令对象{get{返回此;}
公共字符串CommandName{get;private set;}
公共CustomCommand(字符串名称):base()
{
this.CommandName=名称;
}
}
类封装OrderCoratorObjectForContextMenu
{
私有对象baseObject;
//将属性更改为baseObject
公共列表可用命令{get;set;}
公共封装OrderCoratorObjectForContextMenu(对象基对象,列表命令)
{
this.baseObject=baseObject;
this.AvailableCommands=命令;
}
}
类SomePage:Page
{
私人菜单;
公共网页()
{
//我不知道为什么,但它应该在这里…除非UI/设计变得疯狂
this.mFlyout=新的MenuFlyout();
}
私有无效网格保持(对象发送方,保持路由目标)
{
如果(e.OriginalSource为FrameworkElement&(e.OriginalSource为FrameworkElement)。DataContext为封装OrderCoratorObjectForContextMenu)
{
//只有属性是“只读”,而不是列表本身,因此。。。
this.mFlyout.Items.Clear();
MenuFlyoutItem menuItem;
foreach(中的CustomCommand命令((例如,OriginalSource作为FrameworkElement)。DataContext作为封装OrderCoratorObjectForContextMenu)。可用的命令)
{
menuItem=新的MenuFlyoutItem();
menuItem.Text=command.CommandName;
menuItem.Command=Command.CommandObject;
this.mFlyout.Items.Add(menuItem);
}
FrameworkElement senderElement=发送方作为FrameworkElement;
this.mFlyout.ShowAt(senderElement);
}
}
}

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

Jerry的解决方案有一个严重的缺陷:当你点击一个项目时,MenuFlyout并没有关闭,我发现这样做非常困难,因为似乎(几乎?)不可能从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;
    }
}
public类bindableflout:MenuFlyout
{
公共i收集项目资源
{
get{return(ICollection)GetValue(ItemsSourceProperty);}
set{SetValue(ItemsSourceProperty,value);}
}
公共静态只读依赖项Property ItemsSourceProperty=
DependencyProperty.Register(“ItemsSource”、typeof(ICollection)、typeof(BindableFlyout)、new PropertyMetadata(null)(DependencyObject o、DependencyPropertyChangedEventArgs)=>
{
设置(o为BindableFlyout);
}
));
专用静态异步无效设置(BindableFlyout菜单UflyOut)
{
if(Windows.ApplicationModel.DesignMode.DesignModeEnabled)
返回;
if(menuFlyout.ItemsSource==null)
返回;
等待menuFlyout.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,()=>
{
menuFlyout.Items.Clear();
foreach(菜单项资源中的变量菜单项)
{
menuFlyout.Items.Add(新的MenuFlyoutItem()
{
Text=menuItem.Text,
Command=menuItem.Command
});
}
});
}
}
公共类ContextMenuCommand
{
公共ContextMenuCommand(ICommand命令,字符串文本)
{
命令=命令;
文本=文本;
}
公共字符串文本
{
获得;私人设置;
}
公共ICommand命令
{
获得;私人设置;
}
}

上面的代码片段不会侦听ItemsSource的更改,但您可以轻松地调整该类。

以下是我使用示例代码的答案,但我发现单击某个项目后,菜单不再自动关闭。有什么想法吗?哦,我想这可能是因为这是一个弹出窗口,而不是一个菜单,我会看看我是否能从你的博客中找到一些东西。感谢使用ChangePropertyAction混合行为,您只需在单击菜单项时将Flyout.IsOpen属性设置为false即可。有道理?当然,您也可以在代码隐藏中执行此操作。两种方法都有效。如果您想在ViewModel中使用MenuOpen属性,也可以在其中使用它。很多选项。ChangePropertyAction不起作用,因为它实际上是一个名为Hide的方法。CallMethodAction无法工作,因为无法在BindableFlyout的DataTemplate内进行ElementName绑定,因此无法调用父Flyout的Hide方法。最后,我选择了使用视图模型中的代码隐藏和事件。如何使它与
SelectedItem
一起工作?这意味着MenuItem会触发调用控件来根据我在这个可绑定弹出按钮中选择的内容进行更改?