在 TreeView 中突出显示自定义 DataTemplate
Highlight custom DataTemplate in TreeView
我有一个简单的 TreeView
,我正在尝试为其创建自定义 DataTemplate
。它按需要显示,但是当我尝试 select TreeViewItem
时,如果我单击 TreeView
中的文本,突出显示将不起作用。但是,如果我 select 就在文本的左侧,它会起作用:
来源非常简单,所以我猜我只是缺少样式连接:
xaml
<TreeView x:Name="treeView"
ItemsSource="{Binding TreeViewItems}"
Grid.Row="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<TreeViewItem Header="{Binding Header}">
<TreeViewItem.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding Command}" />
</TreeViewItem.InputBindings>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
我试过将此添加到 xaml,但没有帮助:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}"
BasedOn="{StaticResource {x:Type TreeViewItem}}" />
</TreeView.ItemContainerStyle>
树视图模型
public class TreeViewModel : BaseNotifyModel, ITreeViewModel
{
public TreeViewModel(ITreeViewService menuService)
{
TreeViewItems =
new ReadOnlyObservableCollection<MenuItemViewModel>(menuService.TreeViewMenu);
}
public ReadOnlyObservableCollection<MenuItemViewModel> TreeViewItems
{
get
{
return Get<ReadOnlyObservableCollection<MenuItemViewModel>>();
}
private set
{
Set(value);
}
}
}
MenuItemViewModel
public class MenuItemViewModel : BaseNotifyModel
{
public MenuItemViewModel()
{
MenuItems =
new ObservableCollection<MenuItemViewModel>();
}
public String Header
{
get
{
return Get<String>();
}
set
{
Set(value);
}
}
public ICommand Command
{
get
{
return Get<ICommand>();
}
set
{
Set(value);
}
}
public ObservableCollection<MenuItemViewModel> MenuItems
{
get
{
return Get<ObservableCollection<MenuItemViewModel>>();
}
set
{
Set(value);
}
}
}
TreeView
正在为 ItemsSource
中的每个项目创建一个 TreeViewItem
,所以不要在 TreeView
的项目中嵌套另一个 TreeViewItem
已经为您创建。那没有任何作用。您的模板应该只是为现有的 TreeViewItem
提供一种方式来显示其 DataContext
中的任何内容(在本例中为您的 MenuItemViewModel
)。
您想在树视图项目中显示Header
属性;所以就这样做吧。没什么特别的,只是 Label
或 ContentControl
,如果是 String
,甚至是 TextBlock
(尽管在 WPF 中具有任意内容的灵活性很有趣)。当用户单击内容时,您的命令就会执行。用户在树中唯一能看到的就是内容。这是 TreeViewItem
项唯一可见的部分,因此用户将要单击该部分。
第二个问题:一旦输入绑定得到 LeftClick
,就会中断 TreeView
中的选择。在我看来,你无法通过那种方法从这里到达那里。
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentControl
Content="{Binding Header}"
Background="Transparent"
>
<ContentControl.InputBindings>
<!-- This invokes the command, but breaks selection -->
<MouseBinding MouseAction="LeftClick"
Command="{Binding Command}" />
</ContentControl.InputBindings>
</ContentControl>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
您可以执行以下操作:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentControl
Content="{Binding Header}"
Background="Transparent"
>
</ContentControl>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<EventSetter Event="Selected" Handler="MenuTreeViewItem_Click" />
</Style>
</TreeView.ItemContainerStyle>
代码隐藏
private void MenuTreeViewItem_Click(object sender, RoutedEventArgs e)
{
((MenuItemViewModel)((FrameworkElement)sender).DataContext).Command.Execute(null);
}
There is a way to bind an event to a command in pure XAML,但它需要一些 C# 代码(大声笑)。但我的意思是,它是 "pure XAML",因为它是一个很好的通用可重用附加行为,而不是代码隐藏中难看的事件处理程序。相反,它与我上面所做的完全相同,但它是在代码中完成的,您可以更轻松地转移视线,并且可以在纯 XAML 中重复使用。
在@Ed Plunket 和@Evk 的帮助下,我找到了一个可行的解决方案。我切换到 ContentPresenter
并使用 Interaction.Triggers
在 MouseLeftButtonUp
操作上调用命令。
<TreeView ItemsSource="{Binding TreeViewItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentPresenter Content="{Binding Header}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding Path=DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ContentPresenter>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
我有一个简单的 TreeView
,我正在尝试为其创建自定义 DataTemplate
。它按需要显示,但是当我尝试 select TreeViewItem
时,如果我单击 TreeView
中的文本,突出显示将不起作用。但是,如果我 select 就在文本的左侧,它会起作用:
来源非常简单,所以我猜我只是缺少样式连接:
xaml
<TreeView x:Name="treeView"
ItemsSource="{Binding TreeViewItems}"
Grid.Row="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<TreeViewItem Header="{Binding Header}">
<TreeViewItem.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding Command}" />
</TreeViewItem.InputBindings>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
我试过将此添加到 xaml,但没有帮助:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}"
BasedOn="{StaticResource {x:Type TreeViewItem}}" />
</TreeView.ItemContainerStyle>
树视图模型
public class TreeViewModel : BaseNotifyModel, ITreeViewModel
{
public TreeViewModel(ITreeViewService menuService)
{
TreeViewItems =
new ReadOnlyObservableCollection<MenuItemViewModel>(menuService.TreeViewMenu);
}
public ReadOnlyObservableCollection<MenuItemViewModel> TreeViewItems
{
get
{
return Get<ReadOnlyObservableCollection<MenuItemViewModel>>();
}
private set
{
Set(value);
}
}
}
MenuItemViewModel
public class MenuItemViewModel : BaseNotifyModel
{
public MenuItemViewModel()
{
MenuItems =
new ObservableCollection<MenuItemViewModel>();
}
public String Header
{
get
{
return Get<String>();
}
set
{
Set(value);
}
}
public ICommand Command
{
get
{
return Get<ICommand>();
}
set
{
Set(value);
}
}
public ObservableCollection<MenuItemViewModel> MenuItems
{
get
{
return Get<ObservableCollection<MenuItemViewModel>>();
}
set
{
Set(value);
}
}
}
TreeView
正在为 ItemsSource
中的每个项目创建一个 TreeViewItem
,所以不要在 TreeView
的项目中嵌套另一个 TreeViewItem
已经为您创建。那没有任何作用。您的模板应该只是为现有的 TreeViewItem
提供一种方式来显示其 DataContext
中的任何内容(在本例中为您的 MenuItemViewModel
)。
您想在树视图项目中显示Header
属性;所以就这样做吧。没什么特别的,只是 Label
或 ContentControl
,如果是 String
,甚至是 TextBlock
(尽管在 WPF 中具有任意内容的灵活性很有趣)。当用户单击内容时,您的命令就会执行。用户在树中唯一能看到的就是内容。这是 TreeViewItem
项唯一可见的部分,因此用户将要单击该部分。
第二个问题:一旦输入绑定得到 LeftClick
,就会中断 TreeView
中的选择。在我看来,你无法通过那种方法从这里到达那里。
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentControl
Content="{Binding Header}"
Background="Transparent"
>
<ContentControl.InputBindings>
<!-- This invokes the command, but breaks selection -->
<MouseBinding MouseAction="LeftClick"
Command="{Binding Command}" />
</ContentControl.InputBindings>
</ContentControl>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
您可以执行以下操作:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentControl
Content="{Binding Header}"
Background="Transparent"
>
</ContentControl>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<EventSetter Event="Selected" Handler="MenuTreeViewItem_Click" />
</Style>
</TreeView.ItemContainerStyle>
代码隐藏
private void MenuTreeViewItem_Click(object sender, RoutedEventArgs e)
{
((MenuItemViewModel)((FrameworkElement)sender).DataContext).Command.Execute(null);
}
There is a way to bind an event to a command in pure XAML,但它需要一些 C# 代码(大声笑)。但我的意思是,它是 "pure XAML",因为它是一个很好的通用可重用附加行为,而不是代码隐藏中难看的事件处理程序。相反,它与我上面所做的完全相同,但它是在代码中完成的,您可以更轻松地转移视线,并且可以在纯 XAML 中重复使用。
在@Ed Plunket 和@Evk 的帮助下,我找到了一个可行的解决方案。我切换到 ContentPresenter
并使用 Interaction.Triggers
在 MouseLeftButtonUp
操作上调用命令。
<TreeView ItemsSource="{Binding TreeViewItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<ContentPresenter Content="{Binding Header}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding Path=DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ContentPresenter>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>