プログラムで WPF の項目を選択するにはどうすればよいTreeView
ですか? モデルはそれItemsControl
を防ぐようです。
16 に答える
この問題の正しい解決策をまだ探している人のために、ここに以下のものがあります。これは、DaWandererによるCodeProjectの記事「WPFTreeViewSelection」http://www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspxへのコメントで見つかりました。それは2008年11月25日にケンレによって投稿されました。これは私にとって素晴らしい働きをしました。ケンレありがとう!
これが彼の投稿です:
ツリーを歩く代わりに、独自のデータオブジェクトにIsSelectedプロパティを持たせます(IsExpandedプロパティもお勧めします)。TreeViewItemからデータオブジェクトにこれらのプロパティをバインドするTreeViewのItemContainerStyleプロパティを使用して、ツリーのTreeViewItemsのスタイルを定義します。このようなもの:
<Style x:Key="LibraryTreeViewItemStyle"
TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight"
Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="FontWeight"
Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
<TreeView ItemsSource="{Binding Path=YourCollection}"
ItemContainerStyle="{StaticResource LibraryTreeViewItemStyle}"
ItemTemplate={StaticResource YourHierarchicalDataTemplate}/>
ContainerFromItem を使用してコンテナーを取得してから、select メソッドを呼び出す必要があります。
// selectedItemObject is not a TreeViewItem, but an item from the collection that
// populated the TreeView.
var tvi = treeView.ItemContainerGenerator.ContainerFromItem(selectedItemObject)
as TreeViewItem;
if (tvi != null)
{
tvi.IsSelected = true;
}
かつてはここでそれを行う方法に関するブログ エントリがありましたが、リンクは現在無効になっています。
TreeViewItem
を取得してから に設定IsSelected
する必要がありますtrue
。
これは見た目ほど単純ではありません。Stevenによって提供されたリンクには2008年に投稿されたソリューションがあり、それでも機能する可能性がありますが、仮想化されたTreeViewは処理されません。さらに、他の多くの問題がその記事のコメントで言及されています。違反はありませんが、私も同じ問題に悩まされており、完璧な解決策を見つけることができません。これが私を大いに助けたいくつかの記事/投稿へのリンクです-
TreeViewでアイテムを展開するにはどうすればよいですか?–パートIII: http: //bea.stollnitz.com/blog/ ?p=59
TreeViewでアイテムをプログラムで選択する:http: //blog.quantumbitdesigns.com/2008/07/22/programmatically-selecting-an-item-in-a-treeview/#respond
TreeView、TreeViewItem、およびIsSelected: http ://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7e368b93-f509-4cd6-88e7-561e8d3246ae/
私は拡張メソッドを書きました:
using System.Windows.Controls;
namespace Extensions
{
public static class TreeViewEx
{
/// <summary>
/// Select specified item in a TreeView
/// </summary>
public static void SelectItem(this TreeView treeView, object item)
{
var tvItem = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (tvItem != null)
{
tvItem.IsSelected = true;
}
}
}
}
私はこのように使用できます:
if (_items.Count > 0)
_treeView.SelectItem(_items[0]);
VisualTreeExt.GetDescendants<T>
指定された型に一致する列挙可能な要素のコレクションを返すメソッドを作成しました。
public static class VisualTreeExt
{
public static IEnumerable<T> GetDescendants<T>(DependencyObject parent) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < count; ++i)
{
// Obtain the child
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T)
yield return (T)child;
// Return all the descendant children
foreach (var subItem in GetDescendants<T>(child))
yield return subItem;
}
}
}
あなたが求めるとき、VisualTreeHelperExt.GetDescendants<TreeViewItem>(MyAmazingTreeView)
あなたはすべてのTreeViewItem
子供を手に入れます。次のコードを使用して、特定の値を選択できます。
var treeViewItem = VisualTreeExt.GetDescendants<TreeViewItem>(MyTreeView).FirstOrDefault(tvi => tvi.DataContext == newValue);
if (treeViewItem != null)
treeViewItem.IsSelected = true;
これは少し汚い解決策であり (おそらく最も効率的ではありません)、実際の視覚要素の存在に依存するため、仮想化された TreeView を使用している場合は機能しません。しかし、それは私の状況では機能します...
次のようなコードビハインドを介して行うことができます
if (TreeView1.Items.Count > 0)
(TreeView1.Items[0] as TreeViewItem).IsSelected = true;
これが誰かを助けることができる場合に備えて、私が行ったソリューションに参加すると思っただけです。これを行う最善の方法は、kuninlの回答に従って「IsSelected」のようなバインドされたプロパティを使用することですが、私の場合、MVVMに従わなかったレガシーアプリケーションだったので、以下のようになりました。
private void ChangeSessionSelection()
{
foreach (SessionContainer item in this.treeActiveSessions.Items)
{
var treeviewItem = this.treeActiveSessions.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (item.Session == this.selectedSession.Session)
{
treeviewItem.IsSelected = true;
treeviewItem.IsExpanded = true;
}
else
{
treeviewItem.IsSelected = false;
treeviewItem.IsExpanded = false;
}
}
}
これが行うことは、コード ビハインドで選択されたデータ項目を表す UI でツリービュー項目を選択して展開することです。これの目的は、ユーザーの選択が同じウィンドウの項目コントロールで変更されたときに、ツリービューで選択を変更することでした。
ええ..質問が出されてから何年も経っていることは知っていますが、..まだこの問題の迅速な解決策はありません..そしてそう:
以下は、OPが要求したことを行います。
私が基本的に行ったことは、このページのすべての回答を読み、関連するすべてのリンクをたどって、この苛立たしい問題に対する完全な解決策を作成することです.
利点:
- TreeView の仮想化もサポートしています。
- ビヘイビア テクニックを使用しているため、XAML は非常に簡単です。
- 依存関係プロパティを追加して、選択した TreeView アイテムへのバインドを許可します。
この部分はコピーする必要がある唯一のコードであり、他の部分は例を完成させるためのものです。
public static class TreeViewSelectedItemExBehavior
{
private static List<TreeView> isRegisteredToSelectionChanged = new List<TreeView>();
public static readonly DependencyProperty SelectedItemExProperty =
DependencyProperty.RegisterAttached("SelectedItemEx",
typeof(object),
typeof(TreeViewSelectedItemExBehavior),
new FrameworkPropertyMetadata(new object(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemExChanged, null));
#region SelectedItemEx
public static object GetSelectedItemEx(TreeView target)
{
return target.GetValue(SelectedItemExProperty);
}
public static void SetSelectedItemEx(TreeView target, object value)
{
target.SetValue(SelectedItemExProperty, value);
var treeViewItemToSelect = GetTreeViewItem(target, value);
if (treeViewItemToSelect == null)
{
if (target.SelectedItem == null)
return;
var treeViewItemToUnSelect = GetTreeViewItem(target, target.SelectedItem);
treeViewItemToUnSelect.IsSelected = false;
}
else
treeViewItemToSelect.IsSelected = true;
}
public static void OnSelectedItemExChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var treeView = depObj as TreeView;
if (treeView == null)
return;
if (!isRegisteredToSelectionChanged.Contains(treeView))
{
treeView.SelectedItemChanged += TreeView_SelectedItemChanged;
isRegisteredToSelectionChanged.Add(treeView);
}
}
#endregion
private static void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var treeView = (TreeView)sender;
SetSelectedItemEx(treeView, e.NewValue);
}
#region Helper Structures & Methods
public class MyVirtualizingStackPanel : VirtualizingStackPanel
{
/// <summary>
/// Publically expose BringIndexIntoView.
/// </summary>
public void BringIntoView(int index)
{
BringIndexIntoView(index);
}
}
/// <summary>Recursively search for an item in this subtree.</summary>
/// <param name="container">The parent ItemsControl. This can be a TreeView or a TreeViewItem.</param>
/// <param name="item">The item to search for.</param>
/// <returns>The TreeViewItem that contains the specified item.</returns>
private static TreeViewItem GetTreeViewItem(ItemsControl container, object item)
{
if (container != null)
{
if (container.DataContext == item)
{
return container as TreeViewItem;
}
// Expand the current container
if (container is TreeViewItem && !((TreeViewItem)container).IsExpanded)
{
container.SetValue(TreeViewItem.IsExpandedProperty, true);
}
// Try to generate the ItemsPresenter and the ItemsPanel.
// by calling ApplyTemplate. Note that in the
// virtualizing case even if the item is marked
// expanded we still need to do this step in order to
// regenerate the visuals because they may have been virtualized away.
container.ApplyTemplate();
ItemsPresenter itemsPresenter =
(ItemsPresenter)container.Template.FindName("ItemsHost", container);
if (itemsPresenter != null)
{
itemsPresenter.ApplyTemplate();
}
else
{
// The Tree template has not named the ItemsPresenter,
// so walk the descendents and find the child.
itemsPresenter = FindVisualChild<ItemsPresenter>(container);
if (itemsPresenter == null)
{
container.UpdateLayout();
itemsPresenter = FindVisualChild<ItemsPresenter>(container);
}
}
Panel itemsHostPanel = (Panel)VisualTreeHelper.GetChild(itemsPresenter, 0);
// Ensure that the generator for this panel has been created.
UIElementCollection children = itemsHostPanel.Children;
MyVirtualizingStackPanel virtualizingPanel =
itemsHostPanel as MyVirtualizingStackPanel;
for (int i = 0, count = container.Items.Count; i < count; i++)
{
TreeViewItem subContainer;
if (virtualizingPanel != null)
{
// Bring the item into view so
// that the container will be generated.
virtualizingPanel.BringIntoView(i);
subContainer =
(TreeViewItem)container.ItemContainerGenerator.
ContainerFromIndex(i);
}
else
{
subContainer =
(TreeViewItem)container.ItemContainerGenerator.
ContainerFromIndex(i);
// Bring the item into view to maintain the
// same behavior as with a virtualizing panel.
subContainer.BringIntoView();
}
if (subContainer != null)
{
// Search the next level for the object.
TreeViewItem resultContainer = GetTreeViewItem(subContainer, item);
if (resultContainer != null)
{
return resultContainer;
}
else
{
// The object is not under this TreeViewItem
// so collapse it.
subContainer.IsExpanded = false;
}
}
}
}
return null;
}
/// <summary>Search for an element of a certain type in the visual tree.</summary>
/// <typeparam name="T">The type of element to find.</typeparam>
/// <param name="visual">The parent element.</param>
/// <returns></returns>
private static T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
T descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
}
return null;
}
#endregion
}
これは、XAML で TreeView 行がどのように見えるかの例です。
<TreeView x:Name="trvwSs"
Grid.Column="2" Grid.Row="1" Margin="4" ItemsSource="{Binding ItemsTreeViewSs}"
behaviors:TreeViewSelectedItemExBehavior.SelectedItemEx="{Binding SelectedItemTreeViewSs}" />
SelectedItemEx にバインドしようとしているビューモデル プロパティが null でないことを確認するだけです。しかし、それは特別なケースではありません..人々が混乱した場合に備えて言及しました.
public class VmMainContainer : INotifyPropertyChanged
{
private object selectedItemTreeViewSs = new object();
private ObservableCollection<object> selectedItemsTreeViewSs = new ObservableCollection<object>();
private ObservableCollection<VmItem> itemsTreeViewSs = new ObservableCollection<VmItem>();
public object SelectedItemTreeViewSs
{
get
{
return selectedItemTreeViewSs;
}
set
{
selectedItemTreeViewSs = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemTreeViewSs)));
}
}
public ObservableCollection<object> SelectedItemsTreeViewSs
{
get
{
return selectedItemsTreeViewSs;
}
set
{
selectedItemsTreeViewSs = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemsTreeViewSs)));
}
}
public ObservableCollection<VmItem> ItemsTreeViewSs
{
get { return itemsTreeViewSs; }
set
{
itemsTreeViewSs = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemsTreeViewSs)));
}
}
}
最後に.. プログラムによる選択の例: MainWindow.xaml とそのハンドラーからボタンを作成しました..
private void Button_Click(object sender, RoutedEventArgs e)
{
TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, trvwSs.Items[3]);
//TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, null);
}
これが誰かを助けることを願っています:)
これで試してください
/// <summary>
/// Selects the tree view item.
/// </summary>
/// <param name="Collection">The collection.</param>
/// <param name="Value">The value.</param>
/// <returns></returns>
private TreeViewItem SelectTreeViewItem(ItemCollection Collection, String Value)
{
if (Collection == null) return null;
foreach(TreeViewItem Item in Collection)
{
/// Find in current
if (Item.Header.Equals(Value))
{
Item.IsSelected = true;
return Item;
}
/// Find in Childs
if (Item.Items != null)
{
TreeViewItem childItem = this.SelectTreeViewItem(Item.Items, Value);
if (childItem != null)
{
Item.IsExpanded = true;
return childItem;
}
}
}
return null;
}
参照: http://amastaneh.blogspot.com/2011/06/wpf-selectedvalue-for-treeview.html