如何禁用列表框中的项目,即使它在 WPF 中具有多选模式?

问题描述 投票:0回答:1

我正在努力在列表框中创建一个标题,该标题将变成灰色,并且其中有一个冒号,而其余条目(列表框项目)将为黑色。

我想做的是,如果该值与列表字符串中的类型之一匹配,那么它将变成灰色并成为标题。当它成为标题后,我想禁用项目框,使其不可单击,而其余条目可单击。

这是我的功能:

private void PopulateItems(List<string> items, string groupName)
{
    listbx.Items.Clear();

    List<string> itemsList = items;
    string founditemtype = null;

    foreach (string item in items)
    {
        foreach (string itemtype in Type)
        {
            if(item == itemtype)
            {
                founditemtype = itemtype;
                Label label = new Label();
                label.Content = itemtype + ":";
                label.FontWeight = FontWeights.Bold;
                label.Foreground = Brushes.Gray;

                // Handle the PreviewMouseLeftButtonDown event to prevent the grayed text to be selected:
                label.PreviewMouseLeftButtonDown += (sender, e) =>
                {
                    if (label.Content.ToString().Contains(":"))
                    {
                        e.Handled = true;
                    }
                };

                listbx.Items.Add(label);
            }
        }

        if(item != founditemtype)
        {
            listbx.Items.Add(item);
        }
}

这是我的列表框的 WPf:

<ListBox x:Name="listbx" BorderBrush="Black" BorderThickness="2" Grid.Row="2" VerticalAlignment="Top" HorizontalAlignment="Left" Width="525" Height="200" SelectionMode="Multiple" />

但是,每次我运行该应用程序时,列表框总是允许用户选择应该变灰的项目,使其可单击和选择。即使打开多选模式,当用户选择其他项目并误选择标题项目时,仍然可以选择它。我在这里做错了什么?

c# wpf listbox disable
1个回答
0
投票

您应该避免直接在

ItemsControl.Items
属性上工作。相反,创建一个可以操作的成员变量。

此外,切勿直接向 ItemsControl 添加控件。这将极大地影响性能(因为这样做会禁用

ListBox
的 UI 虚拟化)。

您可以通过使用所有

UIElement
元素的内置行为来简化代码:当您将
UIElement.IsEnabled
上的
ListBoxItem
属性设置为
false
时,项目容器将自动禁用,意味着变灰并且不可点击。

要实现此目的,您需要访问

ListBoxItem

使用
MultiBinding
IMultiValueConverter
可以轻松解决您的问题,避免不必要的迭代。

我还建议一般使用数据绑定 Microsoft Learn:WPF 中的数据绑定概述

如果您最初的目标是实现分组,请查看 Microsoft Learn:集合视图 和分组。

ListBox
能够自动创建组。

ListBoxValueToHeaderConverter.cs

public class ListBoxValueToHeaderConverter : IMultiValueConverter
{
  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  {
    string currentItem = values
      .OfType<string>()
      .First();
    ListBoxItem currentItemContainer = values
      .OfType<ListBoxItem>()
      .First();
    IEnumerable<string> predicateCollection = values
      .OfType<IEnumerable<string>>()
      .First();
      
    if (predicateCollection.Contains(currentItem))
    {
      // Disable the item container to make it automatically not clickable
      // and to be rendered as grayed out
      currentItemContainer.IsEnabled = false;
      return ":";
    }

    return currentItem;
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    => throw new NotSupportedException();
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  // You should assign a new collection if you want to modify it.
  // This will update the MultiBinding (in XAML). 
  // In general, when binding an ItemsCOntrol to a collection 
  // prefer to clear/replace the items to improve the performance of the control. 
  // Because this collection is not the binding source for a control 
  // replacing in this scenario is fine.
  public ObservableCollection<string> Type { get; private set; }

  // Declare this property as public if you want to bind to it
  private ObservableCollection<string> Items { get; }

  public MainWindow(TestViewModel dataContext, INavigator navigator)
  {
    InitializeComponent();

    this.Items = new ObservableCollection<string>();

    // It's generally recommended to use data binding instead of explicit assignment.
    this.ListBox.ItemsSource = this. Items;
  }

  private void PopulateItems(List<string> newItems, string groupName)
  {
    this.Items.Clear();
    newItems.ForEach(this.Items.Add);
  }
}

MainWindow.xaml

<Window>
  <Window.Resources>
    <local:ListBoxValueToHeaderConverter x:Key="ListBoxValueToHeaderConverter" />
  </Window.Resources>

  <ListBox x:Name="ListBox">
    <ListBox.ItemContainerStyle>
      <Style TargetType="ListBoxItem">
        <Setter Property="Content">
          <Setter.Value>
            <MultiBinding Converter="{StaticResource ListBoxValueToHeaderConverter}">

              <!-- The current item -->
              <Binding />

              <!-- The item container (ListBoxItem) -->
              <Binding RelativeSource="{RelativeSource Self}" />

              <!-- The filter predicate collection defined in the parent Window --> 
              <Binding RelativeSource="{RelativeSource AncestorType=Window}"
                       Path="Type" />
            </MultiBinding>
          </Setter.Value>
        </Setter>

        <Style.Triggers>
    
          <!-- Define a trigger to change the look of the disabled ListBoxItem -->
          <Trigger Property="IsEnabled"
                   Value="False">
            <Setter Property="FontWeight"
                    Value="Bold" />
          </Trigger>
        </Style.Triggers>
      </Style>
    </ListBox.ItemContainerStyle>
  </ListBox>
</Window>
© www.soinside.com 2019 - 2024. All rights reserved.