【发布时间】:2016-02-12 03:36:40
【问题描述】:
我正在阅读this article 并且不禁想知道同样的事情。
有没有办法对菜单Flyout控件进行数据绑定?
【问题讨论】:
标签: windows-8 winrt-xaml
我正在阅读this article 并且不禁想知道同样的事情。
有没有办法对菜单Flyout控件进行数据绑定?
【问题讨论】:
标签: windows-8 winrt-xaml
是的。
我为需要此功能的开发人员提供了一个简单的解决方案。它使用附加属性来标识 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。
看起来像这样:
希望对你有帮助。
祝你好运!
【讨论】:
SelectedItem一起工作?这意味着 MenuItem 会根据我在此可绑定 Flyout 中选择的内容触发调用控件来更改?
即使最初的问题是很久以前提出的,我也会发布我找到的解决方案,因为其他人可能会觉得它有用。
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的变化,但是你可以很方便的适配这个类。
【讨论】:
它对我有用。我希望我没有错过任何事情。
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);
}
}
}
【讨论】: