我正在阅读this article并且忍不住想知道同样的事情。
有没有办法对菜单Flyout
控件进行数据绑定?
答案 0 :(得分:11)
是
我为需要此功能的开发人员提供了一个简单的解决方案。它使用附加属性来标识ItemsSource和用于Flyout控件的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。
看起来像这样:
我希望这会对你有所帮助。
祝你好运!
答案 1 :(得分:1)
即使很久以前问过这个原始问题,我也会发布我发现的解决方案,因为其他人可能觉得它很有用。
Jerry的解决方案有一个严重的缺陷:当你点击一个项目时,MenuFlyout并没有关闭,我发现这样做非常困难,因为它似乎(几乎?)无法从DataTemplate内部获取对Flyout的引用来关闭它。
我已经提出了这个子类化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;
}
}
上面的代码段不会收听ItemsSource的更改,但您可以轻松调整该类。
答案 2 :(得分:0)
它对我有用。我希望我没有错过任何一件事。
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);
}
}
}