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.
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.
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
- adding 3 items to the ListView using the "Add..." button.
- Navigating to the submenuFlyout of each ListViewItem
- Add 2 more Listview Items using the "Add..." button
- navigate to the submenuFlyout of the two new items and finally
- 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);
}