2011-07-05 4 views
2

J'essaie de créer un TreeView qui permet à l'utilisateur de renommer les nœuds dans le TreeView. L'arbre représente un message HL7 et est structuré de segment en sous-composant hiérarchiquement.WPF TreeView SelectedItemChanged ne se déclenche pas

Par exemple:

PID 
    PID.1 
    PID.2 
    etc... 

J'ai besoin pour permettre à l'utilisateur de sélectionner un nœud, appuyez sur F2 pour mettre le noeud en mode édition. Parce que HL7 permet de répéter les structures de message, j'ai aussi besoin de SelectedItem pour savoir quel nœud a été changé au cas où des noms en double existeraient.

Actuellement, chaque nœud est un TextBox avec IsReadOnly défini sur true et est stylisé pour ressembler à un TextBlock. Lorsque l'utilisateur appuie sur F2, je stylise le TextBox pour qu'il ressemble à ce qu'il fait normalement pour l'entrée. Le problème est que TextBox mange tous les événements de souris empêchant TreeView de définir SelectedItem ou d'élever SelectedItemChanged.

J'ai trouvé une discussion sur MSDN où une personne dit utiliser l'événement PreviewMouseLeftButtonDown sur le TextBox. J'utilise cela et le TextBox consomme toujours l'événement.

Est-ce que quelqu'un a rencontré cela auparavant ou a-t-il des suggestions?

+1

Trouvé la solution. Je suis surpris que je n'ai pas été capable de trouver cela n'importe où pour le moment. Quoi qu'il en soit, si vous utilisez la liaison, gérez les événements GotFocus ou PreviewMouseLeftButtonDown et convertissez l'expéditeur en TextBox en objet local. À partir de là, vous pouvez accéder au membre DataContext de l'objet textbox. Cela représentera l'objet de données lié à TreeViewItems. – Josh

Répondre

0

Une autre façon est d'avoir un TextBlock pour l'affichage et un TextBox caché pour l'édition. Écoutez F2 sur le TreeView qui recevra les événements de clavier puisque le TextBox n'obtiendra aucun focus d'entrée tant qu'il est caché. Lorsque vous appuyez sur F2, masquez le TextBlock et affichez le TextBox pour l'éditer. Gérez l'événement LostFocus sur la zone de texte pour masquer la zone de texte et afficher à nouveau le bloc de texte.

Un avantage de le faire de cette façon est que vous n'avez pas faux un TextBox en regardant et se comporter comme un TextBlock. Le TextBlock aura toujours l'apparence et le comportement d'un TextBlock et le TextBox ressemblera toujours à un TextBox, et il pourra hériter de tout style appliqué à un niveau de ressource plus élevé.

Éditer: Ajout d'un exemple de code.

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

Voici le code derrière:

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

Ce code suppose que votre HL7Object est la classe de base pour vos objets de données, telles que les suivantes:

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

Et aussi que vous avez implémenté un DataTemplateSelector, ce que je suppose que vous avez parce que de vos besoins complexes. Sinon, voici un exemple:

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

Malheureusement, cette solution ne fonctionnerait pas dans mon cas. En raison de la complexité de l'imbrication dans l'arbre, nous avons dû placer plusieurs modèles de données hiérarchiques dans les ressources treeview. Je n'ai pas pu trouver le bloc de texte ou la zone de texte, car ils n'étaient pas dans le modèle d'arborescence. Même si nous utilisions un modèle de données hiérarchique dans le modèle item treeview, je préférerais la solution que j'ai trouvée car avoir une zone de texte stylée pour IsReadOnly ressemblant à un TextBlock, je n'ai pas le temps de maintenir un deuxième champ. Je l'accepterai comme une réponse parce que cela fonctionnera pour les autres. – Josh

+0

Alors qu'il serait en fait possible de trouver le bloc de texte/textbox en utilisant treeView.ItemContainerGenerator.ContainerFromItem (treeView.SelectedItem) et en marchant l'arbre visuel avec VisualTreeHelper, avec l'utilisation efficace de WPF Styling et Templating, vous ne devriez pas _find_ les (I ajoutera un exemple de code à la réponse ci-dessus). De même, vous n'avez pas besoin de gérer un second champ, mais simplement de lier les deux contrôles à la même propriété. –

Questions connexes