Copied from my WordPress blog. As with everything, this may not be the best way to do things.
Well…it’s been a long time since I last posted anything…but oh well. Today was the first time in a long time I’ve even touched the technology known as WPF (my current “killer app” is completely web-based). So today I started working on a tool to help use manage some of the content in our application and decided to do it in, what else, WPF. A little background on what I was trying to do. Basically I needed to implement a pseudo-Object Relational Mapper for our application. This tool would list our objects in a nested TreeView and then there’d be a property grid (hosted in a WindowsFormsHost control) for editing and creating the object/database properties. Everything was progressing smoothly until I needed to incorporate the TreeView into the app. Here, I ran into a little problem — the TreeView’s SelectedItem Property is readonly (and for good reason) — but I needed to specify the selected item programmatically. After digging around for an hour, I finally figured out how to do it — with only 3 lines of code. I’d read on CodeProject (or whatever it’s called) that it’s impossible…but I said “Not for me…;)”.
The “problem” with the TreeView and all objects that derive from the ItemsControl is that they are binding to object data sources (basically everything gets converted to a type of ObservableCollection) — hence the SelectedItem property points to the object that the TreeView is bound to. But what you see on your screen isn’t that object. It’s a TreeViewItem — a container for that object. It’s basically a wrapper of the object and displays whatever data you’ve specified in your template (or that item’s ToString() method). So even if you could change the SelectedItem programmatically, it would serve no purpose. The UI would not know to update the object that you’ve just specified.
I like to think that WPF is UI-driven…rather than data-driven. The way the TreeView works (and any control derived from ItemsControl), is that the TreeViewItems within the TreeView control signal changes to the underlying bound objects. Basically, the children control the parent. What I mean is that instead of a change in the SelectedItem Property signaling a UI change, a UI change signals the SelectedItem change. The TreeViewItem container responds to some UI change (such as a mouse click, etc) and then lets its parent control know that it’s been selected. From there, the TreeView handles all its SelectedItem logic. And here comes the “kicker” — you don’t have direct access to the TreeViewItem “Selection” method so that you can switch items at will. In fact — the only access we have to the underlying TreeViewItems is through ItemContainerGenerator — we don’t even have a collection of TreeViewItems to enumarate through.
But like I said…you don’t have direct access. Now, this took me a while to figure out…but I thought that others would appreciate my “wisdom”.
Using our little buddy — Reflection…we can force WPF to bend to our will. Now…this way of setting the selected item only works for the TreeView. You have to use different methods for different controls. I’m not going to go into all the detail needed to figure out what needs to be done to replicate this in other controls…just a couple words — .NET Reflector. If you’re a "resourceful" programmer, you’ll figure it out.
EDIT: per Alek Davis.
If you don't need the selection to update UI, you can just set the IsSelected property of the TreeViewItem to true.
So here’s the little method you can use to change the SelectedItem of your TreeView. I made my method static — so you need a reference to your TreeView and the object instance you want to select. That’s it…enjoy the code below.
public static void SetSelectedItem(ref TreeView control, object item)
{
try
{
DependencyObject 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 { }
}