2011-07-05 50 views
2

我想创建一个TreeView,它允许用户重命名TreeView中的节点。树表示HL7消息,并且按层次结构从段到子组件。WPF TreeView SelectedItemChanged没有射击

例如:

PID 
    PID.1 
    PID.2 
    etc... 

我需要让用户选择一个节点,按F2把节点进入编辑模式。因为HL7允许重复消息结构,所以我还需要SelectedItem,以便在存在重复名称的情况下知道哪个节点发生了更改。

当前,每个节点都是一个TextBox,其IsReadOnly设置为true,并且风格化看起来像是一个TextBlock。当用户按下F2时,我将TextBox设置为它通常用于输入的样式。问题是,TextBox正在吃掉所有鼠标事件,阻止TreeView设置SelectedItem或提高SelectedItemChanged。

我在MSDN上发现了一些人在TextBox上使用PreviewMouseLeftButtonDown事件的讨论。我正在使用它,TextBox仍在使用该事件。

有没有人遇到过这个或有任何建议?

+1

找到了解决办法。我很惊讶,我还没能在任何地方找到它。 不管怎么说,如果你使用的绑定,处理在GotFocus或事件的PreviewMouseLeftButtonDown,铸发件人为文本框到本地对象。从那里你可以访问文本框对象的DataContext成员。这将表示绑定到TreeViewItems的数据对象。 – Josh

回答

0

另一种方法是有一个TextBlock用于显示和一个用于编辑的隐藏文本框。在TreeView上监听F2,它将接收键盘事件,因为TextBox在隐藏时不会获得任何输入焦点。当按F2时,隐藏TextBlock并显示文本框进行编辑。处理TextBox上的LostFocus事件以隐藏文本框并再次显示TextBlock。

这样做的一个好处是你不必假的一个文本框的外观和行为像一个TextBlock。 TextBlock将总是看起来和行为像一个TextBlock,并且TextBox总是看起来和行为像一个TextBox,并且他们每个都能够继承任何在更高资源级别应用的样式。

编辑:添加一些示例代码。

这里是XAML:

<Window.Resources> 

    <Style x:Key="TreeViewTextBlockStyle" TargetType="TextBlock"> 
     <Setter Property="Text" Value="{Binding DisplayText}"/> 
     <Style.Triggers> 
      <DataTrigger Binding="{Binding InEditMode}" Value="true"> 
       <Setter Property="Visibility" Value="Collapsed"/> 
      </DataTrigger> 
     </Style.Triggers> 
    </Style> 

    <Style x:Key="TreeViewTextBoxStyle" TargetType="TextBox"> 
     <Setter Property="Text" Value="{Binding DisplayText, Mode=TwoWay}"/> 
     <Setter Property="MinWidth" Value="50"/> 
     <EventSetter Event="LostFocus" Handler="TreeViewTextBox_LostFocus" /> 
     <Style.Triggers> 
      <DataTrigger Binding="{Binding InEditMode}" Value="false"> 
       <Setter Property="Visibility" Value="Collapsed"/> 
      </DataTrigger> 
      <DataTrigger Binding="{Binding InEditMode}" Value="true"> 
       <Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Mode=Self}}"/> 
      </DataTrigger> 
     </Style.Triggers> 
    </Style> 

    <HierarchicalDataTemplate x:Key="HL7MessageTemplate" ItemsSource="{Binding Segments}"> 
     <StackPanel Orientation="Horizontal"> 
      <TextBlock Text="[msg]--" FontWeight="Bold"/> 
      <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/> 
      <TextBox Style="{StaticResource TreeViewTextBoxStyle}" /> 
     </StackPanel> 
    </HierarchicalDataTemplate> 

    <DataTemplate x:Key="HL7SegmentTemplate"> 
     <StackPanel Orientation="Horizontal"> 
      <TextBlock Text="[seg]--" FontWeight="Bold"/> 
      <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/> 
      <TextBox Style="{StaticResource TreeViewTextBoxStyle}" /> 
     </StackPanel> 
    </DataTemplate> 

    <HierarchicalDataTemplate x:Key="HL7SegmentWithSubcomponentsTemplate" ItemsSource="{Binding Subcomponents}"> 
     <StackPanel Orientation="Horizontal"> 
      <TextBlock Text="[seg+sub]--" FontWeight="Bold"/> 
      <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/> 
      <TextBox Style="{StaticResource TreeViewTextBoxStyle}" /> 
     </StackPanel> 
    </HierarchicalDataTemplate> 

    <DataTemplate x:Key="HL7SubcomponentTemplate"> 
     <StackPanel Orientation="Horizontal"> 
      <TextBlock Text="[sub]--" FontWeight="Bold"/> 
      <TextBlock Style="{StaticResource TreeViewTextBlockStyle}"/> 
      <TextBox Style="{StaticResource TreeViewTextBoxStyle}" /> 
     </StackPanel> 
    </DataTemplate> 

    <local:HL7DataTemplateSelector x:Key="HL7DataTemplateSelector"/> 

</Window.Resources>  
<Grid> 
    <TreeView Name="treeView1" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource HL7DataTemplateSelector}" KeyUp="treeView1_KeyUp"/> 
</Grid> 

这里是后面的代码:

private void treeView1_KeyUp(object sender, KeyEventArgs e) 
{ 
    if (e.Key == Key.F2) 
    { 
     HL7Object selectedHL7Object = treeView1.SelectedItem as HL7Object; 
     if (selectedHL7Object != null) 
     { 
      selectedHL7Object.InEditMode = true; 
     } 
    } 
} 

private void TreeViewTextBox_LostFocus(object sender, RoutedEventArgs e) 
{ 
    HL7Object selectedHL7Object = treeView1.SelectedItem as HL7Object; 
    if (selectedHL7Object != null) 
    { 
     selectedHL7Object.InEditMode = false; 
    } 
} 

此代码假定您HL7Object是为您的数据对象的基类,如下列:

public class HL7Object : INotifyPropertyChanged 
{ 
    private string DisplayTextField; 
    public string DisplayText 
    { 
     get { return this.DisplayTextField; } 
     set 
     { 
      if (this.DisplayTextField != value) 
      { 
       this.DisplayTextField = value; 
       this.OnPropertyChanged("DisplayText"); 
      } 
     } 
    } 

    private bool InEditModeField = false; 
    public bool InEditMode 
    { 
     get { return this.InEditModeField; } 
     set 
     { 
      if (this.InEditModeField != value) 
      { 
       this.InEditModeField = value; 
       this.OnPropertyChanged("InEditMode"); 
      } 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

此外,你已经实现了DataTemplateSelector,我认为你有,因为您的复杂要求。如果不是,在这里是一个例子:

public class HL7DataTemplateSelector : DataTemplateSelector 
{ 
    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     FrameworkElement element = container as FrameworkElement; 

     if (element != null && item != null && 
      (item is HL7Message || item is HL7Segment || item is HL7Subcomponent) 
      ) 
     { 
      HL7Message message = item as HL7Message; 
      if (message != null) 
      { 
       return element.FindResource("HL7MessageTemplate") as DataTemplate; 
      } 

      HL7Segment segment = item as HL7Segment; 
      if (segment != null) 
      { 
       if (segment.Subcomponents != null && segment.Subcomponents.Count > 0) 
       { 
        return element.FindResource("HL7SegmentWithSubcomponentsTemplate") as DataTemplate; 
       } 
       else 
       { 
        return element.FindResource("HL7SegmentTemplate") as DataTemplate; 
       } 
      } 

      HL7Subcomponent subcomponent = item as HL7Subcomponent; 
      if (subcomponent != null) 
      { 
       return element.FindResource("HL7SubcomponentTemplate") as DataTemplate; 
      } 
     } 

     return null; 
    } 
} 
+0

不幸的是,这种解决方案在我的情况下不起作用。由于在树中嵌套的复杂性,我们必须将多个分层数据模板放入树视图资源。我没能找到文本块或文本框,因为他们在TreeView ItemTemplate中则没有。 即使我们在treeview itemtemplate中使用了一个分层数据模板,我更喜欢我找到的解决方案,因为有一个为IsReadOnly设置样式的文本框看起来像是一个TextBlock,我没有维护第二个字段的开销。我会接受它作为答案,因为它适用于其他人。 – Josh

+0

尽管实际上可以使用treeView.ItemContainerGenerator.ContainerFromItem(treeView.SelectedItem)找到文本块/文本框,并使用VisualTreeHelper漫游可视化树,并且有效使用WPF样式和模板,您不需要_find_它们(I会在上面的答案中添加一些示例代码)。另外,您不需要维护第二个字段,只需将两个控件绑定到同一个属性即可。 –