简体   繁体   中英

Bind and update ObservableCollection<T> to MenuFlyoutSubItem

I am developing a UWP (Universal Windows Platform) app in Visual Studio 2015. I am currently facing this problem that I want to add items to a MenuFlyoutSubItem . Basically, the items are a collection of Playlists.

My question is this: How can I bind an ObservableCollection or a List to a MenuFlyoutSubItem and update the items when a new item is added? I already tried this from here: Bind Obserable Collection to MenuFlyoutSubItem in UWP

public static class MenuExtension
{
public static List<MenuFlyoutItem> GetMyItems(DependencyObject obj)
{ return (List<MenuFlyoutItem>)obj.GetValue(MyItemsProperty); }

public static void SetMyItems(DependencyObject obj, List<MenuFlyoutItem> value)
{ obj.SetValue(MyItemsProperty, value); }

public static readonly DependencyProperty MyItemsProperty =
    DependencyProperty.Register("MyItems", typeof(List<MenuFlyoutItem>), typeof(MenuExtension),
    new PropertyMetadata(new List<MenuFlyoutItem>(), (sender, e) =>
    {
        Debug.WriteLine("Filling collection");
        var menu = sender as MenuFlyoutSubItem;
        menu.Items.Clear();
        foreach (var item in e.NewValue as List<MenuFlyoutItem>) menu.Items.Add(item);
    }));

}

This is good as far as the items do not change but in my scenario Items will change. How can I add updating to this extension? If the Flyout is created again on each popup then it will update but I don't know how to implement this behaviour.

Any help will be highly appreciated, thank you.

If the Flyout is created again on each popup then it will update but I don't know how to implement this behaviour.

You are right, I removed all the items of the MenuFlyout and add them again. And the flyout is updated. Directly update the sub items of MenuFlyoutSubItem doesn't have any effect on surface.

So to do that you can modify the codes like below:

  1. Binding the Items to the MenuFlyout instead of MenuFlyoutSubItem in XAML:

     <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" VerticalAlignment="Center"> <Button Content="click" Name="myBtn"> <Button.Flyout> <MenuFlyout local:FlyoutMenuExtension.MyItems="{Binding OptionItems}"> </MenuFlyout> </Button.Flyout> </Button> <Button Content="modify" Click="Button_Click"/> </StackPanel>
  2. Init/update the MenuFlyout items in code-behind:

     public sealed partial class MainPage : Page,INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); public List<MenuFlyoutItemBase> OptionItems { get; set; } public List<MenuFlyoutItemBase> InitFlyoutItems() { var list = new List<MenuFlyoutItemBase> { new MenuFlyoutItem {Text="Start item 1" }, new MenuFlyoutItem {Text="Start item 2" }, new MenuFlyoutItem {Text="Start item 3" }, new MenuFlyoutSubItem { Text="Start Item 4" } }; ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "Old Sub Item 1" }); ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "Old Sub Item 2" }); ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "Old Sub Item 3" }); ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "Old Sub Item 4" }); return list; } public List<MenuFlyoutItemBase> UpdateFlyoutItems() { var list = new List<MenuFlyoutItemBase> { new MenuFlyoutItem {Text="Start item 1" }, new MenuFlyoutItem {Text="Start item 2" }, new MenuFlyoutItem {Text="Start item 3" }, new MenuFlyoutSubItem { Text="Start Item 4" } }; ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "New Sub Item 1" }); ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "New Sub Item 2" }); ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "New Sub Item 3" }); ((MenuFlyoutSubItem)list[3]).Items.Add(new MenuFlyoutItem { Text = "New Sub Item 4" }); return list; } public MainPage() { this.InitializeComponent(); DataContext = this; } protected override void OnNavigatedTo(NavigationEventArgs e) { OptionItems = InitFlyoutItems(); RaiseProperty(nameof(OptionItems)); base.OnNavigatedTo(e); } private void Button_Click(object sender, RoutedEventArgs e) { OptionItems=UpdateFlyoutItems(); RaiseProperty(nameof(OptionItems)); } }
  3. Modify the MenuExtension :

     public static class FlyoutMenuExtension { public static List<MenuFlyoutItemBase> GetMyItems(DependencyObject obj) { return (List<MenuFlyoutItemBase>)obj.GetValue(MyItemsProperty); } public static void SetMyItems(DependencyObject obj, List<MenuFlyoutItemBase> value) { obj.SetValue(MyItemsProperty, value); } public static readonly DependencyProperty MyItemsProperty = DependencyProperty.Register("MyItems", typeof(List<MenuFlyoutItemBase>), typeof(FlyoutMenuExtension), new PropertyMetadata(new List<MenuFlyoutItemBase>(), (sender, e) => { var menu = sender as MenuFlyout; menu.Items.Clear(); foreach (var item in e.NewValue as List<MenuFlyoutItemBase>) { menu.Items.Add(item); } })); }

Here is the complete Demo: MenuFlyoutSubItemBindingSample

I've tried what @Elvis Xia - MSFT suggested, and although it didn't work for me, it showed me the way to the solution. I got a compilation error (using x:Bind) in one of the auto generated .cs files, saying that it couldn't convert from MenuFlyout to FrameworkElement. When I used 'Binding' instead, I managed to compile, but the menu did not update. So, what I did was simply to move the dependency property one level up (or 2 levels, if you consider <Button.Flyout> ), to the button itself:

<Button local:ButtonExtension.MenuFlyout="{x:Bind MainViewModel.Menu, Mode=OneWay}"/>

Here's the ButtonExtension class:

public static class ButtonExtension
{
    public static readonly DependencyProperty MenuFlyoutProperty =
        DependencyProperty.Register("MenuFlyout",
            typeof(MenuFlyout), typeof(ButtonExtension),
            new PropertyMetadata(new MenuFlyout(), (sender, e) =>
            {
                var button = sender as Button;
                button.Flyout = e.NewValue as MenuFlyout;
            }));

    public static MenuFlyout GetMenuFlyout(DependencyObject obj)
    {
        return (MenuFlyout)obj.GetValue(MenuFlyoutProperty);
    }

    public static void SetMenuFlyout(DependencyObject obj, MenuFlyout value)
    {
        obj.SetValue(MenuFlyoutProperty, value);
    }
}

Here's the property in the ViewModel:

public MenuFlyout Menu { get; private set; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM