在TreeView中突出显示自定义DataTemplate

时间:2016-10-05 16:58:01

标签: c# wpf xaml treeview highlight

我有一个简单的TreeView,我正在尝试为其创建自定义DataTemplate。它会根据需要显示,但是当我尝试选择TreeViewItem时,如果单击TreeView中的文本,则突出显示不起作用。但是,如果我选择文本的左侧,它可以工作:

TreeView selection issue

消息来源很简单,所以我猜测我只是缺少样式连接:

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>

TreeViewModel

   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);
         }
      }
   }

2 个答案:

答案 0 :(得分:1)

TreeViewTreeViewItem中的每个项目创建了ItemsSource,因此请勿在{{1​​}}内嵌入另一个TreeViewItem TreeView已经为你创造了。这并不是出于任何目的。您的模板应该只是为现有的TreeViewItem提供一种方式来显示其DataContext(您的MenuItemViewModel,在这种情况下)中的任何内容。

您想在树视图项中显示Header属性;所以就这样做吧。没有什么特别的,只有LabelContentControl,甚至只有TextBlock String(虽然它在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#代码(LOL)。但我的意思是,它是纯粹的XAML&#34;从某种意义上说,它是一个很好的通用可重用附加行为,而不是代码隐藏中的难看的事件处理程序。相反,它完全与我上面所做的完全相同,但它在代码中完成,您可以更轻松地避开您的眼睛,并且您可以在纯XAML中重用它。

答案 1 :(得分:0)

在@Ed Plunket和@Evk的帮助下,我找到了一个可行的解决方案。我切换到ContentPresenter并使用Interaction.TriggersMouseLeftButtonUp操作上调用命令。

   <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>