Я читал this article и не мог не задаться вопросом то же самое.Почему у Windows 8.1 MenuFlyout нет свойства ItemsSource?
Есть ли способ привязать меню Flyout
?
Я читал this article и не мог не задаться вопросом то же самое.Почему у Windows 8.1 MenuFlyout нет свойства ItemsSource?
Есть ли способ привязать меню 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.
выглядит так:
Я надеюсь, что это поможет вам.
Удачи!
Я использую ваш пример кода, но я нахожу, что меню больше не отменяет себя после щелчка элемента. Есть идеи? – krisdyson
О, я думаю, это может быть потому, что это вылет, а не MenuFlyout, я посмотрю, смогу ли я что-то сделать из вашего блога. спасибо – krisdyson
Использование поведения ChangePropertyAction Blend можно просто установить для свойства Flyout.IsOpen значение false при щелчке по элементу MenuItem. Имеют смысл? Конечно, вы могли бы сделать это и в коде. В любом случае. У вас также может быть свойство MenuOpen в ViewModel, если вы хотите сделать это там. Множество вариантов. –
Это работает для меня. Надеюсь, я ничего не пропустил.
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);
}
}
}
Несмотря на то, что первоначальный вопрос был задан много лет назад, я отправлю решение, которое я нашел, поскольку кто-то может найти его полезным.
Решение Джерри имеет серьезный недостаток: 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;
}
}
Сниппает выше не слушает изменения ItemsSource, но вы можете легко адаптировать класс.
Вот ответ http://blog.jerrynixon.com/2013/12/xaml-how-to-add-itemssource-to-windows.html –