首先,我为模糊的标题道歉,但我无法想出一个更好的方式来解决我提出的问题:
我正在开发一个C#/ WPF应用程序,并且需要在UI中显示嵌套列表结构。有一个应该显示的对象列表,但是每个对象都有一个必须以嵌套方式显示的对象的子列表(可能是空的)。为实现此目的,我使用ListBox
,其中包含一个包含嵌套ListBox
的项模板,以显示子列表。
我已经实现了确保一次只能选择一个项目(无论是外部项目还是内部项目)的目标,但我现在正在努力解决以下两个需要帮助的问题:
ToolTip
。ListBoxItem
上时,禁用外部ListBoxItem
的突出显示。因此,一次只有一个ListBoxItem
突出显示,但突出显示外部ListBoxItem
也可以在内部子列表上绘制突出显示。以下是运行我放在一起的示例所需的两个文件:
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;
}
}
}
到目前为止,我已尝试过以下内容:
ListBoxItem
设置为BoolProperty
的{{1}}个元素。这提供了所需的行为,但内部False
上的ToolTip
也被禁用了。TextBlock
元素上使用MouseEnter
和MouseLeave
事件,并使用该事件设置外部ListBox
元素的属性。我无法通过我尝试设置的任何属性实现所需的行为,上述示例中仍然存在处理这些事件的代码。ListBoxItem
元素上使用Focusable
属性。虽然禁用了这些元素的选择,并允许显示内部ListBoxItem
上的ToolTip
,但它仍然会突出显示这些项目。这似乎很有希望,只要可以删除突出显示,因为我可以在同一触发器上禁用内部TextBlock
。感谢您提供的任何帮助。如果需要,我愿意使用其他ListBox
,但如果可能的话,我希望与ItemsControl
或ListBox
保持一致。