Home > Enterprise >  C# XAML UWP - MenuFlyoutItem List not updating consistently in MenuFlyoutSubItem
C# XAML UWP - MenuFlyoutItem List not updating consistently in MenuFlyoutSubItem

Time:08-29

this is probably a simple solution - its just a bit long to explain.

I add custom list view items to a ListView at run-time. Each ListView item has a Name, a Bool and a button. The button when clicked displays a Flyout menu which has subitem menu as shown in the image. The subitem menu should only display the name of all other items not itself. The correct behavior is shown in the first image as the "Item 4" menu button was clicked we only see Items 0 to 3 listed in the submenu.

5th button clicked works fine

The issue is that if i navigate to a submenu and then later add new items to the listbox, the new items never appear in the submenu for the older items previously navigated to. Like in the image below, where i clicked Item 1 button but only Item 0 and Item 2 are listed and for some reason Items 3 and 4 are not.

enter image description here

Firstly there is a complete minimum VS2019 solution demonstrating the behavior i'm describing above on GitHub here, though i have summarised what i think are the key bits of code below.

Non-boiler plate XAML header (MainPage.Xaml)

xmlns:local="using:DynamicFlyoutMenuTest.ViewModels"

The main ListView defintion and its DataTemplate as well as a button to add ListView items at run-time:

<StackPanel>
    <Button Name="AddCustomListItemBtn" Click="AddCustomListItemBtn_Click">Add Custom ListItem</Button>
    <ListView
        Name="LayerListBox"
        Height="Auto"
        BorderBrush="{ThemeResource SystemBaseLowColor}"
        BorderThickness="1.0"
        ItemsSource="{x:Bind ViewModel.MyCustomListItems}">
        <ListView.HeaderTemplate>
            <DataTemplate>
                <Grid Padding="2" Background="{ThemeResource SystemBaseLowColor}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="190" />
                        <ColumnDefinition Width="132" />
                    </Grid.ColumnDefinitions>
                    <TextBlock Style="{ThemeResource CaptionTextBlockStyle}" Text="Name" />
                    <TextBlock
                        Grid.Column="1"
                        Style="{ThemeResource CaptionTextBlockStyle}"
                        Text="Active" />
                </Grid>
            </DataTemplate>
        </ListView.HeaderTemplate>
        <ListView.ItemTemplate>
            <DataTemplate x:Name="TableDataTemplate" x:DataType="local:MyCustomListItem">
                <Grid Height="48" AutomationProperties.Name="{x:Bind ItemName}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="190" />
                        <ColumnDefinition Width="132" />
                        <ColumnDefinition Width="132" />
                    </Grid.ColumnDefinitions>
                    <TextBlock
                        Grid.Column="0"
                        Padding="10"
                        VerticalAlignment="Center"
                        Text="{x:Bind ItemName, Mode=OneWay}" />
                    <CheckBox
                        Grid.Column="1"
                        VerticalAlignment="Center"
                        IsChecked="{x:Bind isEditing, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    <Button
                        Name="exportLayerButton"
                        Grid.Column="2"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Center">
                        <Button.Flyout>
                            <MenuFlyout Opening="MenuFlyout_Opening">
                                <MenuFlyoutItem
                                    Name="Action1Btn"
                                    Click="Action1Btn_Click"
                                    Text="Action 1" />
                                <MenuFlyoutItem
                                    Name="Action2Btn"
                                    Click="Action2Btn_Click"
                                    Text="Action 2" />
                                <MenuFlyoutSubItem x:Name="SubActionsBtn" Text="Choose Sub Action">
                                    <MenuFlyoutItem Name="NoSubActionBtn" Text="None" />
                                </MenuFlyoutSubItem>
                            </MenuFlyout>
                        </Button.Flyout>
                        <Polygon
                            Fill="Black"
                            Points="0,0 6,4,0,8"
                            Stroke="Black" />
                    </Button>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackPanel>

MainPage.xaml.cs - Add Item to List and Update Flyout Sub Menu Items

private void MenuFlyout_Opening(object sender, object e)
{
    //make MenuFlyoutSubItem list all Items in ListView except the one triggering  this function
    var menuFlyout = sender as MenuFlyout;
    // get the menu list we want to add to

    MenuFlyoutSubItem menuSubItems = menuFlyout.Items.Where(x => x.Name == "SubActionsBtn").FirstOrDefault() as MenuFlyoutSubItem;

    // get the active maplayerlistitem (that triggered this menu opening event) 
    MyCustomListItem myCustomListItem = (menuFlyout.Target as Button).DataContext as MyCustomListItem;

    menuSubItems.Items.Clear();
    foreach (var targetItem in ViewModel.MyCustomListItems)
    {
        if (myCustomListItem.ItemName != targetItem.ItemName)
        {
            var tItem = new MenuFlyoutItem();
            tItem.Text = targetItem.ItemName.ToString();
            //tItem.Click  = new Windows.UI.Xaml.RoutedEventHandler(DoSomethingBtn_Click);
            menuSubItems.Items.Add(tItem);
        }
    }
}

private void AddCustomListItemBtn_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    // Update ListView
    var newItem = new MyCustomListItem();
    newItem.ItemName = "Item "   ViewModel.MyCustomListItems.Count.ToString();
    newItem.isEditing = false;
    ViewModel.MyCustomListItems.Add(newItem);
}

MainViewModel.cs

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Microsoft.Toolkit.Mvvm.ComponentModel;

namespace DynamicFlyoutMenuTest.ViewModels
{
    public class MainViewModel : ObservableObject
    {
        public ObservableCollection<MyCustomListItem> MyCustomListItems = new ObservableCollection<MyCustomListItem>();
        public MainViewModel()
        {

        }
    }
    public class MyCustomListItem : INotifyPropertyChanged
    {
        public MyCustomListItem()
        {

        }

        private bool _isEditing;
        public bool isEditing
        {
            get { return _isEditing; }
            set
            {
                _isEditing = value;
                NotifyPropertyChanged(this, "isEditing");
            }
        }

        private string _itemName;
        public string ItemName
        {
            get { return _itemName; }
            set
            {
                _itemName = value;
                NotifyPropertyChanged(this, "ItemName");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void NotifyPropertyChanged(object sender, string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(sender, e);
            }
        }
    }
}

EDIT You can view the issue in video at https://www.youtube.com/watch?v=yPNNtsS-n5Q You can reproduce the issue from the GitHub source by

  1. adding 3 items to the ListView using the "Add..." button.
  2. Navigating to the submenuFlyout of each ListViewItem
  3. Add 2 more Listview Items using the "Add..." button
  4. navigate to the submenuFlyout of the two new items and finally
  5. navigate to submenuFlyout of the original 3 items and see that they haven't updated to reflect the additional ListView items added.

CodePudding user response:

I found a workaround by removing the exsiting MenuFlyoutSubItem and adding a new one each time the Flyout is opened. So it's not ideal, but it does work.

If anyone has as an actual solution, id be happy to mark it as such.

Otherwise here is the workaround:

private void MenuFlyout_Opening(object sender, object e)
        {
            //make MenuFlyoutSubItem list all Items in ListView except the one triggering this function
            var menuFlyout = sender as MenuFlyout;
            // get the menu list we want to add to

            MenuFlyoutSubItem menuSubItems = menuFlyout.Items.Where(x => x.Name == "SubActionsBtn").FirstOrDefault() as MenuFlyoutSubItem;

            // get the active maplayerlistitem (that triggered this menu opening event) 
            MyCustomListItem myCustomListItem = (menuFlyout.Target as Button).DataContext as MyCustomListItem;

            menuFlyout.Items.Remove(menuSubItems);
            menuSubItems = new MenuFlyoutSubItem();
            menuSubItems.Name = "SubActionsBtn";
            menuSubItems.Text = "Choose Sub Action";
            foreach (var targetItem in ViewModel.MyCustomListItems)
             {
                if (myCustomListItem.ItemName != targetItem.ItemName)
                {
                    var tItem = new MenuFlyoutItem();
                    tItem.Text = targetItem.ItemName.ToString();
                    //tItem.Click  = new Windows.UI.Xaml.RoutedEventHandler(DoSomethingBtn_Click);
                    menuSubItems.Items.Add(tItem);              
                }
            }
            menuFlyout.Items.Add(menuSubItems);
        }
  • Related