选择和嵌套ListBox的麻烦

时间:2017-11-01 12:32:53

标签: c# wpf listbox selection

简介

首先,我为模糊的标题道歉,但我无法想出一个更好的方式来解决我提出的问题:

我正在开发一个C#/ WPF应用程序,并且需要在UI中显示嵌套列表结构。有一个应该显示的对象列表,但是每个对象都有一个必须以嵌套方式显示的对象的子列表(可能是空的)。为实现此目的,我使用ListBox,其中包含一个包含嵌套ListBox的项模板,以显示子列表。

我已经实现了确保一次只能选择一个项目(无论是外部项目还是内部项目)的目标,但我现在正在努力解决以下两个需要帮助的问题:

  1. 我需要一种方法来禁用某些项目的选择,以及鼠标悬停时的突出显示,但不会禁用所有内部UI元素。在下面的示例中,这将禁用带有红色文本的条目的选择/突出显示,以及此条目的子列表,但是当将鼠标悬停在文本上时仍然能够看到ToolTip
  2. 当鼠标位于子列表中的内部ListBoxItem上时,禁用外部ListBoxItem的突出显示。因此,一次只有一个ListBoxItem突出显示,但突出显示外部ListBoxItem也可以在内部子列表上绘制突出显示。
  3. 实施例

    以下是运行我放在一起的示例所需的两个文件:

    MainWindow.xaml

    <Window x:Class="WpfApplication1.MainWindow"
            x:Name="TopLevelWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
            xmlns:local="clr-namespace:WpfApplication1"
            DataContext="{Binding RelativeSource={RelativeSource Self}}"
            Title="MainWindow" Height="350" Width="525">
       <ScrollViewer>
          <ScrollViewer.Resources>
             <local:InvertConverter x:Key="InvertConverter"/>
    
             <CollectionViewSource Source="{Binding Items}" x:Key="ItemsView">
                <CollectionViewSource.SortDescriptions>
                   <scm:SortDescription PropertyName="BoolProperty" Direction="Descending"/>
                   <scm:SortDescription PropertyName="Text" Direction="Ascending"/>
                </CollectionViewSource.SortDescriptions>
             </CollectionViewSource>
    
             <DataTemplate x:Key="ItemTemplate">
                <DockPanel Margin="4,4,2,4"
                           LastChildFill="True">
                   <TextBlock Text="{Binding Text}"
                              DockPanel.Dock="Top"
                              ToolTip="ERROR"
                              ToolTipService.IsEnabled="{Binding BoolProperty,
                                                                 Converter={StaticResource InvertConverter}}">
                      <TextBlock.Style>
                         <Style TargetType="{x:Type TextBlock}">
                            <Style.Triggers>
                               <DataTrigger Binding="{Binding BoolProperty}" Value="False">
                                  <Setter Property="Foreground" Value="Red"/>
                               </DataTrigger>
                            </Style.Triggers>
                         </Style>
                      </TextBlock.Style>
                   </TextBlock>
                   <ListBox x:Name="InnerListBox"
                            ItemsSource="{Binding Items}"
                            SelectedItem="{Binding ElementName=TopLevelWindow,
                                                   Path=DataContext.SelectedInnerItem}"
                            BorderThickness="0"
                            Background="Transparent"
                            MouseEnter="InnerListBox_MouseEnter"
                            MouseLeave="InnerListBox_MouseLeave"/>
                </DockPanel>
             </DataTemplate>
          </ScrollViewer.Resources>
    
          <ListBox ItemsSource="{Binding Source={StaticResource ItemsView}}"
                   ItemTemplate="{StaticResource ItemTemplate}"
                   SelectedItem="{Binding SelectedOuterItem}"
                   BorderThickness="0"/>
       </ScrollViewer>
    </Window>
    

    MainWindow.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
       /// <summary>
       /// Interaction logic for MainWindow.xaml
       /// </summary>
       public partial class MainWindow : Window, INotifyPropertyChanged
       {
          public event PropertyChangedEventHandler PropertyChanged;
    
          private OuterClass _selectedOuterItem;
          private string _selectedInnerItem;
    
          public ObservableCollection<OuterClass> Items { get; set; }
          public OuterClass SelectedOuterItem
          {
             get { return _selectedOuterItem; }
             set
             {
                if (_selectedOuterItem != value)
                {
                   _selectedOuterItem = value;
                   _selectedInnerItem = null;
                   PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedOuterItem)));
                   PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedInnerItem)));
                }
             }
          }
          public string SelectedInnerItem
          {
             get { return _selectedInnerItem; }
             set
             {
                if (_selectedInnerItem != value)
                {
                   _selectedInnerItem = value;
                   _selectedOuterItem = null;
                   PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedInnerItem)));
                   PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedOuterItem)));
                }
             }
          }
    
          public MainWindow()
          {
             Items = new ObservableCollection<OuterClass>()
             {
                new OuterClass("Beep", true, new List<string>()
                {
                   "Beep - A",
                   "Beep - B",
                   "Beep - C"
                }),
                new OuterClass("Boop", true, new List<string>()
                {
                   "Boop - A",
                   "Boop - B"
                }),
                new OuterClass("Baap", false, new List<string>()),
                new OuterClass("Biip", true, new List<string>()),
                new OuterClass("Buup", false, new List<string>()
                {
                   "Buup - A",
                   "Buup - B",
                   "Buup - C",
                   "Buup - D",
                   "Buup - E"
                })
             };
             InitializeComponent();
          }
    
          private void InnerListBox_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
          {
             ListBox listBox = e.Source as ListBox;
             if (listBox != null)
             {
                OuterClass dc = listBox.DataContext as OuterClass;
                if (dc != null)
                {
                   dc.MouseOverInternal = true;
                }
             }
          }
    
          private void InnerListBox_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
          {
             ListBox listBox = e.Source as ListBox;
             if (listBox != null)
             {
                OuterClass dc = listBox.DataContext as OuterClass;
                if (dc != null)
                {
                   dc.MouseOverInternal = false;
                }
             }
          }
       }
    
       public class OuterClass : INotifyPropertyChanged
       {
          private bool _mouseOverInternal;
    
          public string Text { get; set; }
          public bool BoolProperty { get; set; }
          public ObservableCollection<string> Items { get; set; }
          public bool MouseOverInternal
          {
             get { return _mouseOverInternal; }
             set
             {
                if (_mouseOverInternal != value)
                {
                   _mouseOverInternal = value;
                   PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MouseOverInternal)));
                }
             }
          }
    
          public OuterClass(string x, bool y, IEnumerable<string> z)
          {
             Text = x;
             BoolProperty = y;
             Items = new ObservableCollection<string>(z);
          }
    
          public event PropertyChangedEventHandler PropertyChanged;
       }
    
       public class InvertConverter : IValueConverter
       {
          public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
          {
             return foo(value);
          }
    
          public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
          {
             return foo(value);
          }
    
          private bool? foo(object val)
          {
             bool? boolVal = val as bool?;
             if (boolVal.HasValue)
             {
                return !boolVal.Value;
             }
             return null;
          }
       }
    }
    

    我尝试过的&amp;结论

    到目前为止,我已尝试过以下内容:

    • 禁用将ListBoxItem设置为BoolProperty的{​​{1}}个元素。这提供了所需的行为,但内部False上的ToolTip也被禁用了。
    • 在内部TextBlock元素上使用MouseEnterMouseLeave事件,并使用该事件设置外部ListBox元素的属性。我无法通过我尝试设置的任何属性实现所需的行为,上述示例中仍然存在处理这些事件的代码。
    • 在外部ListBoxItem元素上使用Focusable属性。虽然禁用了这些元素的选择,并允许显示内部ListBoxItem上的ToolTip,但它仍然会突出显示这些项目。这似乎很有希望,只要可以删除突出显示,因为我可以在同一触发器上禁用内部TextBlock

    感谢您提供的任何帮助。如果需要,我愿意使用其他ListBox,但如果可能的话,我希望与ItemsControlListBox保持一致。

0 个答案:

没有答案