在使用 ContextMenu.ItemTemplate 动态创建的 ContextMenu 中查找并取消选中所有 MenuItem

Finding and unchecking all MenuItems in ContextMenu created dynamically with ContextMenu.ItemTemplate

我在 DataGridTemplateColumn 中有一个 ContextMenu,菜单项是视图的所有生产国家/地区。我正在使用它来过滤掉国家。

代码如下:

<DataGridTemplateColumn SortMemberPath="ProductionCountry"       x:Name="prodCountryColumn"  Width="Auto" CanUserSort="True">
                <DataGridTemplateColumn.Header>
                    <TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                        <TextBlock.ContextMenu x:Name="cmProdCountry" >
                           <ContextMenu  Loaded="ContextMenu_Loaded"  ItemsSource="{Binding Path=FilterProdCountry}">                                   
                                <ContextMenu.ItemTemplate>                                        
                                    <DataTemplate>
                                        <MenuItem Name="prodCountryFilter"  IsCheckable="True" Checked="toggleFilterOn" Unchecked="toggleFilterOff" Header="{Binding}"  ItemsSource="{Binding}">

                                        </MenuItem>

                                    </DataTemplate>      

                                </ContextMenu.ItemTemplate>
                                </ContextMenu>                        
                        </TextBlock.ContextMenu> 
                        Produksjonsland
                    </TextBlock>
                </DataGridTemplateColumn.Header>

                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Padding="5,1,5,1" Text="{Binding Path=ProductionCountry}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

要删除所有过滤,我有一个列表 "Show all"。

菜单项是可检查的,这是我的问题:当菜单项 "Show all" 被选中时,如何找到并取消选中所有菜单项。

在后面的代码中,我使用 getancestor 函数将我带到上下文菜单,但所有项目仅作为字符串列出,因此我无法设置 MenuItem.IsChecked = false;

因此,当我尝试在代码隐藏中查找所有要取消选中的菜单项时,出现异常。

代码如下:

 var filterItem = (sender as MenuItem);
 var parent = filterItem.FindAncestorTest<TextBlock>();
 foreach (var menuitem in parent.Items) 
        {
            (mi as MenuItem).IsChecked = false;
        }

首先,我建议您避免混合使用 MVVM 和代码隐藏。您应该选择其中之一并将其用于项目的每个部分。

我个人更喜欢 MVVM,所以我的解决方案是 MVVM 兼容的。我不知道你到目前为止写的所有代码,所以我的回答基于一个简化的例子。

我刚刚为您称为 "filter" 的对象创建了一个模型(每个过滤器都由一个菜单项表示):

public class Filter : NotifyPropertyChangedBase
{
    private bool isSelected;
    private string description;

    public Filter(string description)
    {
        this.description = description;
    }

    public string Description
    {
        get
        {
            return description;
        }
        set
        {
            if (StringComparer.Ordinal.Compare(description, value) != 0)
            {
                description = value;
                OnPropertyChanged("Description");
            }
        }
    }

    public bool IsSelected
    {
        get
        {
            return isSelected;
        }
        set
        {
            if (isSelected != value)
            {
                isSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }
    }
}

其中 NotifyPropertyChangedBase 是一个基础 class,它只是实现了 INotifyPropertyChanged.

现在我的 XAML:

<TextBox Margin="5" HorizontalAlignment="Stretch">
    <TextBox.ContextMenu>
        <ContextMenu ItemsSource="{Binding Path=FilterProdCountry}">
            <ContextMenu.ItemTemplate>
                <DataTemplate>
                    <CheckBox Margin="4" HorizontalAlignment="Left"
                                VerticalAlignment="Center"
                                IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"
                                Content="{Binding Path=Description, Mode=OneWay}" />
                </DataTemplate>
            </ContextMenu.ItemTemplate>
        </ContextMenu>
    </TextBox.ContextMenu>
</TextBox>

假设 FilterProdCountry 是属于您的 ViewModel 的 Filter 个对象的集合。那么...您要取消选中 MenuItem 吗?那么,只需检索相关的 Filter 对象,然后将其 IsSelected 属性 设置为 false;例如:

if (vm.FilterProdCountry[0].IsSelected)
{
    foreach (Filter filter in vm.FilterProdCountry.Skip(1))
    {
        filter.IsSelected = false;
    }
}

其中 vm 是您的 ViewModel 的实例。

我希望我的回答能给您一些提示,让您了解您的项目方式。

所以首先我想指出 Xaml 中的一个错误,我将 MenuItem 放在 ContextMenu.ItemTemplate 中,这给了我两个 MenuItem 元素。为了获得我想要的所有复选框元素,我包括了在 post: How can I find WPF controls by name or type? 中找到的扩展函数。 并像这样使用 FindAllChildren 函数:

private void toggleFilterOn(object sender, RoutedEventArgs e)
{
  var filterItem = (sender as CheckBox);
  var parent = filterItem.FindAncestorOfType<ContextMenu>();
  var children = parent.FindAllChildren().Where(item => item is CheckBox);
  foreach (CheckBox cb in children)
  {
      cb.IsChecked = false;
  }  
}