2009-06-02 11 views
22

私は3レベルのツリービューを持っています。コードから第3レベルのアイテムを選択するにはどうすればよいですか?私は多くのブログやstackoverflowに記載されているメソッドを試してみましたが、それは最初のレベル(dbObjectは最初のレベル以下のアイテムのnull)でしか動作しないようです。コードからTreeViewアイテムを選択する方法

ここは、TreeViewItemを選択するために使用しているコードです。私は何かが恋しいですか?

public static void SetSelectedItem(this TreeView control, object item) 
{ 
    try 
    { 
     var dObject = control.ItemContainerGenerator.ContainerFromItem(item); 

     //uncomment the following line if UI updates are unnecessary 
     ((TreeViewItem)dObject).IsSelected = true; 

     MethodInfo selectMethod = typeof(TreeViewItem).GetMethod("Select", 
      BindingFlags.NonPublic | BindingFlags.Instance); 

     selectMethod.Invoke(dObject, new object[] { true }); 
    } 
    catch { } 
} 
+12

WPFのTreeViewコントロールの犠牲はほとんどの人にはない機能を実現するために、ユーザビリティのすべての*最後のビットを*:それは単純な比較を行う代わりに、EqualityConverterでテストしなければならないので、残念ながらDataTriggersは、その価値をバインドすることはできませんほとんどの時間が必要なようです... –

答えて

31

になってしまったコードです。あなたは(たとえば)各TreeViewItemのテキストを取得するとの結合を使用しているオブジェクトを持っている場合は、あなたもIsSelectedプロパティをバインドするスタイルを作成することができます

<TreeView> 
    <TreeView.Resources> 
     <Style TargetType="TreeViewItem"> 
      <Setter Property="IsSelected" 
        Value="{Binding Path=IsSelected, Mode=TwoWay}" /> 
     </Style> 
    </TreeView.Resources> 
</TreeView> 

をこれは、バインドされたオブジェクトがあることを前提としていboolIsSelectedプロパティ。対応するオブジェクトに対してIsSelectedtrueを設定して、TreeViewItemを選択できます。

TreeViewItemが展開または折りたたまれたときを制御する同じ方法をIsExpandedプロパティで使用できます。

+0

はい、私はこれを知っていました。しかし、それはコード結合を導入するように見えます。 とにかくここにその答えがあることは良いことです。このページに来る人はあなたの方法を好むかもしれません –

+0

@Andy:どうすればこのことができますか?私はこのコードを試してみます '読み取り専用プロパティを設定できません ''。 –

+1

@Navidできるかどうかわからない。私はSilverlightをあまり使っていませんが、私はTreeViewItem.IsSelectedがSilverlightのDependencyPropertyだとは思いません。 DependencyPropertyではないプロパティに対してはバインドを使用できません。 – Andy

0

はい、ContainerFromItemメソッドは、直接の親のTreeViewItemから呼び出すときでも、何も返されません。

再設計を少し行う必要があるかもしれません。明示的なTreeViewItemとしてすべてを作成した場合、そのツリーへの参照を保持し、その上にIsSelectedを設定できる必要があります。

4

異なる解決策を試した後、私はthisサイトに来ました。 Zhou Yongは、TreeViewのすべてのノードをプログラム的に展開する方法を示します。彼のメソッドには2つの主要な考え方があります:

  • ContainerFromItemはitemが要素の直接の子である場合にのみコンテナを返します。 TreeViewでは、最初のレベルの子コンテナのみが返され、次のレベルからコンテナを取得するには、TreeViewItemの子にContainerFromItemを呼び出さなければなりません。
  • ContainerFromItemが動作するためには、TreeViewItemビジュアル子が作成されなければなりません。つまり、TreeViewItemを選択するには、必要な項目の前にあるすべての項目を展開する必要があります。実際には、アイテムの代わりに選択したいアイテムへのパスを提供する必要があることを意味します。ここで

は、私は別のオプションは、バインディングを使用することです

public static void SelectItem(this ItemsControl parentContainer, List<object> path) 
{ 
    var head = path.First(); 
    var tail = path.GetRange(1, path.Count - 1); 
    var itemContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(head) as TreeViewItem; 

    if (itemContainer != null && itemContainer.Items.Count == 0) 
    { 
     itemContainer.IsSelected = true; 

     var selectMethod = typeof(TreeViewItem).GetMethod("Select", BindingFlags.NonPublic | BindingFlags.Instance); 
     selectMethod.Invoke(itemContainer, new object[] { true }); 
    } 
    else if (itemContainer != null) 
    { 
     itemContainer.IsExpanded = true; 

     if (itemContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) 
     { 
      itemContainer.ItemContainerGenerator.StatusChanged += delegate 
      { 
       SelectItem(itemContainer, tail); 
      }; 
     } 
     else 
     { 
      SelectItem(itemContainer, tail); 
     } 
    } 
} 
+0

私は選択したいアイテムを*持っているようです。しかし、通常、そのオブジェクトは利用できません。あなたはちょうどそのオブジェクトのIDを持っていますよね?実際のアイテムインスタンスを持たずに 'ContainerFromItem'を呼び出す方法がわかりません。 –

0

私の場合(私も同じ問題がありましたが、データオブジェクトのIsSelectedプロパティへのバインドを使用するのは不適切でした。また、ツリー項目へのパスを簡単に取得できないため、

private void SelectTreeViewItem(object item) 
    { 
     try 
     { 
      var tvi = GetContainerFromItem(this.MainRegion, item); 

      tvi.Focus(); 
      tvi.IsSelected = true; 

      var selectMethod = 
       typeof(TreeViewItem).GetMethod("Select", 
       System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); 

      selectMethod.Invoke(tvi, new object[] { true }); 
     } 
     catch { } 
    } 

    private TreeViewItem GetContainerFromItem(ItemsControl parent, object item) 
    { 
     var found = parent.ItemContainerGenerator.ContainerFromItem(item); 
     if (found == null) 
     { 
      for (int i = 0; i < parent.Items.Count; i++) 
      { 
       var childContainer = parent.ItemContainerGenerator.ContainerFromIndex(i) as ItemsControl; 
       TreeViewItem childFound = null; 
       if (childContainer != null && childContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) 
       { 
        childContainer.ItemContainerGenerator.StatusChanged += (o, e) => 
         { 
          childFound = GetContainerFromItem(childContainer, item); 
         }; 
       } 
       else 
       { 
        childFound = GetContainerFromItem(childContainer, item);        
       } 
       if (childFound != null) 
        return childFound;     
      } 
     } 
     return found as TreeViewItem; 
    } 
3

あなたは、私は簡単な解決策である見つける以下TreeView拡張子、使用することができます。

public static class TreeViewExtension 
{ 
    public static bool SetSelectedItem(this TreeView treeView, object item) 
    { 
     return SetSelected(treeView, item); 
    } 

    private static bool SetSelected(ItemsControl parent, object child) 
    { 
     if (parent == null || child == null) 
      return false; 

     TreeViewItem childNode = parent.ItemContainerGenerator 
     .ContainerFromItem(child) as TreeViewItem; 

     if (childNode != null) 
     { 
      childNode.Focus(); 
      return childNode.IsSelected = true; 
     } 

     if (parent.Items.Count > 0) 
     { 
      foreach (object childItem in parent.Items) 
      { 
      ItemsControl childControl = parent 
       .ItemContainerGenerator 
       .ContainerFromItem(childItem) 
       as ItemsControl; 

      if (SetSelected(childControl, child)) 
       return true; 
      } 
     } 

     return false; 
    } 
} 

詳細情報については、このブログの記事を読んで。 http://decompile.it/blog/2008/12/11/selecting-an-item-in-a-treeview-in-wpf/

+0

データ構造を変更する必要はなく、効率的ではありません。 –

0

非常に遅れて私の答えを持つが、これはイベントトリガー(ユーザーが新しいアイテムを選択したときにバインディングを更新する)とデータトリガで行うことができ、純粋なMVVMソリューションを望む人のためのパーティーに(更新しますバインディングの値が変更されたときに選択された項目)。

public class MainViewModel : ViewModelBase 
{ 
    // the currently selected node, can be changed programmatically 
    private Node _CurrentNode; 
    public Node CurrentNode 
    { 
     get { return this._CurrentNode; } 
     set { this._CurrentNode = value; RaisePropertyChanged(() => this.CurrentNode); } 
    } 

    // called when the user selects a new node in the tree view 
    public ICommand SelectedNodeChangedCommand { get { return new RelayCommand<Node>(OnSelectedNodeChanged); } } 
    private void OnSelectedNodeChanged(Node node) 
    { 
     this.CurrentNode = node; 
    } 

    // list of items to display in the tree view 
    private ObservableCollection<Node> _Items; 
    public ObservableCollection<Node> Items 
    { 
     get { return this._Items; } 
     set { this._Items = value; RaisePropertyChanged(() => this.Items); } 
    } 
} 

ツリービューは、イベントトリガーが必要です。これは、メインのViewModelに機能するために

は、現在選択されている項目が変更されたときに呼び出される項目、現在選択されている項目のプロパティとコマンドプロパティを必要とします

<TreeView x:Name="treeView" ItemsSource="{Binding Items}" 
      xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
      xmlns:cmd ="http://www.galasoft.ch/mvvmlight"> 
     <TreeView.Resources> 

      <conv:EqualityConverter x:Key="EqualityConverter" /> 

      <Style TargetType="TreeViewItem"> 
       <Setter Property="IsExpanded" Value="True" /> 
       <Setter Property="IsSelected" Value="False" /> 
       <Style.Triggers> 
        <!-- DataTrigger updates TreeViewItem selection when vm code changes CurrentNode --> 
        <DataTrigger Value="True"> 
         <DataTrigger.Binding> 
          <MultiBinding Converter="{StaticResource EqualityConverter}"> 
           <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type TreeView}}" Path="DataContext.CurrentNode" /> 
           <Binding /> 
          </MultiBinding> 
         </DataTrigger.Binding> 
         <Setter Property="IsSelected" Value="True" /> 
        </DataTrigger> 
       </Style.Triggers> 
      </Style> 


      <!-- *** HierarchicalDataTemplates go here *** --> 

     </TreeView.Resources> 

     <!-- EventTrigger invokes SelectedNodeChangedCommand when selection is changed by user interaction --> 
     <i:Interaction.Triggers> 
      <i:EventTrigger EventName="SelectedItemChanged"> 
       <cmd:EventToCommand Command="{Binding SelectedNodeChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=SelectedItem}" /> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 

    </TreeView> 

DataTrig:現ノードの値はコードでプログラム的に変更されたときに、制御項目が選択を取得するようにするときの選択の変更、およびツリービューアイテムのスタイルでDataTrigger SelectedNodeChangedCommandを呼び出すためにgerは、CurrentNodeの値が現在のリスト項目のノードといつ一致するかを検出することによって機能します。

public class EqualityConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     return values[0] == values[1]; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 
関連する問題