<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Bea Stollnitz</title>
	
	<link>http://bea.stollnitz.com/blog</link>
	<description>on Silverlight and WPF</description>
	<lastBuildDate>Wed, 01 Jul 2009 16:59:19 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/BeatrizCosta" type="application/rss+xml" /><feedburner:browserFriendly>This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site, subject to copyright and fair use.</feedburner:browserFriendly><item>
		<title>New look</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/BxkzlQZgY9k/</link>
		<comments>http://bea.stollnitz.com/blog/?p=275#comments</comments>
		<pubDate>Tue, 23 Jun 2009 22:24:46 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=275</guid>
		<description><![CDATA[
My blog has a fresh new look! If you use a blog reader, make sure you go to the site to check it out! I&#8217;d love to hear your feedback about it, especially if it doesn&#8217;t work correctly in your favorite browser (unless your favorite browser is IE6&#8230;).


Also, some of you may have noticed that [...]]]></description>
			<content:encoded><![CDATA[<p>
<p>My blog has a fresh new look! If you use a blog reader, make sure you <a href="http://bea.stollnitz.com">go to the site to check it out</a>! I&#8217;d love to hear your feedback about it, especially if it doesn&#8217;t work correctly in your favorite browser (unless your favorite browser is IE6&#8230;).</p>
</p>
<p>
<p>Also, some of you may have noticed that I changed domain name a while ago, from www.beacosta.com to <a href="http://bea.stollnitz.com">bea.stollnitz.com</a>, to reflect my name change. I will keep the old domain name working for a while, but eventually I plan to let it go, so make sure you update any links to my blog with the new domain.</p>
</p>
<p>
<p>I also included my email address in this site (on the top right corner).</p></p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=275</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=275</feedburner:origLink></item>
		<item>
		<title>How can I expand items in a TreeView? – Part III</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/6eagqc32Jas/</link>
		<comments>http://bea.stollnitz.com/blog/?p=59#comments</comments>
		<pubDate>Wed, 31 Dec 2008 03:13:03 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[Silverlight]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=59</guid>
		<description><![CDATA[This is the third of a three-part series about expanding TreeViewItems. In the first post I explained how to use an implicit Style to expand all TreeViewItems at load time. In the second post I showed how you can drive expansion and selection of items using an intermediate data source. In this post, I will [...]]]></description>
			<content:encoded><![CDATA[<p>This is the third of a three-part series about expanding TreeViewItems. In the <a href="http://bea.stollnitz.com/blog/?p=54">first post</a> I explained how to use an implicit Style to expand all TreeViewItems at load time. In the <a href="http://bea.stollnitz.com/blog/?p=55">second post</a> I showed how you can drive expansion and selection of items using an intermediate data source. In this post, I will explain how you can expand and select TreeViewItems using the dispatcher.</p>
<p>Expanding all TreeViewItems by setting the IsExpanded property on the items directly is not as simple as doing a tree walk and marking this property as you go. The problem is that after expanding a TreeViewItem, you need to return control to WPF or Silverlight so that the children TreeViewItems can be instantiated, before it&#8217;s their turn to be expanded. Fortunately, the Dispatcher can be used on both of these technologies to ensure the instantiation of the TreeViewItems.</p>
<h3>WPF</h3>
<p>Those of you who have experience with previous Microsoft technologies may have used the &#8220;DoEvents&#8221; method in the past, which performs a non-blocking wait. WPF doesn&#8217;t ship an equivalent method, but it&#8217;s easy to implement similar behavior using the Dispatcher. I like using a version of this method that takes a DispatcherPriority, so that I have more control over when to resume execution of my code. You can take a look at the code I use below:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;internal static void WaitForPriority(DispatcherPriority priority)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatcherFrame frame = new DispatcherFrame();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(priority, new DispatcherOperationCallback(ExitFrameOperation), frame);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dispatcher.PushFrame(frame);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dispatcherOperation.Status != DispatcherOperationStatus.Completed)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dispatcherOperation.Abort();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private static object ExitFrameOperation(object obj)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((DispatcherFrame)obj).Continue = false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return null;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>In the code above, you can see that I create a new DispatcherFrame and only exit from that frame once dispatcher operations of the specified priority are reached. This way, I will give WPF a chance to execute anything with a priority higher than the one passed as a parameter before continuing executing the next line of code. For example, in the following code I ensure that all jobs with priority higher than Background have been executed by the time I call MyMethod2.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;MyMethod1();<br/>&nbsp;&nbsp;&nbsp;&nbsp;WaitForPriority(DispatcherPriority.Background);<br/>&nbsp;&nbsp;&nbsp;&nbsp;MyMethod2();<br/></p>
<p>If there is other work for the dispatcher to do at the priority specified, it is not guaranteed that that work will happen before starting execution of MyMethod2. Since the &#8220;ExitFrameOperation&#8221; method is BeginInvoked at Background priority in this case, it is possible that other tasks at the same priority will be executed after this one. For this reason, if you want to make sure all operations at Background priority have been executed, you should pass priority ContextIdle instead (the next priority level). </p>
<p>Now that you understand this very useful WaitForPriority method, we can look at how we can use it to expand all items in a TreeView. Expanding the first level of TreeViewItems is easy, but in order to expand the second level of items, we need to make sure that the TreeViewitems are fully instantiated. This can only be achieved by returning control back to WPF just long enough for those items to be instantiated, and then continue execution. This is the perfect job for WaitForPriority.</p>
<p>In the code below, I do a full non-recursive depth-first tree traversal, and for each item I encounter, I set IsExpanded to true and wait in a non-blocking way for the child TreeViewItems to be instantiated. </p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void ExpandAll(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyActionToAllTreeViewItems(itemsControl =&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemsControl.IsExpanded = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatcherHelper.WaitForPriority(DispatcherPriority.ContextIdle);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}, <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;treeView);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void ApplyActionToAllTreeViewItems(Action&lt;TreeViewItem&gt; itemAction, ItemsControl itemsControl)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stack&lt;ItemsControl&gt; itemsControlStack = new Stack&lt;ItemsControl&gt;();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemsControlStack.Push(itemsControl);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (itemsControlStack.Count != 0)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ItemsControl currentItem = itemsControlStack.Pop() as ItemsControl;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem currentTreeViewItem = currentItem as TreeViewItem;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (currentTreeViewItem != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemAction(currentTreeViewItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (currentItem != null) // this handles the scenario where some TreeViewItems are already collapsed<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (object dataItem in currentItem.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ItemsControl childElement = (ItemsControl)currentItem.ItemContainerGenerator.ContainerFromItem(dataItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemsControlStack.Push(childElement);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>My explanation about collapsing TreeViewItems in the previous post is applicable to this scenario too. There are basically two ways you can collapse the item: you can either collapse just the top level items (in which case expanding them again will keep the previous expansion state) or you can collapse every single item in the tree (in which case the previous expansion state is lost).</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void CollapseTopLevel(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (Taxonomy item in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem tvi = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tvi.IsExpanded = false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void CollapseAll(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyActionToAllTreeViewItems(itemsControl =&gt; itemsControl.IsExpanded = false, treeView);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Expanding and selecting one tree node is a bit more complex using the dispatcher than it was using the intermediate data source. In the previous post, I was able to use recursion to look for the item, and as I returned from each level of the recursion, I expanded the item. So I started by expanding the bottom level node, and worked my way to the top. When using the intermediate data source solution this order didn&#8217;t matter because all items were updated in the same layout pass. </p>
<p>However, when interacting with the TreeViewItems directly, I always need to start expanding the nodes from the top and wait for the next level of nodes to be instantiated before proceeding. So the simple algorithm from the previous post won&#8217;t help me here. The solution is to do this in two parts: first I navigate the whole tree using recursion, find the item to expand, and as I return from each level of recursion, I create a collection with the parent hierarchy. Once I have that information, I can now start from the top of the tree and expand each TreeViewItem that corresponds to a data item in the collection. </p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void SelectOne(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ArrayList treeOfLifeCollection = (ArrayList)this.Resources[&quot;treeOfLife&quot;];<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Taxonomy elementToExpand = ((Taxonomy)treeOfLifeCollection[2]).Subclasses[3].Subclasses[0].Subclasses[0].Subclasses[0];<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (Taxonomy firstLevelDataItem in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collection&lt;Taxonomy&gt; superclasses = GetSuperclasses(firstLevelDataItem, elementToExpand);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (superclasses != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Expand superclasses<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem parentTreeViewItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(firstLevelDataItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parentTreeViewItem.IsExpanded = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatcherHelper.WaitForPriority(DispatcherPriority.Background);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (Taxonomy superclassToExpand in superclasses.Skip(1))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem treeViewItemToExpand = (TreeViewItem)parentTreeViewItem.ItemContainerGenerator.ContainerFromItem(superclassToExpand);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;treeViewItemToExpand.IsExpanded = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatcherHelper.WaitForPriority(DispatcherPriority.Background);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parentTreeViewItem = treeViewItemToExpand;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Select node<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem treeViewItemToSelect = (TreeViewItem)parentTreeViewItem.ItemContainerGenerator.ContainerFromItem(elementToExpand);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;treeViewItemToSelect.IsSelected = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private Collection&lt;Taxonomy&gt; GetSuperclasses(Taxonomy currentItem, Taxonomy itemToLookFor)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (itemToLookFor == currentItem)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collection&lt;Taxonomy&gt; results = new Collection&lt;Taxonomy&gt;();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return results;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (Taxonomy subclass in currentItem.Subclasses)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collection&lt;Taxonomy&gt; results = GetSuperclasses(subclass, itemToLookFor);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (results != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results.Insert(0, currentItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return results;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return null;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>If you were able to change your data source, a simpler alternative to this algorithm would be to add parent pointers to each data item. If you had parent pointers, instead of using recursion in the GetSuperclasses method, you could find the parent hierarchy with a simple for loop. If you were not able to change the data source but your data source used an ObservableCollection&lt;T&gt; to store the children of each data item, yet another option would be to add an intermediate data source that listens to collection changes in the original data, and adds parent pointers to the intermediate data when items are added. However, I didn&#8217;t want to provide a solution that assumes you can change your data source, because often you can&#8217;t. And I didn&#8217;t want to assume your source uses ObservableCollection&lt;T&gt; because often it doesn&#8217;t. I am certain that if you have the luxury of parent pointers or collection change notifications, you will be able to write the corresponding code easily.</p>
<p>I&#8217;d provide a link to the running xbap here, but that&#8217;s not possible for this example because DispatcherFrame can&#8217;t be used in the partial-trust security model of an xbap.  Instead, you&#8217;ll have to build the WPF example from the source code provided at the end of this post.</p>
<h3>Silverlight</h3>
<p>The Silverlight version of the Dispatcher solution is quite a bit different from the WPF one. Silverlight does not have DispatcherFrame or DispatcherPriority, so there is no way to write a helper method similar to DoEvents. Fortunately, the Silverlight Dispatcher has a BeginInvoke method that I can use to return control to Silverlight, and allow it to instantiate the next level of TreeViewItems before continuing. Take a look at the code below. By calling MyMethod2 asynchronously, I am ensuring that control is returned to Silverlight before MyMethod2 is executed. This is the technique I will use to allow Silverlight to instantiate the next level of TreeViewItems before I can expand them.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;MyMethod1();<br/>&nbsp;&nbsp;&nbsp;&nbsp;myElement.Dispatcher.BeginInvoke(MyMethod2);<br/></p>
<p>You can see this technique being using to expand all TreeViewItems:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void ExpandAll(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; treeView.Items.Count; i++)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpandAllTreeViewItems((TreeViewItem)treeView.ItemContainerGenerator.ContainerFromIndex(i));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void ExpandAllTreeViewItems(TreeViewItem currentTreeViewItem)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!currentTreeViewItem.IsExpanded)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentTreeViewItem.IsExpanded = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentTreeViewItem.Dispatcher.BeginInvoke(() =&gt; ExpandAllTreeViewItems(currentTreeViewItem));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; currentTreeViewItem.Items.Count; i++)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem child = (TreeViewItem)currentTreeViewItem.ItemContainerGenerator.ContainerFromIndex(i);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpandAllTreeViewItems(child);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Similarly to the previous solutions, I show both how you can collapse all items or just the top level. Since we don&#8217;t have to wait for TreeViewItems to be instantiated when collapsing all items, it is not necessary to use BeginInvoke. Any tree walking algorithm would work.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void CollapseAll(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; treeView.Items.Count; i++)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CollapseAllTreeViewItems((TreeViewItem)treeView.ItemContainerGenerator.ContainerFromIndex(i));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void CollapseAllTreeViewItems(TreeViewItem rootTreeViewItem)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stack&lt;TreeViewItem&gt; treeViewItemsStack = new Stack&lt;TreeViewItem&gt;();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;treeViewItemsStack.Push(rootTreeViewItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (treeViewItemsStack.Count != 0)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem current = treeViewItemsStack.Pop();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current.IsExpanded = false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; current.Items.Count; i++)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;treeViewItemsStack.Push(current.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void CollapseTopLevel(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// This iterates through the three top-level items only.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (Taxonomy item in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem tvi = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tvi.IsExpanded = false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>The code for expanding and selecting one item is also quite a bit different, and a bit more complex. In WPF, once we had all superclasses, we could navigate down the TreeView hierarchy with a for loop, as long as we remembered to return control to WPF after expanding each TreeViewItem. In Silverlight, I had to introduce a new ExpandPathAndSelectLast method that calls itself using BeginInvoke, giving an opportunity for Silverlight to create the next level of TreeViewItems between method calls. I don&#8217;t show the GetSuperclasses method again here, since it&#8217;s the same as the WPF version.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void SelectOne(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectCollection treeOfLifeCollection = (ObjectCollection)this.Resources[&quot;treeOfLife&quot;];<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Taxonomy elementToExpand = ((Taxonomy)treeOfLifeCollection[2]).Subclasses[3].Subclasses[0].Subclasses[0].Subclasses[0];<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (Taxonomy firstLevelDataItem in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collection&lt;Taxonomy&gt; superclasses = GetSuperclasses(firstLevelDataItem, elementToExpand);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (superclasses != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem parentTreeViewItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(firstLevelDataItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpandPathAndSelectLast(parentTreeViewItem, superclasses.Skip(1).GetEnumerator(), elementToExpand);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void ExpandPathAndSelectLast(TreeViewItem currentTreeViewItem, IEnumerator enumerator, object itemToSelect)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!currentTreeViewItem.IsExpanded)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentTreeViewItem.IsExpanded = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentTreeViewItem.Dispatcher.BeginInvoke(() =&gt; ExpandPathAndSelectLast(currentTreeViewItem, enumerator, itemToSelect));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (enumerator.MoveNext())<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;object dataItem = enumerator.Current;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem nextContainer = (TreeViewItem)currentTreeViewItem.ItemContainerGenerator.ContainerFromItem(dataItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpandPathAndSelectLast(nextContainer, enumerator, itemToSelect);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem treeViewItemToSelect = (TreeViewItem)currentTreeViewItem.ItemContainerGenerator.ContainerFromItem(itemToSelect);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;treeViewItemToSelect.IsSelected = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>This is all the code you need to expand and collapse TreeViewItems.</p>
<p>If you have Silverlight 2 installed, you can see a running version of this example <a href="http://bea.stollnitz.com/files/49/TestPage.html" target="_blank">in its own page</a> or embedded below:</p>
<p><center></p>
<p><iframe src="http://bea.stollnitz.com/files/49/TestPage.html" frameborder="0" width="300px" height="585px" scrolling="no"  marginheight="0px" marginwidth="0px"></iframe></p>
<p></center></p>
<p><br/></p>
<h3>Which TreeView expansion solution should I use?</h3>
<p>Now that you know of three ways to expand, collapse and select items in a TreeView, you&#8217;re faced with the decision of which one you should use in your project. Below I show a quick bullet-point list of the pros and cons of each solution. Hopefully this will help you make the right decision.</p>
<h4>Solution 1 &#8211; Expand all TreeViewItems using an implicit style</h4>
<p>Pros:</p>
<ul>
<li>Really really simple to implement.</li>
</ul>
<p>Cons:</p>
<ul>
<li>It is only useful to expand all TreeViewItems at app startup.</li>
</ul>
<h4>Solution 2 &#8211; Expand, collapse and select TreeViewItems using an intermediate data source</h4>
<p>Pros:</p>
<ul>
<li>This is the fastest way to expand items, since they all get expanded in one layout pass. If you are binding your TreeView to a large amount of data, this may be your best option.</li>
</ul>
<p>Cons:</p>
<ul>
<li>If your data source is complex, it may be very hard to write an intermediate data source.</li>
</ul>
<h4>Solution 3 &#8211; Expand, collapse and select TreeViewItems using the Dispatcher</h4>
<p>Pros:</p>
<ul>
<li>It&#8217;s independent of the data source, so you can reuse the code in TreeViews bound to different data sources (in fact, you can even add these as extension methods on TreeView).</li>
<li>If you don&#8217;t have a lot of data, the visual effect of expanding one hierarchy level per layout pass can be fun.</li>
</ul>
<p>Cons:</p>
<ul>
<li>If your data has a deep hierarchy, it will be very slow to expand the items. It will take as many layout passes as levels in the hierarchy.</li>
</ul>
<p>Download the <a href="http://bea.stollnitz.com/files/49/ExpandTreeViewWPF.zip">WPF project</a> (built with .NET 3.5 SP1). </p>
<p>Download the <a href="http://bea.stollnitz.com/files/49/ExpandTreeViewSilverlight.zip">Silverlight project</a> (built with Silverlight 2).</p>
<p><br/></p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=59</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=59</feedburner:origLink></item>
		<item>
		<title>How can I expand items in a TreeView? – Part II</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/xLPmdHE_vKY/</link>
		<comments>http://bea.stollnitz.com/blog/?p=55#comments</comments>
		<pubDate>Fri, 14 Nov 2008 01:00:11 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[Silverlight]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=55</guid>
		<description><![CDATA[For those of you in Europe, I will be giving a talk at the Oredev conference in Malmo, Sweden, on Thursday the 20th of November. I hope I&#8217;ll get to meet some of you there.
&#160;&#160;&#160;&#160;

&#160;&#160;&#160;&#160;
In my last post, I showed how you can expand all items in a TreeView at load time. In WPF, this [...]]]></description>
			<content:encoded><![CDATA[<p>For those of you in Europe, I will be giving a talk at the Oredev conference in Malmo, Sweden, on Thursday the 20th of November. I hope I&#8217;ll get to meet some of you there.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/></p>
<p><img style="DISPLAY: block; MARGIN: 0px auto 10px; TEXT-ALIGN: center" alt="" src="http://bea.stollnitz.com/files/48/Oredev.jpg" border="0" /></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/></p>
<p>In my <a href="http://bea.stollnitz.com/blog/?p=54">last post</a>, I showed how you can expand all items in a TreeView at load time. In WPF, this can be done by simply adding an implicit style to the resources, and in Silverlight we need a little help from ImplicitStyleManager to achieve the same behavior.</p>
<p>However, applications typically allow more complex interaction with a TreeView. In particular, they often permit users to expand all nodes, collapse all nodes, and expand the tree to reveal a particular node. I will show you one way to accomplish these tasks in this post, and a different way in my next post.</p>
<p>For the first approach, I will show you how to add an intermediate data layer to your application that adds UI-specific functionality on top of the data source. The idea of having an intermediate data layer isn&#8217;t new, and is explained in great detail by <a href="http://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx">John Gossman</a> in the context of WPF and <a href="http://www.nikhilk.net/Silverlight-ViewModel-Pattern.aspx">Nikhil Kothari</a> in the context of Silverlight.</p>
<h4>WPF</h4>
<p>I used the same &#8220;Taxonomy&#8221; data source in this post that you may already be familiar with from my previous post or from the Silverlight Toolkit&#8217;s sample pages. </p>
<p>For this sample, I decided that I wanted to allow users to perform three operations on the TreeView: expand all items, collapse all items, and expand just enough items to  select a particular data item. To make these operations possible, I need a data source that has IsExpanded and IsSelected properties in each data item, so that I can data bind the corresponding properties of each TreeViewItem. Since my original data source does not contain these properties, I introduced an intermediate data source (sometimes called a view model) that has the properties I need.</p>
<p>There are a couple of ways to create an intermediate data source: you can either wrap the original data (containment) or you can derive from it (inheritance). Deriving may not be an option, if the data source is sealed or already has subclasses, so I chose the more general solution of containment for this sample. Here&#8217;s what my intermediate data source looks like at this point:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public abstract class TaxonomyViewModel : INotifyPropertyChanged<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public Taxonomy Taxonomy { get; private set; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private bool isExpanded;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public bool IsExpanded<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { return isExpanded; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isExpanded = value;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnPropertyChanged(&quot;IsExpanded&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private bool isSelected;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public bool IsSelected<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { return isSelected; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isSelected = value;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnPropertyChanged(&quot;IsSelected&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Although wrapping is more flexible, it requires forwarding of properties and methods to the actual data items. I exposed the Classification and Rank properties of Taxonomy in the TaxonomyViewModel class, but the Subclasses collection presented a problem: a TaxonomyViewModel has as its &#8220;Subclasses&#8221; other TaxonomyViewModels, but a Taxonomy has &#8220;Subclasses&#8221; of type Taxonomy. For this reason, I had to create a parallel collection of the right type, and I had to make sure that changes to the collection in TaxonomyViewModel are propagated to Taxonomy.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private TaxonomyViewModelCollection subclasses;<br/>&nbsp;&nbsp;&nbsp;&nbsp;public Collection&lt;TaxonomyViewModel&gt; Subclasses { get { return subclasses; } }<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private class TaxonomyViewModelCollection : Collection&lt;TaxonomyViewModel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private Collection&lt;Taxonomy&gt; originalCollection;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public TaxonomyViewModelCollection(Collection&lt;Taxonomy&gt; originalCollection)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.originalCollection = originalCollection;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected override void InsertItem(int index, TaxonomyViewModel item)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base.InsertItem(index, item);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;originalCollection.Insert(index, item.Taxonomy);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>My next step was to add a method that expands all nodes. This method needs to traverse the whole hierarchy of data and set IsExpanded to true on all nodes. Any tree traversal algorithm would work &#8211; I chose to use a Stack to do a non-recursive depth-first traversal.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public void ExpandAll()<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyActionToAllItems(item =&gt; item.IsExpanded = true);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void ApplyActionToAllItems(Action&lt;TaxonomyViewModel&gt; itemAction)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stack&lt;TaxonomyViewModel&gt; dataItemStack = new Stack&lt;TaxonomyViewModel&gt;();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataItemStack.Push(this);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (dataItemStack.Count != 0)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TaxonomyViewModel currentItem = dataItemStack.Pop();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemAction(currentItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (TaxonomyViewModel childItem in currentItem.Subclasses)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataItemStack.Push(childItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Now that the code for traversing the tree is already in place, collapsing all TreeViewItems can be done in one line. The result of setting IsExpanded to false in all nodes will have effect in the same layout pass (since I don&#8217;t return control to Silverlight during the tree traversal), so the order in which the IsExpanded properties are set does not matter. All items in the TreeView will collapse at the same time, in one layout pass. </p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public void CollapseAll()<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyActionToAllItems(item =&gt; item.IsExpanded = false);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>And finally, I added a method that, given a data item, expands all the items in its ancestor chain. This method uses recursion to search for the data item passed as a parameter. Once the item is found, its  ancestor chain gets expanded as the recursive call stack unwinds.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public bool ExpandSuperclasses(TaxonomyViewModel itemToLookFor)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return ApplyActionToSuperclasses(itemToLookFor, superclass =&gt; superclass.IsExpanded = true);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private bool ApplyActionToSuperclasses(TaxonomyViewModel itemToLookFor, Action&lt;TaxonomyViewModel&gt; itemAction)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (itemToLookFor == this)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (TaxonomyViewModel subclass in this.Subclasses)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool foundItem = subclass.ApplyActionToSuperclasses(itemToLookFor, itemAction);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (foundItem)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemAction(this);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>And that&#8217;s all for the intermediate data source. Now we need to hook this up to the UI. I started by adding three buttons that call the methods I just wrote. Because a TreeView can actually contain multiple trees, my button event handlers iterate over all the root items, calling the appropriate method.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;collections:ArrayList x:Key=&quot;treeOfLife&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;local:DomainViewModel Classification=&quot;Bacteria&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/local:DomainViewModel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;local:DomainViewModel Classification=&quot;Archaea&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/local:DomainViewModel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;local:DomainViewModel Classification=&quot;Eukarya&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/local:DomainViewModel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/collections:ArrayList&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void ExpandAll(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (TaxonomyViewModel item in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item.ExpandAll();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void SelectOne(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ArrayList treeOfLifeCollection = (ArrayList)this.Resources[&quot;treeOfLife&quot;];<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TaxonomyViewModel elementToExpand = (TaxonomyViewModel)((TaxonomyViewModel)treeOfLifeCollection[2]).Subclasses[3].Subclasses[0].Subclasses[0].Subclasses[0];<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (TaxonomyViewModel item in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (item.ExpandSuperclasses(elementToExpand))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elementToExpand.IsSelected = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>The CollapseAll scenario is a bit of a special case. There are really two options for collapsing all items:</p>
<ul>
<li>You can set IsExpanded to false on every TreeViewItem. The CollapseAll method I showed earlier can be used in this case. If you pick this option, any previous item expansion is forgotten once you collapse all items. This means that if you expand a few items, collapse all, and expand one top level item, the previous item expansion will not be restored.</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void CollapseAll(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (TaxonomyViewModel item in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item.CollapseAll();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<ul>
<li>The other option is to collapse only the top level items. If you pick this option, previous expansions will be remembered and restored after collapsing all items.</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void CollapseTopLevel(object sender, RoutedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (TaxonomyViewModel item in treeView.Items)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeViewItem tvi = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tvi.IsExpanded = false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>And last, I need to bind the TreeViewItem&#8217;s IsExpanded and IsSelected properties to the corresponding properties in my intermediate data source. At first sight, it may seem that the following XAML would work well:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Style TargetType=&quot;TreeViewItem&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Setter Property=&quot;IsExpanded&quot; Value=&quot;{Binding Path=IsExpanded}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Setter Property=&quot;IsSelected&quot; Value=&quot;{Binding Path=IsSelected}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Style&gt;<br/></p>
<p>If you try this, however, you will notice that once you collapse a TreeViewItem manually by clicking on it,  the Expand All button will no longer affect that item. That&#8217;s because when interacting directly with the UI, the IsExpanded property is set explicit, overwriting the binding. The solution is to make the Bindings two-way. As you would expect, two-way Bindings are not lost when the target value is set, they simply propagate the value back to the source.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Style TargetType=&quot;TreeViewItem&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Setter Property=&quot;IsExpanded&quot; Value=&quot;{Binding Path=IsExpanded, Mode=TwoWay}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Setter Property=&quot;IsSelected&quot; Value=&quot;{Binding Path=IsSelected, Mode=TwoWay}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Style&gt;<br/></p>
<p>If you have WPF installed on your machine, you can <a href="http://bea.stollnitz.com/files/48/ExpandTreeViewWPF.xbap" target="_blank" >click here</a> to see this code running as an xbap. I also link to the source at the end of this post.</p>
<h4>Silverlight</h4>
<p>Most of the code and XAML I showed for WPF works in Silverlight too, with the exception of the Binding in the Setter&#8217;s Value, since Silverlight currently doesn&#8217;t support that feature. In order to work around this limitation, I created custom TreeView and TreeViewItem classes that derive from the Toolkit classes and override GetContainerForItemOverride. This method is called to create each TreeViewItem container, so I was able to include the Bindings through code at the moment these containers are created.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public class MyTreeView : TreeView<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected override DependencyObject GetContainerForItemOverride()<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyTreeViewItem tvi = new MyTreeViewItem();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Binding expandedBinding = new Binding(&quot;IsExpanded&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expandedBinding.Mode = BindingMode.TwoWay;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tvi.SetBinding(MyTreeViewItem.IsExpandedProperty, expandedBinding);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Binding selectedBinding = new Binding(&quot;IsSelected&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selectedBinding.Mode = BindingMode.TwoWay;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tvi.SetBinding(MyTreeViewItem.IsSelectedProperty, selectedBinding);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return tvi;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Also, unfortunately we have a bug in the Toolkit TreeView  that occasionally causes more than one item to appear selected. Hopefully we&#8217;ll get that fixed for the next release.</p>
<p>If you have Silverlight 2 installed, you can see a running version of this example <a href="http://bea.stollnitz.com/files/48/TestPage.html" target="_blank">in its own page</a>, or embedded below:</p>
<p><center></p>
<p><iframe src="http://bea.stollnitz.com/files/48/TestPage.html" frameborder="0" width="300px" height="585px" scrolling="no"  marginheight="0px" marginwidth="0px"></iframe></p>
<p></center></p>
<p>And that&#8217;s all for today. In my next post, I will discuss a third way of expanding, collapsing  and selecting TreeViewItems.</p>
<p>Download the <a href="http://bea.stollnitz.com/files/48/ExpandTreeViewWPF.zip">WPF project</a> (built with .NET 3.5 SP1). </p>
<p>Download the <a href="http://bea.stollnitz.com/files/48/ExpandTreeViewSilverlight.zip">Silverlight project</a> (built with Silverlight 2).</p>
<p><br/></p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=55</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=55</feedburner:origLink></item>
		<item>
		<title>How can I expand items in a TreeView? – Part I</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/1vM9F1b91Ek/</link>
		<comments>http://bea.stollnitz.com/blog/?p=54#comments</comments>
		<pubDate>Wed, 29 Oct 2008 22:32:02 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[Silverlight]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=54</guid>
		<description><![CDATA[Exciting!
Yesterday, the new Silverlight Toolkit (the team I now work for) was made available in CodePlex. This was announced by Scott Guthrie in his PDC keynote and by my manager Shawn Burke in his blog.
We created the Silverlight Toolkit to provide developers with a set of reusable components that  maximize productivity and help take [...]]]></description>
			<content:encoded><![CDATA[<p>Exciting!</p>
<p>Yesterday, the new Silverlight Toolkit (the team I now work for) was made available in CodePlex. This was announced by <a href="http://weblogs.asp.net/scottgu/">Scott Guthrie</a> in his <a href="http://www.microsoftpdc.com/">PDC</a> keynote and by my manager <a href="http://blogs.msdn.com/sburke/">Shawn Burke in his blog</a>.</p>
<p>We created the Silverlight Toolkit to provide developers with a set of reusable components that  maximize productivity and help take Silverlight to the next level. The toolkit is available under the <a href="http://www.microsoft.com/opensource/licenses.mspx#Ms-PL">Microsoft Public License</a>, so we provide the full source code (as well as tests and samples), and encourage you to reuse our code in your applications.</p>
<p>If you want to know more about it, I recommend that you check out our <a href="http://www.codeplex.com/Silverlight">web site</a>, <a href="http://www.codeplex.com/Silverlight/Release/ProjectReleases.aspx?ReleaseId=18804">download the toolkit</a>, and post questions in the <a href="http://silverlight.net/forums/35.aspx">Silverlight controls forum</a>.  My team just went through a resource-constrained, short, crazy release, where everyone on the team went beyond at so many levels, so it feels amazing to get these bits out to customers. </p>
<p><br/></p>
<h3>Expanding items in a TreeView</h3>
<p>Among many other controls, we shipped a Silverlight TreeView, nearly identical to the WPF one. This is the first of three blog posts in which I am going to discuss different ways of expanding the items in a TreeView. These blog posts will cover both WPF and Silverlight, and point out the differences between the two. I&#8217;m hoping this will be especially useful to those of you who are familiar with one technology and interested in learning the other.</p>
<p>I have encountered customers in the past who had implemented complex solutions to expand all items of a TreeView at load time (which is a pretty common scenario). This scenario is actually really easy to accomplish &#8211; you can simply add an implicit Style for TreeViewItem that sets IsExpanded to true. </p>
<h3>WPF</h3>
<p>In WPF, this can be done by adding an implicit style to the resources of your page:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Page.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;collections:ArrayList x:Key=&quot;treeOfLife&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;local:Domain Classification=&quot;Bacteria&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;local:Kingdom Classification=&quot;Eubacteria&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/local:Domain&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/collections:ArrayList&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Style TargetType=&quot;TreeViewItem&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Setter Property=&quot;IsExpanded&quot; Value=&quot;True&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Style&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Page.Resources&gt;<br/></p>
<p>        <TreeView ItemsSource="{StaticResource treeOfLife}" ItemTemplate="{StaticResource treeOfLifeTemplate}"/></p>
<p>Couldn&#8217;t be any easier. </p>
<p>If you have WPF installed on your machine, you can <a href="http://bea.stollnitz.com/files/47/ExpandTreeViewWPF.xbap" target="_blank" >click here</a> to see this code running as an xbap. I also link to the source at the end of this post.</p>
<h3>Silverlight</h3>
<p>Now let&#8217;s look at the differences between the WPF solution and the corresponding Silverlight one.</p>
<p>You may have noticed that I used an ArrayList to store my data source in the WPF sample.  Usually I would prefer to use a strongly typed generic collection, but for this example I chose ArrayList because the WPF XAML parser does not yet support generic types. In Silverlight, the ArrayList type is not available for customer use (it&#8217;s internal), so I used an ObjectCollection instead. ObjectCollection is a new collection type we ship in the Silverlight toolkit exactly for the purpose of defining collections in XAML. Here is its source code:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public partial class ObjectCollection : Collection&lt;object&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public ObjectCollection()<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public ObjectCollection(IEnumerable collection)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (object obj in collection)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Add(obj);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>In this case I don&#8217;t need collection change notifications, so deriving from Collection&lt;object&gt; works well for me, without adding the overhead of ObservableCollection&lt;T&gt;. If you need collection change notifications, you can use the same technique and simply replace the base class with ObservableCollection&lt;T&gt;.</p>
<p>The Style for the TreeViewItem looks really similar to the WPF one. However, if you try this in Silverlight, you will notice that the style doesn&#8217;t get applied. </p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;UserControl.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Style TargetType=&quot;controls:TreeViewItem&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Setter Property=&quot;IsExpanded&quot; Value=&quot;True&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Style&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/UserControl.Resources&gt;<br/></p>
<p>This is because Silverlight doesn&#8217;t have support for implicit styles. When using the Silverlight runtime, you need to give each style a key and refer to it explicitly using StaticResource. Because the TreeViewItems are generated automatically by the TreeView, it would be pretty hard to specify an explicit style for each one.</p>
<p>Fortunately, the Silverlight Toolkit contains a solution to this problem that provides a behavior similar to WPF&#8217;s implicit styles. You can use the &#8220;ImplicitStyleManager&#8221; class, and in particular its ApplyMode attached property:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;controls:TreeView ItemsSource=&quot;{StaticResource treeOfLife}&quot; ItemTemplate=&quot;{StaticResource treeOfLifeTemplate}&quot; theming:ImplicitStyleManager.ApplyMode=&quot;Auto&quot;/&gt;<br/></p>
<p>And now the implicit style is applied and the TreeView expands all items on load! If you have Silverlight 2 installed, you can see a running version of this example <a href="http://bea.stollnitz.com/files/47/TestPage.html" target="_blank">in its own page</a>, or embedded below:</p>
<p><center></p>
<p><iframe src="http://bea.stollnitz.com/files/47/TestPage.html" frameborder="0" width="300px" height="545px" scrolling="no"  marginheight="0px" marginwidth="0px"></iframe></p>
<p></center></p>
<h3>ImplicitStyleManager</h3>
<p>ImplicitStyleManager only affects elements in the subtree rooted at the element where ApplyMode is set. In the sample above, implicit styles are only applied to the TreeView  itself and its descendants in the visual tree (TreeViewItems, etc.). However, when searching for styles to use, ImplicitStyleManager looks within resource dictionaries in the entire name scope of the element where ApplyMode is set, as well as in the Application&#8217;s resources (just like in WPF). In this sample, it looks for implicit styles anywhere within the current UserControl (each UserControl defines a name scope), and in the Application&#8217;s resources.</p>
<p>You&#8217;ve already seen how to use ImplicitStyleManager in a simple scenario above, but this class provides some additional functionality. ISM (as we lovingly call it) has three different modes, which can be specified through its ApplyMode property:</p>
<h4>Auto</h4>
<p>ISM reapplies implicit styles every time layout is updated. So, if elements are added later to the visual tree (like the TreeViewItems in this sample), they will be implicitly styled. </p>
<p>Keep in mind that the LayoutUpdated event fires quite frequently (not just when elements are added to the visual tree), and walking the whole visual tree may be an expensive operation if you have a large tree. Unfortunately there is no &#8220;ItemAddedToTree&#8221; event that we could listen to, so we had to compromise on performance  to offer the convenience of this mode.</p>
<p>In this post&#8217;s sample, the visual tree is small and very dynamic, so it makes sense to use this mode. But if your tree is relatively static or very large, you should consider using the OneTime mode instead.</p>
<h4>OneTime</h4>
<p>In this mode, ISM applies the implicit styles to the elements in the visual tree at load time. If new elements get added later, they will not be styled. In this blog&#8217;s sample, the generated TreeViewItems are not all instantiated at load time, so using ApplyMode=OneTime wouldn&#8217;t work.</p>
<p>Sometimes you may have a scenario where new items are added to the tree occasionally, but your tree is large enough that you would rather not use the Auto mode. For these situations, you can set ApplyMode to OneTime, and call ISM&#8217;s &#8220;Apply&#8221; method through code every time you want to reapply the styles. This provides extra flexibility while avoiding the performance cost of the Auto mode.</p>
<h4>None</h4>
<p>This mode is exactly the same as not attaching the ApplyMode property. It does not prevent styles from being propagated to the subtree where this property is set, as some people may expect. Since WPF doesn&#8217;t permit you to disable implicit styling within a subtree, ImplicitStyleManager doesn&#8217;t either.</p>
<p>There is more to ImplicitStyleManager than what I&#8217;ve explained here, so stay tuned for future posts.</p>
<p>Download the <a href="http://bea.stollnitz.com/files/47/ExpandTreeViewWPF.zip">WPF project</a> (built with .NET 3.5 SP1). </p>
<p>Download the <a href="http://bea.stollnitz.com/files/47/ExpandTreeViewSilverlight.zip">Silverlight project</a> (built with Silverlight 2).</p>
<p><br/></p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=54</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=54</feedburner:origLink></item>
		<item>
		<title>How can I drag and drop items between data bound ItemsControls?</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/-001YaE1fJc/</link>
		<comments>http://bea.stollnitz.com/blog/?p=53#comments</comments>
		<pubDate>Fri, 22 Feb 2008 11:40:42 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=53</guid>
		<description><![CDATA[In this blog post, I will explain how I implemented a sample that allows you to drag and drop items between data bound ItemsControls. Imagine  you have two or more data bound (or not data bound) ItemsControls in the same Window. You may want to drag an item from an ItemsControl and drop it [...]]]></description>
			<content:encoded><![CDATA[<p>In this blog post, I will explain how I implemented a sample that allows you to drag and drop items between data bound ItemsControls. Imagine  you have two or more data bound (or not data bound) ItemsControls in the same Window. You may want to drag an item from an ItemsControl and drop it on another one. Or maybe you want to drag and drop it in a different position within the same ItemsControl. The code in this blog post allows you to do those operations.</p>
<p><br/></p>
<h3>Use this code</h3>
<p>You don&#8217;t need to understand the implementation details of this sample to take advantage of its functionality. If you want to use this code in your app, you should copy three files from the project at the end of the post &#8211; DraggedAdorner.cs, InsertionAdorner.cs and DragDropHelper.cs. You may want to change the namespace in these files. Then, within your app, you simply need to add three attached properties:</p>
<ul>
<li>IsDropTarget should be set on the ItemsControl that  you want to drag items to. This property is of type boolean and should be set to true. Don&#8217;t forget to add a namespace mapping on the top window that maps to the namespace you chose for DragDropHelper.</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Window &#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:local=&quot;clr-namespace:DragDropListBox&quot; &#8230; /&gt;<br/></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;ItemsControl &#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;local:DragDropHelper.IsDropTarget=&quot;true&quot;  &#8230;/&gt;<br/></p>
<ul>
<li>IsDragSource should be set on the ItemsControl that you want to drag items from. This property should also be set to true.</li>
</ul>
<ul>
<li>DragDropTemplate should also be set on the drag source ItemsControl (where you want to drag the items from). This property should be set to a DataTemplate that controls how the data item should be  displayed while it is dragged around. If you want it to be displayed exactly the same way as in the source ItemsControl, the DragDropTemplate and ItemTemplate properties can share the same template.</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;ItemsControl  &#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;local:DragDropHelper.IsDragSource=&quot;true&quot; local:DragDropHelper.DragDropTemplate=&quot;{StaticResource pictureTemplate}&quot;/&gt;<br/></p>
<p>And that&#8217;s all there is to it. Next I will show the list of features and limitations of this solution.</p>
<h4>Features</h4>
<ul>
<li>The code in this sample enables the user to drag and drop between ItemsControls and any controls that derive from it (see exception for TreeView in the limitations section).</li>
<li>This solution supports dragging and dropping within the same ItemsControl.</li>
<li>This solution supports dragging and dropping between non data bound and data bound ItemsControls.</li>
<li>I&#8217;ve added support for ItemsControls that use FlowPanel and StackPanel (with both horizontal and vertical orientation). Support for other panels can be added easily.</li>
<li>This sample prevents the user from dropping a data item into a target ItemsControl when its data items are of an incompatible type. For example, if the drag source ItemsControl contains a collection of Potatoes but the drop target ItemsControl is data bound to a collection of Squirrels, the app will prevent the user from doing the drop.</li>
<li>This sample provides visual feedback of where the dragged item will go if dropped. For example, when dragging a data item over a ListBox, this sample shows a horizontal line between the two items where the item will go if dropped.</li>
</ul>
<h4>Limitations</h4>
<ul>
<li>This solution doesn&#8217;t drag and drop data items across Windows.</li>
<li>It also doesn&#8217;t work across Applications.</li>
<li>It allows drag and drop to work between ItemsControls, but it does not allow you to drag or drop to a  ContentControl.</li>
<li>This sample works as expected for the first level of a TreeView, but not for nested levels of the hierarchy.</li>
</ul>
<p><br/></p>
<h3>Overview</h3>
<p>To implement a drag and drop operation, we need to attach handlers for several events on the drag source and drop target elements. In this scenario, I decided that I am going to support only ItemsControls as the source and target of the drag and drop operation. All the events that I need to provide handlers for are public, so I could have easily done that in the Window1.xaml.cs file. However, I wanted an easily reusable solution that would allow all the drag-and-drop-specific code to be abstracted in one place. I thought of three different solutions to this problem (although I&#8217;m sure there are more):</p>
<ul>
<li>I could derive from ItemsControl and override &#8220;OnPreviewMouseLeftButtonDown&#8221; and all the other &#8220;On&#8221; methods that correspond to the events I care about. I don&#8217;t like this solution so much because users would have to replace their ItemsControls with my custom &#8220;DragAndDropItemsControl&#8221;.</li>
</ul>
<ul>
<li>I could create a DragHelper class that takes an instance of the drag source ItemsControl as a parameter, and a DropHelper class that takes the drop target. The only disadvantage of this solution is that it needs to be set up from code.</li>
</ul>
<ul>
<li>Instead, I decided to create a DragDropHelper class that defines properties to be attached to the drag source and drop target IsDragSource and IsDropTarget. The main advantages of this solution are that it can be implemented entirely in XAML, and that the XAML changes are minimal (you only need to set these properties). I&#8217;ll explain below how this works.</li>
</ul>
<p>These two attached properties are of type boolean, and should be attached to the drag source and drop target. When they&#8217;re set to false, dragging/dropping is not allowed, otherwise it is. If your application requires drag and drop operations to be allowed sometimes but not other times, you can simply change the properties on the fly.</p>
<p>When registering the attached properties, I made sure I passed change handlers called IsDragSourceChanged and IsDropTargetChanged to the UIPropertyMetadata. These methods are responsible for adding and removing handlers for the desired events, depending on the value of the attached property. I will show here the code for IsDragSourceChanged (the code for the target is similar):</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private static void IsDragSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var dragSource = obj as ItemsControl;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dragSource != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (Object.Equals(e.NewValue, true))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dragSource.PreviewMouseLeftButtonDown += Instance.DragSource_PreviewMouseLeftButtonDown;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dragSource.PreviewMouseLeftButtonUp += Instance.DragSource_PreviewMouseLeftButtonUp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dragSource.PreviewMouseMove += Instance.DragSource_PreviewMouseMove;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dragSource.PreviewMouseLeftButtonDown -= Instance.DragSource_PreviewMouseLeftButtonDown;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dragSource.PreviewMouseLeftButtonUp -= Instance.DragSource_PreviewMouseLeftButtonUp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dragSource.PreviewMouseMove -= Instance.DragSource_PreviewMouseMove;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>I describe below the events that I provided handlers for. I had to use the preview versions of these events to make sure I handle them before other controls do (for example, ListBox handles MouseLeftButtonDown).</p>
<h4>ItemsControl Drag Source</h4>
<ul>
<li>PreviewMouseLeftButtonDown Its main responsibility is to remember the data item that the user clicked on.</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;this.sourceItemContainer = Utilities.GetItemContainer(this.sourceItemsControl, visual);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (this.sourceItemContainer != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.draggedData = this.sourceItemContainer.DataContext;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<ul>
<li>PreviewMouseMove For a drag to happen, the user needs to click on the drag source and move the mouse by a significant amount. When a user performs a button click, there is a pretty high likelihood that the mouse will move inadvertently by a small amount, and I want to ignore those situations. Fortunately, there is a SystemParameter that can be used in these situations, so I make sure that the mouse has moved by at least SystemParameters.MinimumHorizontalDragDistance  or SystemParameters.MinimumVerticalDragDistance before I consider it a drag. Once I decide a drag has started, I initiate the drag-and-drop operation. Notice below that I am initiating the drag operation with the data that I determined in the PreviewMouseLeftButtonDown handler (draggedData).</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;DataObject data = new DataObject(this.format.Name, this.draggedData);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;DragDropEffects effects = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move);<br/></p>
<ul>
<li>PreviewMouseLeftButtonUp This happens when the user releases the mouse without initiating a drag-and-drop operation. In this case, I simply set the draggedData field to null.</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;this.draggedData = null;<br/></p>
<h4>ItemsControl Drop Target</h4>
<ul>
<li>PreviewDragEnter This event fires every time the user first  drags a data item into an element with AllowsDrop=true (I set this property on the drop target ItemsControl within the IsDropTargetChanged  method, so you don&#8217;t have to). Since this property is inherited, this handler will be called every time the mouse enters any element that belongs to the visual tree of the drop target ItemsControl. Among other things, within this handler I have code that updates the two adorners that I will explain later in this post.</li>
</ul>
<ul>
<li>PreviewDragOver This event fires when the user drags the data item over elements with AllowsDrop=true. Similarly to the previous handler, the adorners are updated in this handler.</li>
</ul>
<ul>
<li>PreviewDragLeave As expected, this event fires when the mouse leaves an element that has AllowsDrop=true. Here, I remove one of the adorners. More details on the adorners later.</li>
</ul>
<ul>
<li>PreviewDrop This event notifies us that the drop operation has actually occurred. Here, I remove the data item from the drag source ItemsControl&#8217;s collection and add it to the drop target.</li>
</ul>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;object movedItem = e.Data.GetData(this.format.Name);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;indexRemoved = Utilities.RemoveItemFromItemsControl(this.sourceItemsControl, movedItem);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Utilities.InsertItemInItemsControl(this.targetItemsControl, movedItem, this.insertionIndex);<br/></p>
<p><br/></p>
<h3>Dragging and dropping data</h3>
<p>There are several problems that are particular to dragging and dropping data items between ItemsControls. I will discuss a few of those next.</p>
<p>When dragging a data item over the target ItemsControl, I need to determine where the item will go if dropped. I broke down this scenario into three possible different situations:</p>
<ul>
<li>The mouse is over an empty ItemsControl. In this case, if the ItemsControl is data bound to an empty collection the item gets added as the first and only item of that collection; similarly, if the ItemsControl is empty and not data bound, the item is added to the Items collection of the ItemsControl.</li>
</ul>
<ul>
<li>The ItemsControl has items, although the mouse is over the empty part of the ItemsControl, after the last item. In this case, the item should be added to the end of the ItemsSource or Items collection.</li>
</ul>
<ul>
<li>Last, the mouse could be over an item container. In this case, I need to determine whether I should insert the dragged data before or after the item under the mouse.  If the ItemsControl uses a panel with vertical orientation, I check to see whether the mouse is over the top half or bottom half of the item container. Similarly, if the panel is laid out horizontally, I check to see whether the mouse is over the left half or right half of the item container.</li>
</ul>
<p>This leads me to the next topic: I had to store the orientation of the panel so that I could calculate the insertion location when dropping over an item container. This calculation is done in the following method:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public static bool HasVerticalOrientation(FrameworkElement itemContainer)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool hasVerticalOrientation = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (itemContainer != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Panel panel = VisualTreeHelper.GetParent(itemContainer) as Panel;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StackPanel stackPanel;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WrapPanel wrapPanel;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((stackPanel = panel as StackPanel) != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hasVerticalOrientation = (stackPanel.Orientation == Orientation.Vertical);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if ((wrapPanel = panel as WrapPanel) != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hasVerticalOrientation = (wrapPanel.Orientation == Orientation.Vertical);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// You can add support for more panel types here.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return hasVerticalOrientation;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Notice that I am providing support only for StackPanel and WrapPanel here, but you can easily add more &#8220;if&#8221; statements to support other panels.</p>
<p>Once I have the panel&#8217;s orientation information, I can determine whether the dragged data should be inserted before or after the item container under the mouse. In the code below, &#8220;first half&#8221; means the left half for a horizontally oriented panel, and top half for a vertically oriented one.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public static bool IsInFirstHalf(FrameworkElement container, Point clickedPoint, bool hasVerticalOrientation)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (hasVerticalOrientation)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return clickedPoint.Y &lt; container.ActualHeight / 2;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return clickedPoint.X &lt; container.ActualWidth / 2;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Finally, I wanted to give the user feedback when a certain data item type can not be added to the drop target collection. To achieve this, I wrote a method called IsDropDataTypeAllowed, from which the following code snippet was taken. The drop operation is allowed if the target collection is of type IList<T> where the dragged type can be assigned to type T, or if the target collection is of type IList, or if the drop target is not data bound. If none of the above is true, the cursor will change to a slashed circle to indicate that a drop is not allowed in that location.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;Type draggedType = draggedItem.GetType();<br/>&nbsp;&nbsp;&nbsp;&nbsp;Type collectionType = collectionSource.GetType();<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Type genericIListType = collectionType.GetInterface(&quot;IList`1&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (genericIListType != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Type[] genericArguments = genericIListType.GetGenericArguments();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isDropDataTypeAllowed = genericArguments[0].IsAssignableFrom(draggedType);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;else if (typeof(IList).IsAssignableFrom(collectionType))<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isDropDataTypeAllowed = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isDropDataTypeAllowed = false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p><br/></p>
<h3>Dragged Adorner</h3>
<p>As I mentioned earlier, the DragDropHelper class contains a DragDropTemplate attached property that should be set to the DataTemplate responsible for determining the look of the dragged data item. The DraggedAdorner&#8217;s main responsibility is to provide a representation for the dragged data using that DataTemplate. To achieve this behavior, I decided that the adorner&#8217;s only child should be a ContentPresenter with its Content set to the dragged data, and its ContentTemplate set to the DataTemplate specified in the DragDropTemplate attached property. I also decided to give the adorner a bit of transparency, so I set the Opacity of the ContentPresenter to 0.7. Here is the constructor for this adorner:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public DraggedAdorner(object dragDropData, DataTemplate dragDropTemplate, UIElement adornedElement, AdornerLayer adornerLayer)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: base(adornedElement)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.adornerLayer = adornerLayer;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.contentPresenter = new ContentPresenter();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.contentPresenter.Content = dragDropData;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.contentPresenter.ContentTemplate = dragDropTemplate;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.contentPresenter.Opacity = 0.7;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.adornerLayer.Add(this);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>And here is what this adorner looks like, with the template I specified in this blog&#8217;s project:</p>
<p><img src="http://bea.stollnitz.com/files/46/DraggedAdorner.png" class="postImage" /></p>
<p>There&#8217;s a bit more logic in this adorner class, such as the code that allows the adorner to follow the position of the mouse and the code that sets up the ContentPresenter as the adorner&#8217;s child visual.</p>
<p>Next I will explain briefly when this adorner is created, removed, and updated.</p>
<p>The DraggedAdorner is created:</p>
<ul>
<li>When the user first drags data into an element belonging to the drop target DropTarget_PreviewDragEnter. This event is fired every time we enter a new element in the target&#8217;s tree, but we only need to create the Dragged Adorner the first time.</li>
<li>Every time we re-enter the Window TopWindow_DragEnter.</li>
</ul>
<p>The DraggedAdorner is removed:</p>
<ul>
<li>When dropping the item in a valid area DropTarget_PreviewDrop.</li>
<li>When leaving the window TopWindow_DragLeave.</li>
<li>If I drop the item in an invalid area, the drop handler doesn&#8217;t get called but the Window&#8217;s leave handler does, so this scenario is covered.</li>
</ul>
<p>The DraggedAdorner is updated:</p>
<ul>
<li>Every time the cursor moves over the drop target or anywhere else within the window. DropTarget_PreviewDragOver, TopWindow_DragOver.</li>
</ul>
<p>In summary, I create the adorner once, the first time it is needed. I remove the adorner every time the mouse leaves the window and recreate it when the mouse re-enters the window. And I remove the adorner again at the end of the drag-and-drop operation.</p>
<p><br/></p>
<h3>Insertion Adorner</h3>
<p>The insertion adorner is responsible for the visual feedback that indicates the insertion point of a dropped data item. For example, if you drag a data item over a ListBox, you will see a line between two of the items indicating the position where the dragged item will be inserted if dropped. The visual appearance of an insertion adorner is a line with two triangles facing inwards at the ends:</p>
<p><img src="http://bea.stollnitz.com/files/46/InsertionAdorner.png" class="postImage" /></p>
<p>In order to make this adorner as performant as possible, I wrote a static constructor where I create a triangle and freeze all Freezable objects involved in its creation:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;static InsertionAdorner()<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pen = new Pen { Brush = Brushes.Gray, Thickness = 2 };<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pen.Freeze();<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LineSegment firstLine = new LineSegment(new Point(0, -5), false);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstLine.Freeze();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LineSegment secondLine = new LineSegment(new Point(0, 5), false);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secondLine.Freeze();<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PathFigure figure = new PathFigure { StartPoint = new Point(5, 0) };<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;figure.Segments.Add(firstLine);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;figure.Segments.Add(secondLine);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;figure.Freeze();<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;triangle = new PathGeometry();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;triangle.Figures.Add(figure);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;triangle.Freeze();<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>I need two triangles in this adorner. Because their positions and orientations vary according to the ItemsControl&#8217;s layout, I created just one triangle object and draw it twice after applying the appropriate translate and rotate transforms. The method below is called twice with different parameters:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void DrawTriangle(DrawingContext drawingContext, Point origin, double angle)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;drawingContext.PushTransform(new TranslateTransform(origin.X, origin.Y));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;drawingContext.PushTransform(new RotateTransform(angle));<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;drawingContext.DrawGeometry(pen.Brush, null, triangle);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;drawingContext.Pop();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;drawingContext.Pop();<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>Next I will explain briefly when this adorner is created, removed, and updated.</p>
<p>The InsertionAdorner is created:</p>
<ul>
<li>Every time the cursor enters a new element in the target&#8217;s tree. It needs to be created this frequently because different elements can have different sizes DropTarget_PreviewDragEnter.</li>
</ul>
<p>The InsertionAdorner is removed:</p>
<ul>
<li>Every time the cursor leaves the current element or the item is dropped DropTarget_PreviewDragLeave, DropTarget_PreviewDrop.</li>
</ul>
<p>The InsertionAdorner is updated:</p>
<ul>
<li>Every time the cursor moves over the drop target DropTarget_PreviewDragOver.</li>
</ul>
<p><br/></p>
<h3>Windows events</h3>
<p>The fact that I&#8217;m providing handlers for the window&#8217;s drag drop events may seem a little strange at first. </p>
<p>Imagine I provide handlers for the ItemsControl drop target only. Since I have code in these handlers that displays the preview adorner (which is where DragDropTemplate gets applied), the preview would only come up when the mouse cursor enters the drop target. Anywhere else in the window, the mouse cursor would turn into a crossed circle, indicating that it&#8217;s not an allowed drop location.</p>
<p>I like the crossed circle to come up in those areas, but I would like the adorner to be displayed anywhere I drag the item within the window.</p>
<p>To achieve the effect I wanted, I set the AllowDrop property of the window to true, and hooked up handlers for its drag events. I used the preview versions of the drop events for the target, but not for the window. This way, when the cursor is within the drop target I get the drop target events (and set Handled to true), but when the cursor is anywhere else in the window, I get the window&#8217;s events instead. I used these events to ensure the creation and removal of the preview dragged adorner, and to make sure that the mouse cursor is set to the crossed circle.</p>
<p><br/></p>
<h3>Other similar solutions available</h3>
<p>Other developers have blogged about similar drag-and-drop solutions in the past. I will link to some of those solutions here, although there are probably more that I haven&#8217;t come across. </p>
<p>Pavan Podila has a <a href="http://blog.pixelingene.com/?p=43">great series of four blog posts</a> about a similar scenario, which I highly recommend reading. The main difference between our two solutions is that his code can be used to drag UI elements from one panel to another, while my solution is used to drag data from one ItemsControl to another. I was very glad to see that Pavan Podila is also a fan of the attached properties solution for extending functionality.</p>
<p>Jaime Rodriguez also talked about this topic in a great <a href="http://blogs.gotdotnet.com/jaimer/archive/2007/07/12/drag-drop-in-wpf-explained-end-to-end.aspx">three part series blog post</a>. As in this post, he provides a solution for dragging and dropping data items.</p>
<p>If you need drag-and-drop functionality in your app, you will probably end up using a combination of the features from many blog posts. I hope you find my solution useful!</p>
<p><a href="http://bea.stollnitz.com/files/46/DragDropListBox.zip">Here</a> you can find the project with this code built using VS 2008 RTM.</p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=53</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=53</feedburner:origLink></item>
		<item>
		<title>How can I debug WPF bindings?</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/ds80FK4WmLM/</link>
		<comments>http://bea.stollnitz.com/blog/?p=52#comments</comments>
		<pubDate>Mon, 28 Jan 2008 06:02:29 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=52</guid>
		<description><![CDATA[Data Binding can be tricky to debug. In this post, I will share the techniques I use to debug WPF bindings, including the new debugging improvements we implemented in the latest 3.5 release.  I will discuss the following four techniques:

Scanning Visual Studio&#8217;s Output window for errors.
Using Trace Sources for maximum control.
Using the new property [...]]]></description>
			<content:encoded><![CDATA[<p>Data Binding can be tricky to debug. In this post, I will share the techniques I use to debug WPF bindings, including the new debugging improvements we implemented in the latest 3.5 release.  I will discuss the following four techniques:</p>
<ul>
<li>Scanning Visual Studio&#8217;s Output window for errors.</li>
<li>Using Trace Sources for maximum control.</li>
<li>Using the new property introduced in WPF 3.5 PresentationTraceSources.TraceLevel.</li>
<li>Adding a breakpoint to a Converter.</li>
</ul>
<p>The DataContext of this app is set to a new instance of Star, which is a class that contains two properties: Picture and Caption. Picture is of type BitmapImage and contains an image of the sun, which I included in the project. Caption is a string property that takes a while to be initialized (more details about this later).</p>
<p><br/></p>
<h3>Output Window</h3>
<p>In the XAML of this application, I have an Image whose Source is data bound to an incorrect property name. This is a simple scenario that causes a Binding to fail.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Image Source=&quot;{Binding Path=PictureWrong}&quot; Width=&quot;300&quot; Height=&quot;300&quot; Grid.Row=&quot;0&quot;/&gt;<br/></p>
<p>Every time a Binding fails, the binding engine prints an informative message in the Output window of Visual Studio. In this case, I get the following message:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;System.Windows.Data Error: 35 : BindingExpression path error: &#8216;PictureWrong&#8217; property not found on &#8216;object&#8217; &#8221;Star&#8217; (HashCode=49535530)&#8217;. BindingExpression:Path=PictureWrong; DataItem=&#8217;Star&#8217; (HashCode=49535530); target element is &#8216;Image&#8217; (Name=&#8221;); target property is &#8216;Source&#8217; (type &#8216;ImageSource&#8217;)<br/></p>
<p>This message should give you enough information to quickly figure out the mistake in the property name. </p>
<p>Advantage of this technique:</p>
<ul>
<li>It is very easy to look at the Output window, and in most cases it&#8217;s sufficient. It should be the first approach you consider when you have a problem with your bindings.</li>
</ul>
<p>Disadvantage of this technique:</p>
<ul>
<li>Most real world applications print so much information to the Output window that it may be hard to find the error you’re looking for.</li>
</ul>
<p><br/></p>
<h3> Trace Sources</h3>
<p>The Trace Sources solution was already around in WPF 3.0. It adds a lot more flexibility to the previous solution by allowing you to control the level of detail you care about, where you want that messages to be printed and the WPF feature you want to debug. </p>
<p>The Trace Sources solution relies on a config file for the configuration of its behavior &#8211; the App.config file.  Here is a portion of the contents of that file:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;configuration&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;system.diagnostics&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;sources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;      <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;source name=&quot;System.Windows.Data&quot; switchName=&quot;SourceSwitch&quot; &gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;listeners&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;add name=&quot;textListener&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/listeners&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/source&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;    <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;switches&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;add name=&quot;SourceSwitch&quot; value=&quot;All&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/switches&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;sharedListeners&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;add name=&quot;textListener&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type=&quot;System.Diagnostics.TextWriterTraceListener&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initializeData=&quot;DebugTrace.txt&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/sharedListeners&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;trace autoflush=&quot;true&quot; indentsize=&quot;4&quot;&gt;&lt;/trace&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/system.diagnostics&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/configuration&gt;<br/></p>
<p>In this file, I am specifying that:</p>
<ul>
<li>I want only messages generated in the Data subarea to be printed. If you&#8217;re trying to debug, for example, animations, you would instead add the area System.Windows.Media.Animation.</li>
<li>I want as much information as possible about data binding. This was done by setting the switch to All. Other possible values are Off, Error, Warning. For a complete list, look up SourceLevels in .NET Reflector.</li>
<li>I want the messages to be printed to an external file called DebugTrace.txt, instead of the Output Window. This file will be created in the bindebug folder for the application. If you run this application twice, the messages generated the second time will be appended to the existing messages in this file. If you don&#8217;t want this behavior, remember to delete the file before running the app.</li>
</ul>
<p>Other pre-defined listeners allow printing to the Console (ConsoleTraceListener), or to an external file in XML format (XmlWriterTraceListener).</p>
<p>If you run the application with the settings above, you should find a DebugTrace.txt file in the bindebug directory. If you open it, you will see the data binding error we saw previously in the Output Window, plus four &#8220;Information&#8221; messages. These lower-priority messages are printed because I specified in the switch that I want all the information available about the bindings. </p>
<p>If you want to learn more about this topic, I recommend <a href="http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx">Mike Hillberg&#8217;s blog</a>. He wrote the best article I&#8217;ve read about Trace Sources, which I use frequently as a reference.</p>
<p>Advantages of this technique:</p>
<ul>
<li>It separates the debug messages you care about from the rest of the information printed in the Output window.</li>
<li>This solution may help you debug other areas in WPF, not just binding.</li>
<li>You can get lower-priority messages (such as information or warnings) that are not typically printed to the Output window.</li>
</ul>
<p>Disadvantages of this technique:</p>
<ul>
<li>The text file (or whatever form of output you choose) will contain debug messages about all the bindings in your application. Although this is not as much clutter as the Output window, it may still require some digging for you to find exactly the information you need.</li>
<li>It won&#8217;t help you in scenarios where your Binding actually succeeds, but you still don&#8217;t see what you expect in the UI. The first and second techniques I show here only help in scenarios where the Binding fails.</li>
</ul>
<p>Before you move on, make sure you correct the Path in the Image&#8217;s Source so that these errors won&#8217;t interfere with the ones I show next.</p>
<p><br/></p>
<h3>Trace Level &#8211; new in 3.5</h3>
<p>In order to understand this feature, let&#8217;s start by uncommenting the first TextBlock in the XAML of this application:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Caption}&quot; &#8230; /&gt;<br/></p>
<p>This TextBlock attempts to bind to Caption, a property whose value is slow to be initialized. In this case, I am simulating a slow data source by adding a Dispatcher timer to the constructor, but in reality this delay could have many different causes. Notice also that I am *not* raising a property changed notification when Caption changes value. </p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public string Caption<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private set<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.caption= value;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//OnPropertyChanged(&quot;Caption&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return caption;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;public Star()<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.Picture = new BitmapImage(new Uri(&quot;Images\sun.jpg&quot;, UriKind.Relative));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.Caption = String.Empty;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatcherTimer timer = new DispatcherTimer();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.Interval = new TimeSpan(0, 0, 3);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.Tick += new EventHandler(Timer_Tick);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.Start();<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void Timer_Tick(object sender, EventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.Caption = &quot;Sun&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>In this scenario, the Binding will succeed (it will bind to the initial value of empty string), but you don&#8217;t see what you expect to see in the UI. The unwanted behavior is caused by the fact that the events happen in an order different from what you expect: the Binding will be evaluated before Caption gets its real value.  In this case, there are no errors in the Output window or DebugTrace.txt because the Binding succeeds. </p>
<p>You can use the new debugging feature in 3.5 to debug this scenario by adding the attached property PresentationTraceSources.TraceLevel to the Binding, which can be set to None, Low, Medium and High. Since PresentationTraceSource is not in the default namespace mappings for WPF, you will have to write the following XAML:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Window &#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;xmlns:diagnostics=&quot;clr-namespace:System.Diagnostics;assembly=WindowsBase&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Caption, diagnostics:PresentationTraceSources.TraceLevel=High}&quot; &#8230; /&gt;<br/></p>
<p>If you look at the Output window, you will notice that the binding engine generated debug information for every task that may help users find problems with this particular binding:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;System.Windows.Data Warning: 47 : Created BindingExpression (hash=25209742) for Binding (hash=3888474)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;System.Windows.Data Warning: 91 : BindingExpression (hash=25209742): GetValue at level 0 from Star (hash=31609076) using RuntimePropertyInfo(Caption): &#8216; &#8216;<br/>&nbsp;&nbsp;&nbsp;&nbsp;System.Windows.Data Warning: 71 : BindingExpression (hash=25209742): TransferValue &#8211; got raw value &#8216; &#8216;<br/>&nbsp;&nbsp;&nbsp;&nbsp;System.Windows.Data Warning: 78 : BindingExpression (hash=25209742): TransferValue &#8211; using final value &#8216; &#8216;<br/></p>
<p>In this case, we can scan the debug messages quickly to see that there were no errors, and look at the last few messages to understand that the value found at the Source was the empty string. This information will help you come to the conclusion that there is some timing issue with the scenario.</p>
<p>Advantages of this technique:</p>
<ul>
<li>This feature is particularly useful when you know exactly which binding you want to find out more about, which is the most common scenario.</li>
<li>It allows you to know more about bindings that succeed, which many times helps you find the mistake in your logic.</li>
</ul>
<p>Disadvantages of this technique:</p>
<ul>
<li>If you forget to remove the property after you found the problem, it adds to the clutter of the Output Window (which adds to the time it takes to start your application in debug mode).</li>
</ul>
<p><br/></p>
<h3>Converter</h3>
<p>The last solution is extremely simple: you can simply add a no-op Converter to your binding and put a breakpoint in its Convert method. This is what this solution looks like:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Caption, Converter={StaticResource converter}}&quot; &#8230;/&gt;<br/></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public class DebuggingConverter : IValueConverter<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return value; // Add the breakpoint here!!<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new NotImplementedException(&quot;This method should never be called&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>If you run this code and set a breakpoint inside the Convert method, you can simply hover over the &#8220;value&#8221; parameter to see what is being passed from the source to the target. In this case, you will see that &#8220;value&#8221; contains an empty string. This may help you realize that the source of your binding does not have the value you thought it had.</p>
<p>Advantages of this technique:</p>
<ul>
<li>It is really easy to implement. It relies on a concept that most data binding users are familiar with.</li>
<li>It does not depend on the Output window.</li>
<li>It helps you find out more about scenarios where the binding doesn&#8217;t fail.</li>
</ul>
<p>Disadvantages to this technique:</p>
<ul>
<li>It doesn&#8217;t provide as much information as the TraceLevel technique.</li>
<li>If the Binding fails early, the Converter may not be called.</li>
</ul>
<p><br/></p>
<h3>Conclusion</h3>
<p>I mentioned here techniques to debug WPF bindings using Visual Studio, however there are other tools that can help you with this process. One other tool I use frequently is <a href="http://blois.us/blog/2006/08/long-time-since-my-last-post-but-dont_21.html">Snoop</a>, not only to debug bindings but many other aspects of my application. <a href="http://karlshifflett.wordpress.com/mole-for-visual-studio/">Mole</a> is also useful, and has great documentation. </p>
<p>What technique or tool you use to debug your bindings comes down to a combination of personal preference and your specific scenario.</p>
<p><a href="http://bea.stollnitz.com/files/45/DebuggingDataBinding.zip">Here</a> you can find the project with this code built using VS 2008 RTM.</p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=52</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=52</feedburner:origLink></item>
		<item>
		<title>How can I implement the Master-Detail pattern when binding to XLinq and XML?</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/jtVwsHEjP4E/</link>
		<comments>http://bea.stollnitz.com/blog/?p=51#comments</comments>
		<pubDate>Mon, 14 Jan 2008 04:54:30 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=51</guid>
		<description><![CDATA[I have explained in previous posts how to implement:

A two-level Master-Detail scenario using objects as data source here.
A three level Master-Detail scenario using objects as data source here
A three level Master-Detail scenario using ADO.NET here.  The two-level version of this scenario with ADO.NET should be obvious based on the samples above.

In this post, I [...]]]></description>
			<content:encoded><![CDATA[<p>I have explained in previous posts how to implement:</p>
<ul>
<li>A two-level Master-Detail scenario using objects as data source <a href="http://bea.stollnitz.com/blog/?p=13">here</a>.</li>
<li>A three level Master-Detail scenario using objects as data source <a href="http://bea.stollnitz.com/blog/?p=14">here</a></li>
<li>A three level Master-Detail scenario using ADO.NET <a href="http://bea.stollnitz.com/blog/?p=21">here</a>.  The two-level version of this scenario with ADO.NET should be obvious based on the samples above.</li>
</ul>
<p>In this post, I will explain how to implement a two and three level Master-Detail when binding to XLinq and XML. </p>
<p><br/></p>
<h3>Two level Master-Detail with XML</h3>
<p>Here is the XML data source I used for this scenario:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;XmlDataProvider x:Key=&quot;PlanetsXDP&quot; XPath=&quot;SolarSystemPlanets/Planet&quot; Source=&quot;SolarSystemPlanets.xml&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!&#8211; In file SolarSystemPlanets.xml &#8211;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;SolarSystemPlanets&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Planet Name=&quot;Mercury&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Orbit&gt;57,910,000 km (0.38 AU)&lt;/Orbit&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Diameter&gt;4,880 km&lt;/Diameter&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Mass&gt;3.30e23 kg&lt;/Mass&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Details&gt;The small and rocky planet Mercury is the closest planet to the Sun.&lt;/Details&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Planet&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/SolarSystemPlanets&gt;<br/></p>
<p>The two level Master-Detail scenario works very similarly regardless of the data source. The following code and XAML make up the core of this scenario, when using XML as a data source:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Grid Margin=&quot;30&quot; Name=&quot;XML2Level&quot; DataContext=&quot;{Binding Source={StaticResource PlanetsXDP}}&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding}&quot;  Name=&quot;MasterListBox2&quot; ItemTemplate=&quot;{StaticResource XMLMasterPlanetTemplate}&quot; IsSynchronizedWithCurrentItem=&quot;True&quot; Width=&quot;100&quot; &#8230; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ContentControl Name=&quot;Detail2&quot; Content=&quot;{Binding}&quot; ContentTemplate=&quot;{StaticResource XMLDetailPlanetTemplate}&quot; &#8230; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Grid&gt;<br/></p>
<p>Notice that both the ListBox and ContentControl elements have the exact same empty Binding as content. This is possible because, as I explained in a <a href="http://bea.stollnitz.com/blog/?p=13">previous post</a>, the data binding engine detects that the ContentControl can not display a list of items, and grabs the current item of that list instead. This is true for every data source type.</p>
<p><br/></p>
<h3>Two level Master-Detail with XLinq</h3>
<p>I started by creating an XElement by loading the same XML file shown above, and used it as the data source for the XLinq scenario. In my <a href="http://bea.stollnitz.com/blog/?p=50">last blog post</a> I loaded the XML in code, so this time I show how to do this in XAML, using ObjectDataProvider.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;ObjectDataProvider ObjectType=&quot;{x:Type xlinq:XElement}&quot; MethodName=&quot;Load&quot; x:Key=&quot;PlanetsODP&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ObjectDataProvider.MethodParameters&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;system:String&gt;../../SolarSystemPlanets.xml&lt;/system:String&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ObjectDataProvider.MethodParameters&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ObjectDataProvider&gt;<br/></p>
<p>You can see below that the only somewhat interesting difference between the XLinq and XML scenarios is the Path of the DataContext Binding. In this scenario, we need to drill into the &#8220;Elements&#8221; property of the root XElement, in order to obtain a list of Planet elements. Since we now have a collection as the DataContext, we can use a simple empty Binding on the ListBox and ContentControl to get the Master-Detail pattern to work.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Grid Margin=&quot;30&quot; Name=&quot;XLinq2Level&quot; DataContext=&quot;{Binding Source={StaticResource PlanetsODP}, Path=Elements}&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding}&quot; Name=&quot;MasterListBox1&quot; ItemTemplate=&quot;{StaticResource XLinqMasterPlanetTemplate}&quot; IsSynchronizedWithCurrentItem=&quot;True&quot; &#8230; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ContentControl Name=&quot;Detail1&quot; Content=&quot;{Binding}&quot; ContentTemplate=&quot;{StaticResource XLinqDetailPlanetTemplate}&quot; &#8230; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Grid&gt;<br/></p>
<p><br/></p>
<h3>Three level Master-Detail with XML</h3>
<p>Here is the XML data source I used for this scenario :</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;XmlDataProvider x:Key=&quot;MountainsXDP&quot; XPath=&quot;Mountains/Mountain&quot; Source=&quot;Mountains.xml&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!&#8211; In file Mountains.xml &#8211;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Mountains &gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Mountain Name=&quot;Whistler&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Lifts&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Lift Name=&quot;Big Red Express&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Runs&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Run&gt;Headwall&lt;/Run&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Run&gt;Fisheye&lt;/Run&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Run&gt;Jimmy&#8217;s Joker&lt;/Run&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Runs&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Lift&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Lifts&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Mountain&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Mountains&gt;<br/></p>
<p>The three level Master-Detail with XML is a little trickier than the two level version of the same pattern. Here is the code and XAML necessary to implement this scenario:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Grid Margin=&quot;30&quot; Name=&quot;XML3Level&quot; DataContext=&quot;{Binding Source={StaticResource MountainsXDP}}&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding}&quot; Name=&quot;MasterListBox4&quot; ItemTemplate=&quot;{StaticResource XMLMountainTemplate}&quot; IsSynchronizedWithCurrentItem=&quot;True&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding XPath=Lifts/Lift}&quot; ItemTemplate=&quot;{StaticResource XMLLiftTemplate}&quot; IsSynchronizedWithCurrentItem=&quot;True&quot; Name=&quot;MiddleListBox4&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox Name=&quot;Detail4&quot; DataContext=&quot;{Binding ElementName=MiddleListBox4, Path=SelectedItem}&quot; ItemsSource=&quot;{Binding XPath=Runs/Run}&quot; ItemTemplate=&quot;{StaticResource XMLRunTemplate}&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Grid&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/></p>
<p>The first ListBox simply binds to the collection of Mountains (notice the XPath in the XmlDataProvider). </p>
<p>The second ListBox drills into the collection of Lifts of each Mountain. This is implemented internally by executing an XPath query on the instance of the Moutains collection shown in the first ListBox, with the syntax found in the XPath property. The two collections can be kept in sync because the second collection instance is a subcollection of the first.</p>
<p>If you&#8217;ve seen my post on <a href="http://bea.stollnitz.com/blog/?p=14">three level Master-Detail when binding to objects</a>, you probably expect the following syntax to work for the third ListBox (a similar syntax works when binding to objects):</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox &#8230; ItemsSource=&quot;{Binding XPath=Lifts/Lift/Runs/Run}&quot; &#8230; /&gt;<br/></p>
<p>If you try this on the third ListBox, you might be surprised by the results. The third ListBox shows all the runs of <b>all the lifts</b> of the selected mountain, instead of showing all the runs of <b>the selected lift</b> of the selected mountain. That&#8217;s because when you use this syntax, the binding engine executes a new XPath query on the DataContext, which happens to be the collection of mountains displayed in the first list box. The XPath query can&#8217;t be executed on a collection, so the binding engine executes the query on the current item of the collection &#8211; whichever mountain is selected in the first ListBox. The result of the query is completely independent of the current selection in the second ListBox.</p>
<p>The only way to grab instances of items from the collection in the second ListBox is by binding to the ListBox itself. This is why the DataContext of the third ListBox is bound to the SelectedItem of the second ListBox in the working solution. </p>
<p>You&#8217;re probably wondering why I&#8217;m binding to the DataContext and the ItemsSource of the third ListBox, instead of using the ItemsSource property only. This is what the Binding would look like, if I used the ItemsSource only:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox &#8230; ItemsSource=&quot;{Binding ElementName=MiddleListBox4, Path=SelectedItem, XPath=Runs/Run}&quot; &#8230; /&gt;<br/></p>
<p>As I explained in <a href="http://bea.stollnitz.com/blog/?p=50">my previous post</a>, it&#8217;s actually possible (and sometimes very useful) to have a Path and XPath in the same Binding. Keep in mind, however, that whenever XPath and Path are both specified, the XPath is always executed before the Path, independently of the order in which you specify them in the Binding. We decided on this order of execution because this feature is used mostly to drill into properties of XmlElement (such as HasAttributes, InnerXml or Name). In the Master-Detail sample, we need to first get the SelectedItem (which is an XmlElement), and then execute the XPath query on it. Unfortunately there is no way for us to specify in a Binding that we want to execute the Path before the XPath. And this is the reason I had to break up the Binding into two: the part with the Path needs to be executed first so it goes in the DataContext, and the XPath needs to be executed second, so it goes in the ItemsSource.</p>
<p><br/></p>
<h3>Three level Master-Detail with XLinq</h3>
<p>Here is the code and XAML for the XLinq version of the three level Master-Detail:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;ObjectDataProvider ObjectType=&quot;{x:Type xlinq:XElement}&quot; MethodName=&quot;Load&quot; x:Key=&quot;MountainsODP&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ObjectDataProvider.MethodParameters&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;system:String&gt;../../Mountains.xml&lt;/system:String&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ObjectDataProvider.MethodParameters&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ObjectDataProvider&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Grid Margin=&quot;30&quot; Name=&quot;XLinq3Level&quot; DataContext=&quot;{Binding Source={StaticResource MountainsODP}, Path=Elements}&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding}&quot; Name=&quot;MasterListBox3&quot; ItemTemplate=&quot;{StaticResource XLinqMountainTemplate}&quot; IsSynchronizedWithCurrentItem=&quot;True&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding Path=Element[Lifts].Elements}&quot; ItemTemplate=&quot;{StaticResource XLinqLiftTemplate}&quot; IsSynchronizedWithCurrentItem=&quot;True&quot; Name=&quot;MiddleListBox3&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox Name=&quot;Detail3&quot; ItemsSource=&quot;{Binding ElementName=MiddleListBox3, Path=SelectedItem.Element[Runs].Elements}&quot; ItemTemplate=&quot;{StaticResource XLinqRunTemplate}&quot; IsSynchronizedWithCurrentItem=&quot;True&quot;  &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Grid&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/></p>
<p>Similarly to the XML scenario, the first ListBox binds to the collection of Mountain XElements directly. The second ListBox gets the Lifts collection of a particular mountain, and drills into its Elements collection. This returns all the Lift XElements for the selected mountain.</p>
<p>But the most interesting part of this scenario is the third ListBox. First of all, notice that I am still using ElementName to bind to the second ListBox. The reason for this is exactly the same as in the XML scenario. Notice also I am no longer splitting the Binding between the DataContext and ItemsSource. That is because we use Path to do XLinq queries, so there are no issues with precedence of Path versus XPath. As you can see, we can specify our query in a much more linear way: we first get the SeletedItem (of type XElement) from the second ListBox, then get its Runs child, and then drill into all the Run elements. </p>
<p><br/></p>
<h3>Keeping two ListBoxes in sync</h3>
<p>There are some differences of behavior when keeping two ListBoxes in sync (as part of three level Master-Detail or in isolation) using different data sources, so I will add a few extra words on this.</p>
<p>In the XML three-level Master-Detail, if you select &#8220;Whistler&#8221;, then &#8220;Stevens Pass&#8221;, then &#8220;Jupiter Chair&#8221;, then &#8220;Whistler&#8221;, then &#8220;Stevens Pass&#8221; again, you will notice that &#8220;Jupiter Chair&#8221; is no longer selected (the first element of that ListBox is selected instead). This happens because every time  you click on &#8220;Stevens Pass&#8221;, we execute the appropriate XPath query, and that returns a different instance of the same collection. </p>
<p>If you try this scenario when binding to CLR objects, ADO.NET or XLinq, you will see that previous selections in the second ListBox will be remembered.  This is typically what users want, so we paid special attention to it when designing integration between WPF and XLinq.</p>
<p><br/></p>
<h3>Conclusion</h3>
<p>When you have the option of either binding directly to the current item of the previous collection or using ElementName and SelectedItem to achieve Master-Detail, I typically advise people to bind to the current item. One reason for this is that the Master-Detail scenario is a data concept, and if possible it should be implemented by using only data concepts such as currency (SelectedItem is a concept similar to currency, but relevant to the UI only). Besides, your scenario becomes independent of the names of the ListBoxes. </p>
<p>However, in scenarios like the three-level Master-Detail using XML and XLinq, where there is no solution purely based on current item, in my opinion it&#8217;s perfectly OK to implement the binding on the second ListBox using ElementName and SelectedItem. This has the advantage of maintaining consistency between the second and third ListBoxes, making the XAML easier to read. In this post, I wanted to show how far you can go in Master-Detail by binding to the current item only, which is why I opted to show different styles of Bindings in the second and third ListBoxes. In my opinion, both ways are OK, so it really comes down to personal preference.</p>
<p><img src="http://bea.stollnitz.com/files/44/XLinqXMLMasterDetail.png" class="postImage" /></p>
<p><a href="http://bea.stollnitz.com/files/44/XLinqXMLMasterDetail.zip">Here</a> you can find the project with this code built using VS 2008 RTM.</p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=51</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=51</feedburner:origLink></item>
		<item>
		<title>How can I bind WPF elements to XLinq?</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/7KSeVElFFIU/</link>
		<comments>http://bea.stollnitz.com/blog/?p=50#comments</comments>
		<pubDate>Wed, 02 Jan 2008 02:41:07 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=50</guid>
		<description><![CDATA[One of the main features that we shipped in .NET 3.5 is the ability to bind to XLinq directly from XAML. In this post, I will first cover the basics of this new feature, and then discuss the seven queries I added to the sample project. Please keep in mind that this blog post does [...]]]></description>
			<content:encoded><![CDATA[<p>One of the main features that we shipped in .NET 3.5 is the ability to bind to XLinq directly from XAML. In this post, I will first cover the basics of this new feature, and then discuss the seven queries I added to the sample project. Please keep in mind that this blog post does not attempt to provide an introduction to XLinq. For those of you that need a quick introduction to the topic, I found <a href="http://msdn2.microsoft.com/en-us/library/bb308960.aspx">this site</a> to be a good resource.</p>
<p><br/></p>
<h3>Basics of binding to XLinq</h3>
<p>When the XLinq and WPF teams started thinking about the integration of the two products, we quickly realized that we would need to do some work to improve our XAML scenarios in .NET 3.5. The main reason for this decision was the fact that simple XLinq queries are mostly done by calling sequences of methods on an XElement (such as .Elements() or .Descendants()) and we don&#8217;t have a good syntax to do that in XAML. (Yes, we can bind to the result of a method by using ObjectDataProvider as I explained in <a href="http://bea.stollnitz.com/blog/?p=22">this post</a>, but you can imagine how cumbersome the syntax would be if you wanted to call a method on the object returned by another method call. It would be much easier to do it in code.)</p>
<p>Since WPF already has a good syntax for binding to properties and subproperties, the logical solution to this problem was to add PropertyDescriptors to the XLinq DLL that expose certain key methods as if they were properties. If you are not familiar with PropertyDescriptors, I encourage you to read <a href="http://msdn.microsoft.com/msdnmag/issues/05/04/NETMatters/">this article on ICustomTypeDescriptor</a> and <a href="http://msdn.microsoft.com/msdnmag/issues/05/05/NETMatters/default.aspx">this one on TypeDescriptorProvider</a>. These are the two best articles I have found on the topic. There is a precedent for this solution: we did basically the same thing to provide XAML support when binding to ADO.NET. As I described in <a href="http://bea.stollnitz.com/blog/?p=21">this post</a> , we expose the Relation names using PropertyDescriptors so that you can bind to them as if they were properties.</p>
<p>I want you to understand how we implemented this feature so that it doesn&#8217;t come as a surprise when I say that to bind to XLinq, you will need to use a binding&#8217;s Path (not XPath!) and the same &#8220;dot syntax&#8221; that we use to bind to subproperties.</p>
<p>We implemented six new PropertyDescriptors. You can find the new code added by the XLinq team in the System.Xml.XLinq.ComponentModel namespace of the System.Xml.XLinq DLL. You will see the TypeDescriptionProvider (XTypeDescriptionProvider&lt;T&gt;) and a new PropertyDescriptor class for each property we expose this way (e.g., XElementAttributePropertyDescriptor). The table below shows the correspondence between the properties exposed by the PropertyDescriptors (to be used in XAML) and their equivalent XLinq code. </p>
<p><br/></p>
<table border="1">
<tr>
<p>
<td>XAML</td>
</p>
<p>
<td>Code</td>
</p>
<p>
<td>Notes</td>
</p>
</tr>
<tr>
<p>
<td>Element[MyElementName]</td>
</p>
<p>
<td>.Element(&#8221;MyElementName&#8221;)</td>
</p>
<td />
</tr>
<tr>
<p>
<td>Elements[MyElementName]</td>
</p>
<p>
<td>.Elements(&#8221;MyElementName&#8221;)</td>
</p>
<p>
<td>Parameter is optional.</td>
</p>
</tr>
<tr>
<p>
<td>Attribute[MyAttributeName]</td>
</p>
<p>
<td>.Attribute(&#8221;MyAttributeName&#8221;)</td>
</p>
<td />
</tr>
<tr>
<p>
<td>Descendents[ElementName]</td>
</p>
<p>
<td>.Descendents(&#8221;ElementName&#8221;)</td>
</p>
<p>
<td>Parameter is optional.</td>
</p>
</tr>
<tr>
<p>
<td>Value</td>
</p>
<p>
<td>.Value</td>
</p>
<p>
<td>Special case. (I explain below.)</td>
</p>
</tr>
<tr>
<p>
<td>Xml</td>
</p>
<p>
<td>.ToString(SaveOptions.DisableFormatting)</td>
</p>
<td />
</tr>
</table>
<p><br/></p>
<p>The properties in the XAML column of this table are all exposed in XElement, but Value is also exposed in XAttribute. Based on the table above, you can deduce that the following XLinq code&#8230;</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;var query = myXElement.Attribute(&quot;MyAttribute&quot;);<br/></p>
<p>&#8230;has the following equivalent in XAML (assuming that the DataContext is the XElement myElement):</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Attribute[MyAttribute]}&quot; /&gt;<br/></p>
<p>However, you may be wondering why we decided to define a PropertyDescriptor for Value, given that it is already a property (and not a method) of XElement (and XAttribute). It seems that we could simply bind to XElement and dot into it, right? Well, the reason for this is that we wanted to provide change notifications for Value. If you get a particular XElement as the result of a query, bind to it, and then change its value, we want that to be reflected in the UI. By adding a PropertyDescriptor for this property, we can provide change notifications in a non-intrusive way (without having to change the XElement class). </p>
<p>Similarly, it is important to keep in mind that anytime you bind to properties that don&#8217;t have a corresponding PropertyDescriptor (such as Root and HasAttributes in the samples I will show below), WPF will not be notified of property changes. If those properties happen to be in the middle of a query, they break the chain of notifications.</p>
<p>You can use <a href="http://www.aisto.com/roeder/dotnet/">.NET reflector</a> to look into these PropertyDescriptors and understand how I came up with the table above. For example, search for XElementXmlPropertyDescriptor, look at the GetValue method, and you will see that it returns this.element.ToString(SaveOptions.DisableFormatting).</p>
<p>This is all there is to this new feature. Next I will show a few samples and a comparison between the XLinq code syntax, XLinq XAML syntax, and the XPath way of doing the same query.</p>
<p><br/></p>
<h3>Samples</h3>
<p>Here is the data source used in these samples:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;SolarSystemPlanets&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Planet Name=&quot;Mercury&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Orbit&gt;57,910,000 km (0.38 AU)&lt;/Orbit&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Diameter&gt;4,880 km&lt;/Diameter&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Mass&gt;3.30e23 kg&lt;/Mass&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Details&gt;The small and rocky planet Mercury is the closest planet to the Sun.&lt;/Details&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Planet&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/SolarSystemPlanets&gt;<br/></p>
<p>I defined the XML in an external file, added it as a Resource, and parsed it into an XDocument with the following code:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;XDocument planetsDocument = InitializeDataSource(&quot;/Planets.xml&quot;);<br/></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private XDocument InitializeDataSource(string uriString)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StreamResourceInfo resourceInfo = Application.GetResourceStream(new Uri(uriString, UriKind.Relative));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StreamReader reader = new StreamReader(resourceInfo.Stream);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return XDocument.Load(reader);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>I also created an XmlDataProvider so that I could show queries equivalent to the XLinq ones, but using XPath:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;XmlDataProvider xdp = new XmlDataProvider();<br/>&nbsp;&nbsp;&nbsp;&nbsp;xdp.Source = new Uri(&quot;Planets.xml&quot;, UriKind.Relative);<br/></p>
<p><br/></p>
<h4>First Planet&#8217;s Orbit</h4>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq code<br/>&nbsp;&nbsp;&nbsp;&nbsp;var query2 = planetsDocument.Root.Element(&quot;Planet&quot;).Element(&quot;Orbit&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(query2);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq XAML<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Root.Element[Planet].Element[Orbit]}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XPath<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding XPath=/SolarSystemPlanets/Planet[1]/Orbit, Path=OuterXml}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// Result<br/>&nbsp;&nbsp;&nbsp;&nbsp;//&lt;Orbit&gt;57,910,000 km (0.38 AU)&lt;/Orbit&gt;<br/></p>
<p>This working sample shows how easy it is to follow the table above to translate XLinq code into XAML. Please keep in mind that the range of useful query methods supported by XLinq is far greater than the ones we decided to support. For example, you could have written this same query in the following way:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;var query2 = planetsDocument.Element(&quot;SolarSystemPlanets&quot;).Element(&quot;Planet&quot;).Element(&quot;Orbit&quot;);<br/></p>
<p>This can not be translated directly into XAML because the first Element(&#8230;) method is called on XDocument, and we only added the Element property descriptor for XElement. </p>
<p>Here are a couple of other examples of queries that produce the same result:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;var query2 = planetsDocument.Root.Elements().First().Element(&quot;Orbit&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;var query2 = planetsDocument.Root.Elements().ElementAt(0).Element(&quot;Orbit&quot;);<br/></p>
<p>These can also not be translated into XAML because we don&#8217;t provide property descriptors for First() and ElementAt(&#8230;). So, if you have an XLinq query in code that you want to translate into XAML, you should try to first modify it to use only the methods we support. Of course, this will only work for simple queries &#8211; if you are using more advanced features of XLinq, your only option is to use code.</p>
<p>You may have noticed that the XmlDataProvider binding above uses both XPath and Path. This scenario is fully supported and sometimes very useful. The data binding engine will first resolve the XPath, and then it will call the OuterXml property on the resulting XmlElement. If I hadn&#8217;t called OuterXml, the query would return only the content of this element.</p>
<h4>List of Planet Names</h4>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq code<br/>&nbsp;&nbsp;&nbsp;&nbsp;var query3 = planetsDocument.Root.Elements();<br/>&nbsp;&nbsp;&nbsp;&nbsp;foreach (var element in query3)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(element.Attribute(&quot;Name&quot;).Value);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq XAML <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Name=&quot;XLinqPanel3&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate x:Key=&quot;PlanetTemplate&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Attribute[Name].Value}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding Path=Root.Elements}&quot; ItemTemplate=&quot;{StaticResource PlanetTemplate}&quot; &#8230; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XPath<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Name=&quot;XMLPanel3&quot; Margin=&quot;10,0,0,0&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate x:Key=&quot;PlanetTemplate&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding XPath=@Name}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding XPath=/SolarSystemPlanets/Planet}&quot; ItemTemplate=&quot;{StaticResource PlanetTemplate}&quot; &#8230;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// Result<br/>&nbsp;&nbsp;&nbsp;&nbsp;//Mercury<br/>&nbsp;&nbsp;&nbsp;&nbsp;//Venus<br/>&nbsp;&nbsp;&nbsp;&nbsp;// &#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;//Pluto<br/></p>
<p>In this sample I show how to bind to a collection and how to display an attribute. Notice that in XLinq, if I only want to show the content of the attribute, I have to explicitly call its Value property. If I don&#8217;t call Value, each item in the ListBox will display something like &#8216;Name=&#8221;Mercury&#8221;&#8216;. In XML, binding to the attribute @Name will give me the contents of the attribute automatically.</p>
<h4>List of Planet Diameters</h4>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq code<br/>&nbsp;&nbsp;&nbsp;&nbsp;var query4 = planetsDocument.Root.Descendants(&quot;Diameter&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;foreach (var element in query4)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(element.Value);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq XAML<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding Path=Root.Descendants[Diameter]}&quot; DisplayMemberPath=&quot;Value&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XPath<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox ItemsSource=&quot;{Binding XPath=/SolarSystemPlanets/*/Diameter}&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// Result<br/>&nbsp;&nbsp;&nbsp;&nbsp;//4,880 km<br/>&nbsp;&nbsp;&nbsp;&nbsp;//12,103.6 km<br/>&nbsp;&nbsp;&nbsp;&nbsp;// &#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;//2274 km<br/></p>
<p>I added this sample to show binding to the &#8220;Descendants&#8221; property, and the corresponding XML. Pretty simple.</p>
<h4>Non-formatted XML</h4>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq code <br/>&nbsp;&nbsp;&nbsp;&nbsp;var query5 = planetsDocument.Root.Element(&quot;Planet&quot;).ToString(SaveOptions.DisableFormatting);<br/>&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(query5);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq XAML<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Root.Element[Planet].Xml}&quot; &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XPath<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding XPath=/SolarSystemPlanets/Planet[1], Path=OuterXml}&quot;  &#8230;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// Result<br/>&nbsp;&nbsp;&nbsp;&nbsp;// &lt;Planet Name=&quot;Mercury&quot;&gt;&lt;Orbit&gt;57,910,000 km (0.38 AU)&lt;/Orbit&gt;&lt;Diameter&gt;4,880 km&lt;/Diameter&gt;&lt;Mass&gt;3.30e23 kg&lt;/Mass&gt;&lt;Details xmlns=&quot;http://myNamespace&quot;&gt;The small and rocky planet Mercury is the closest planet to the Sun.&lt;/Details&gt;&lt;/Planet&gt;<br/></p>
<p>The Xml property allows you to query for the unformatted version of the XML. This is the only property whose name is quite different from the corresponding method in code (as you can imagine, it would be quite cumbersome to keep them consistent). If you use the same XLinq query in XAML but without  &#8220;.Xml&#8221; in the Path, you would get XML that is formatted with indentation.</p>
<h4>HasAttributes of Mass</h4>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq code <br/>&nbsp;&nbsp;&nbsp;&nbsp;var query6 = planetsDocument.Root.Element(&quot;Planet&quot;).Element(&quot;Mass&quot;).HasAttributes;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(query6);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq XAML<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Root.Element[Planet].Element[Mass].HasAttributes}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XPath<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding XPath=/SolarSystemPlanets/Planet[1]/Mass, Path=HasAttributes}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// Result<br/>&nbsp;&nbsp;&nbsp;&nbsp;// false<br/></p>
<p>HasAttributes is a property that exists both in XmlElement and XElement. You can see in this example that since XLinq uses Path, it&#8217;s easy to bind to properties exposed directly in XElement. This same scenario using XPath has to be implemented by combining XPath and Path.</p>
<h4>First Planet&#8217;s Details</h4>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq code <br/>&nbsp;&nbsp;&nbsp;&nbsp;XNamespace ns = &quot;http://myNamespace&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;var query7 = planetsDocument.Root.Element(&quot;Planet&quot;).Element(ns + &quot;Details&quot;).Value;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(query7);<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq XAML<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Root.Element[Planet].Element[{http://myNamespace}Details].Value}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XPath<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding XPath=/SolarSystemPlanets/Planet[1]/mn:Details}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// Result<br/>&nbsp;&nbsp;&nbsp;&nbsp;//The small and rocky planet Mercury is the closest planet to the Sun.<br/></p>
<p>This sample shows how to embed namespaces in XAML. Below you can see that when I instantiated the XmlDataProvider, I also added an XmlNamespaceMapping that allows me to use the string &#8220;mn&#8221; instead of the full namespace. That is how the data binding engine knows how to resolve the namespace in &#8220;mn:Details&#8221; correctly.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;XmlDataProvider xdp = new XmlDataProvider();<br/>&nbsp;&nbsp;&nbsp;&nbsp;xdp.Source = new Uri(&quot;Planets.xml&quot;, UriKind.Relative);<br/>&nbsp;&nbsp;&nbsp;&nbsp;XmlNamespaceMapping mapping = new XmlNamespaceMapping(&quot;mn&quot;, new Uri(&quot;http://myNamespace&quot;));<br/>&nbsp;&nbsp;&nbsp;&nbsp;XmlNamespaceMappingCollection mappingCollection = new XmlNamespaceMappingCollection();<br/>&nbsp;&nbsp;&nbsp;&nbsp;mappingCollection.Add(mapping);<br/>&nbsp;&nbsp;&nbsp;&nbsp;xdp.XmlNamespaceManager = mappingCollection;<br/></p>
<p>In XLinq, however, there is no concept of namespace prefixes. This decision was made in order to avoid confusion caused by the fact that prefixes depend on context to have meaning. For this reason, we need to include the full namespace directly in the XAML query as I show above. See what the XLinq team has to say about <a href="http://www.xlinq.net/xlinq/reference/index.html?page=html%2F719fee2f-2bc9-4235-99e1-cf105c0cf26e.htm">namespaces and prefixes</a>.</p>
<h4>First Planet&#8217;s info</h4>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq code <br/>&nbsp;&nbsp;&nbsp;&nbsp;var query8 = planetsDocument.Root.Element(&quot;Planet&quot;).Elements();<br/>&nbsp;&nbsp;&nbsp;&nbsp;foreach (var element in query8)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(element.Value);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XLinq XAML<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Name=&quot;XLinqPanel8&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate DataType=&quot;Mass&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock FontWeight=&quot;Bold&quot; Text=&quot;Mass:  &quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Value}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt;<br/></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate DataType=&quot;{}{http://myNamespace}Details&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock FontWeight=&quot;Bold&quot; Text=&quot;Details:  &quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=Value}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ItemsControl ItemsSource=&quot;{Binding Path=Root.Element[Planet].Elements}&quot; HorizontalAlignment=&quot;Left&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// XPath<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Name=&quot;XMLPanel8&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate DataType=&quot;Mass&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock FontWeight=&quot;Bold&quot; Text=&quot;Mass:  &quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding XPath=.}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt;<br/></p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate DataType=&quot;mn:Details&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock FontWeight=&quot;Bold&quot; Text=&quot;Details:  &quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding XPath=.}&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel.Resources&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ItemsControl ItemsSource=&quot;{Binding XPath=/SolarSystemPlanets/Planet[1]/*}&quot; HorizontalAlignment=&quot;Left&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// Result<br/>&nbsp;&nbsp;&nbsp;&nbsp;//57,910,000 km (0.38 AU)<br/>&nbsp;&nbsp;&nbsp;&nbsp;//4,880 km<br/>&nbsp;&nbsp;&nbsp;&nbsp;//3.30e23 kg<br/>&nbsp;&nbsp;&nbsp;&nbsp;//The small and rocky planet Mercury is the closest planet to the Sun.<br/></p>
<p>In the previous sample, you may have noticed that XLinq namespaces are surrounded by curly braces in XAML. This worked well when used inside of an indexer, however, since curly braces already have meaning for the XAML parser, sometimes we need to escape them. For example, in the DataType of the Details DataTemplate in this sample, I had to escape the namespace with a leading {}. Remember that when binding to objects, the DataType property could be set to a value such as {x:Type local:MyDataObject}, so escaping the namespace allows the parser to distinguish between the two scenarios. </p>
<p><img src="http://bea.stollnitz.com/files/43/BindToLinq.png" class="postImage" /></p>
<p><a href="http://bea.stollnitz.com/files/43/BindToLinq.zip">Here</a> you can find the project with this code built using VS 2008 RTM.</p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=50</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=50</feedburner:origLink></item>
		<item>
		<title>How can I replace PowerPoint with WPF in my presentations?</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/I2-cmJfYkxw/</link>
		<comments>http://bea.stollnitz.com/blog/?p=49#comments</comments>
		<pubDate>Mon, 26 Nov 2007 05:51:56 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=49</guid>
		<description><![CDATA[If you&#8217;ve attended one of my presentations before, you&#8217;ve noticed that I never use PowerPoint &#8211; instead, I have my slides coded in WPF. I&#8217;ve decided to write my own presentation code because I want my slides to be interactive &#8211; instead of showing a concept in PowerPoint and switching to VS to demo it, [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve attended one of my presentations before, you&#8217;ve noticed that I never use PowerPoint &#8211; instead, I have my slides coded in WPF. I&#8217;ve decided to write my own presentation code because I want my slides to be interactive &#8211; instead of showing a concept in PowerPoint and switching to VS to demo it, I can demo it directly within the presentation. Several people have asked me for my presentation code in the past, so I&#8217;ve decided to share it here in my blog.</p>
<h3>Use this project</h3>
<p>You don&#8217;t have to read the whole blog post if all you want to do is use my code as a base for your presentation. Here are the steps to add your slides to this project:</p>
<ul>
<li>You may want to delete the pages I created (01Title.xaml, 02Slide.xaml, 03Slide.xaml).</li>
<li>Add a new WPF Page for each new slide you want to create.</li>
<li>In the &#8220;Presentation.cs&#8221; file, add the name of your xaml slide to the &#8220;slides&#8221; string array, in the order you want them to appear.</li>
</ul>
<h3>Understand the code</h3>
<p>Yeah, I thought all of you geeks would continue reading. I will explain below how I implemented each of the features in this project.</p>
<h4>Navigation</h4>
<p>I added two elements to the Window&#8217;s main grid: </p>
<ul>
<li>A frame that displays each slide in turn. Notice that its Source is data bound to the CurrentSlide property of a data source. The CurrentSlide property will contain the name of the XAML Page that is being displayed. I will show what that data source looks like later in this section.</li>
<li>A TextBlock that contains the title of the slide. Notice that this TextBlock is data bound to the Title of the Frame&#8217;s content (which is really the Title of each slide Page).</li>
</ul>
<p>These two elements are wrapped by a Viewbox. I find the Viewbox quite handy in this scenario because I want my slides to scale correctly for every screen resolution. </p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&lt;Grid x:Name=&quot;LayoutRoot&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Viewbox Margin=&quot;10,20,10,40&quot; Stretch=&quot;Uniform&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!&#8211; Title &#8211;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock VerticalAlignment=&quot;Top&quot; Height=&quot;84&quot; FontFamily=&quot;Calibri&quot; FontSize=&quot;65&quot; FontWeight=&quot;Bold&quot; Text=&quot;{Binding Path=Content.Title, ElementName=Frame, Mode=Default}&quot; TextAlignment=&quot;Center&quot; TextWrapping=&quot;Wrap&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!&#8211; Content of the slide &#8211;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Frame Width=&quot;1000&quot; Height=&quot;600&quot; Source=&quot;{Binding Path=CurrentSlide, Source={StaticResource presentation}}&quot; x:Name=&quot;Frame&quot; NavigationUIVisibility=&quot;Hidden&quot; Background=&quot;{x:Null}&quot; Focusable=&quot;False&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Viewbox&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Grid&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/></p>
<p>The data source that contains the slide information contains a private array with the names of the slides, and it exposes public properties for CurrentSlide and CurrentIndex.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private string[] slides = { &quot;01Title.xaml&quot;, &quot;02Slide.xaml&quot;, &quot;03Slide.xaml&quot; };<br/></p>
<p>In the XAML above, you can see that I disabled the usual frame navigation UI by setting NavigationUIVisibility to Hidden. Instead, I decided to add custom &#8220;previous&#8221; and &#8220;next&#8221; buttons in the shape of arrows. I designed the arrows in Blend, added IsMouseOver, IsPressed and IsEnabled triggers that cause changes in their appearance, and used those assets within the style of my &#8220;previous&#8221; and &#8220;next&#8221; buttons. I added GoBack() and GoNext() methods to the data source that decrement and increment the CurrentIndex, and made sure that those are called in the click handlers of the arrow buttons.</p>
<p>Last, I wanted to disable the &#8220;next&#8221; button upon reaching the end of the presentation, and disable the &#8220;previous&#8221; button at the start of the presentation. To do this, I added CanGoBack and CanGoNext properties to my data source, to which I bound the IsEnabled property of the arrow buttons:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public bool CanGoBack<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { return this.currentIndex &gt; 0; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;public bool CanGoNext<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { return this.currentIndex &lt; this.slides.Length &#8211; 1; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Button IsEnabled=&quot;{Binding Path=CanGoNext, Source={StaticResource presentation}}&quot; … Style=&quot;{DynamicResource NextButtonStyle}&quot; /&gt;<br/></p>
<p>This is really all it took to have a basic but functional presentation program. </p>
<h4>Pick starting slide</h4>
<p>Sometimes I like to stop the slide application to change some xaml or code in one of the slides, and then restart it to show the changes during a live demo. However, I don’t want to restart the whole slide show; I want to skip directly to the slide that I just edited. So I added the following code to the constructor of the Window in order to find the xaml file with the most recent modification time:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public Window1()<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// When restarting this app, it will start the slide show in the last edited XAML page.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int indexLastWritten = 0;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DateTime latestDateTimeWritten = DateTime.MinValue;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; presentation.Slides.Length; i++)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string slide = presentation.Slides[i];<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DateTime dateLastWritten = File.GetLastWriteTime(@&quot;&#8230;.&quot; + slide);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dateLastWritten.CompareTo(latestDateTimeWritten) &gt; 0)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;latestDateTimeWritten = dateLastWritten;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;indexLastWritten = i;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;presentation.CurrentIndex = indexLastWritten;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<h4>Hide cursor</h4>
<p>I found that sometimes the cursor gets in the way during a presentation. I want it to be visible when I move the mouse, but I want it to disappear when I&#8217;m just talking about a slide or demo. I wrote some code that causes the cursor to disappear when I don&#8217;t move the mouse for 5 seconds, and causes it to reappear when I move the mouse again.</p>
<p>In the constructor for the Window, I added a DispatcherTimer that ticks every 5 seconds, and started it. In the tick handler for this timer, I set the cursor to Cursors.None, causing it to disappear. Then I added handlers for the mouse move and mouse click events, which restart the timer and make the cursor go back to its default look.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private DispatcherTimer timer;<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;public Window1()<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Initializes timer that causes the mouse cursor to disappear after 5 seconds.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer = new DispatcherTimer();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.Interval = TimeSpan.FromSeconds(5);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.Tick += new EventHandler(Timer_Tick);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.Start();        <br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void Timer_Tick(object sender, EventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.Cursor = Cursors.None;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void ShowCursor()<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.Start();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.ClearValue(FrameworkElement.CursorProperty);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShowCursor();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private void Window_PreviewMouseMove(object sender, MouseEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShowCursor();<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<h4>Animation when clicking</h4>
<p>When attending a presentation, it is hard to know when the presenter is hovering over an element, and when she&#8217;s clicking on it. For some of my demos, it is actually important for the customers attending to recognize those gestures, so I started thinking of ways to make this distinction obvious.</p>
<p>I ended up deciding to add a small animation near the mouse pointer when I click anywhere within the Window. I used Blend to get the animation set up, to make sure it gets triggered on PreviewMouseLeftButtonDown, and to control the visibility of the canvas containing the animation (this canvas should be always collapsed except when I click on the Window). Then I wrote a little bit of code to make sure that the canvas is positioned at the mouse location when the click happens:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShowCursor();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Point currentPoint = e.GetPosition(this);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.clickCanvas.RenderTransform = new TranslateTransform(currentPoint.X, currentPoint.Y);<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<h4>Keyboard shortcuts</h4>
<p>Finally, I realized that it was a bit impractical to always have to use the mouse to move to the next slide, so I decided to add support for KeyDown events. My event handler jumps to the previous or next slide when I press the left or right arrow key, and exits the presentation when I press the escape key.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (e.Key == Key.Left)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;presentation.GoBack();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Handled = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (e.Key == Key.Right)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;presentation.GoNext();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Handled = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (e.Key == Key.Escape)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Application.Current.Shutdown();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Handled = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<h3>Conclusion</h3>
<p>As you can see, my presentation program is quite simple but effective. Hopefully, those of you who do WPF presentations will find it as useful as I do.</p>
<p><img src="http://bea.stollnitz.com/files/42/WPFPresenter.jpg" class="postImage" /></p>
<p><a href="http://bea.stollnitz.com/files/42/WPFPresenter.zip">Here</a> you can find the project with this code built using VS 2008 RTM.</p>
<p><br/></p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=49</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=49</feedburner:origLink></item>
		<item>
		<title>Are there any tricks that will help me improve TreeView’s performance? – Part III</title>
		<link>http://feedproxy.google.com/~r/BeatrizCosta/~3/fFpv4k-BJno/</link>
		<comments>http://bea.stollnitz.com/blog/?p=45#comments</comments>
		<pubDate>Sat, 22 Sep 2007 19:06:33 +0000</pubDate>
		<dc:creator>Bea</dc:creator>
				<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://bea.stollnitz.com/blog/?p=45</guid>
		<description><![CDATA[Update: This post is out of date. With .NET 3.5 SP1, TreeView now provides opt-in UI virtualization. You can see this feature working by setting VirtualizingStackPanel.IsVirtualizing=&#8221;True&#8221; on the TreeView itself. TreeView also supports container recycling, which you can control by setting the VirtualizingStackPanel.VirtualizationMode property.

In part I of my discussions on TreeView performance, I presented the [...]]]></description>
			<content:encoded><![CDATA[<p class="note">Update: This post is out of date. With .NET 3.5 SP1, TreeView now provides opt-in UI virtualization. You can see this feature working by setting VirtualizingStackPanel.IsVirtualizing=&#8221;True&#8221; on the TreeView itself. TreeView also supports container recycling, which you can control by setting the VirtualizingStackPanel.VirtualizationMode property.</p>
<p><br/></p>
<p>In <a href="http://bea.stollnitz.com/blog/?p=42">part I</a> of my discussions on TreeView performance, I presented the three main limitations in the current implementation of TreeView that may lead to slower performance:</p>
<ul>
<li>UI elements stay in memory even when collapsed.</li>
<li>There is no UI virtualization.</li>
<li>There is no data virtualization.</li>
</ul>
<p>In <a href="http://bea.stollnitz.com/blog/?p=43">part II</a>, I talked about a solution where I introduced a middle tier between the UI and the data layer, that discards the data when a TreeViewItem is collapsed, and brings the data back into memory when a TreeViewItem is expanded. This solution completely fixes the first limitation of TreeView &#8211; the UI elements no longer stay in memory after expanding and collapsing a TreeViewItem. It also partially fixes the lack of data virtualization in TreeView because we only keep a small portion of the data in memory. I say &#8220;partially&#8221; because it virtualizes data on expand/collapse, but it does not take scrolling into account.</p>
<p>Today I will discuss a solution that builds on top of the previous one by providing UI virtualization.</p>
<p>With the current version of WPF, only two Controls offer UI virtualization: ListBox and ListView (actually, ListView gets it for free because it derives from ListBox). The work to make virtualization happen is done by VirtualizingStackPanel, which is the panel used by default in ListBox. It would be nice if we could simply tell TreeView to use a VirtualizingStackPanel to lay out its items, but unfortunately it&#8217;s not that simple. VirtualizingStackPanel understands only flat lists of items, so it is not capable of laying out the hierarchical data required for a TreeView.</p>
<p>On the other hand, styles and templates are among the most powerful features of WPF because they allow you to completely change the look of a control while retaining its behavior. For example, <a href="http://bea.stollnitz.com/blog/?p=40">this post</a> shows how a ListBox can easily be customized to look like a diagram of our solar system. With this in mind, Ben Carter (an awesome dev on the WPF team) had the brilliant idea of simply making a ListBox look like a TreeView. This allows us to use VirtualizingStackPanel for free, which offers UI virtualization. And you will see how easy it is to make a ListBox look like a TreeView, thanks to the power of styles and templates in WPF. We&#8217;ll need to make a few changes to the data side, but I will explain what they are.</p>
<p>I started by thinking about the theming portion of this scenario. To make my ListBox look like a TreeView, I need the toggle button that expands and collapses TreeViewItems. I used Blend, once again, to dig into the default style for the ToggleButton in TreeViewItem (which in the Aero theme looks like a triangle), and copied it to the window&#8217;s resources. This style contains triggers to change its look when the mouse is over it, and to rotate it when a user clicks on it. Then I added the following DataTemplate to the ItemTemplate property of my ListBox:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ToggleButton x:Name=&quot;tb&quot; ClickMode=&quot;Press&quot; Style=&quot;{StaticResource ExpandCollapseToggleStyle}&quot;/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=ShortName}&quot; /&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/></p>
<p>I tested it with a rudimentary data source &#8211; a simple (non-hierarchical) ObservableCollection of RegistryKey items that contain a ShortName property. This helped me understand how I need my data to be presented to the ListBox.</p>
<h3>Adding and removing items in the ObservableCollection</h3>
<p>My first realization was that my data source can not be hierarchical this time, because ListBox only understands flat lists of data. So I will need to have a single ObservableCollection with the data for all the visible items, regardless of their depth in the original hierarchy. I will have to update this list of visible items any time the user expands or collapses an item. When the user expands an item, I will insert the item&#8217;s children just after it in the ObservableCollection. When the user collapses an item, I will remove its children from the ObservableCollection. Here is the code I wrote to make this happen:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public class RegistryData3 : INotifyPropertyChanged <br/>&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private ObservableCollection&lt;RegistryKeyHolder3&gt; allKeys; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public ObservableCollection&lt;RegistryKeyHolder3&gt; AllKeys <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get { return allKeys; } <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public RegistryData3() <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.allKeys = new ObservableCollection&lt;RegistryKeyHolder3&gt;(); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.AddNewKeyHolder(Registry.CurrentUser); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.AddNewKeyHolder(Registry.CurrentConfig); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private void AddNewKeyHolder(RegistryKey registryKey) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RegistryKeyHolder3 newKeyHolder = new RegistryKeyHolder3(registryKey, 0); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newKeyHolder.PropertyChanged += new PropertyChangedEventHandler(KeyHolder_PropertyChanged); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.allKeys.Add(newKeyHolder); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void PopulateSubKeys(RegistryKeyHolder3 parentKeyHolder) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int indexParentKey = this.allKeys.IndexOf(parentKeyHolder); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (indexParentKey == this.allKeys.Count &#8211; 1 || this.allKeys[indexParentKey + 1].Level &lt;= parentKeyHolder.Level) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string[] subKeyNames = parentKeyHolder.Key.GetSubKeyNames(); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; subKeyNames.Length; i++) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RegistryKeyHolder3 childKeyHolder = new RegistryKeyHolder3(parentKeyHolder.Key.OpenSubKey(subKeyNames[i]), parentKeyHolder.Level + 1); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;childKeyHolder.PropertyChanged += new PropertyChangedEventHandler(KeyHolder_PropertyChanged); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;allKeys.Insert(indexParentKey + i + 1, childKeyHolder); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void ClearSubKeys(RegistryKeyHolder3 parentKeyHolder) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int indexToRemove = this.allKeys.IndexOf(parentKeyHolder) + 1; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while ((indexToRemove &lt; this.allKeys.Count) &amp;&amp; (this.allKeys[indexToRemove].Level &gt; parentKeyHolder.Level)) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.allKeys.RemoveAt(indexToRemove); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;} <br/></p>
<p>The PopulateSubKeys method is responsible for adding an item&#8217;s children to the ObservableCollection when the user expands that item. This method retrieves the children of the parent item, creates a RegistryKeyHolder3 instance for each item and inserts those instances starting at the index just after the parent. Don&#8217;t worry about the Level concept you see in this code; I will explain it in the next section. I will also explain and show the code for the property changed event handler later in this post.</p>
<p>The ClearSubKeys method removes an item&#8217;s children from the list, and is called when the user collapses the parent. It starts removing items from the list in the index after the parent&#8217;s and continues until the expected number of items has been removed.</p>
<p>These two methods allow us to keep a flat list with the items in the order we want the ListBox to display them. Adding items to and removing items from the flat list achieves partial data virtualization, just like the solution in my previous post.</p>
<h3>Indentation</h3>
<p>I also realized that I need to tag each RegistryKeyHolder3 data item with the level they belong to, which will help me figure out how much they have to be indented in the ListBox. I decided to add a property called &#8220;Level&#8221; to the RegistryKeyHolder3 class for that purpose. For the root keys the Level property will be set to 0, for the next level it will be set to 1, and so on. Notice that while constructing the children key holders, the code in PopulateSubKeys specifies that the level of the children is the parent&#8217;s level incremented by 1. Also, in the ClearSubKeys method, one of the conditions to stop removing children is encountering an item that has the same level as the one being collapsed.</p>
<p>To indent the items in the UI based on the Level value, I added a Border to the left of the expander and text and bound its Width property to the Level property in the source:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Border Width=&quot;{Binding Path=Level, Converter={StaticResource ConvertLevelToIndent}}&quot; /&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ToggleButton x:Name=&quot;tb&quot; ClickMode=&quot;Press&quot; &#8230; Style=&quot;{StaticResource ExpandCollapseToggleStyle}&quot;/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=ShortName}&quot; /&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/></p>
<p>In order to convert a Level value to the Border&#8217;s Width, I defined the following converter:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;public class ConvertLevelToIndent : IValueConverter <br/>&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (int)value * 16; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new NotSupportedException(&quot;Not supported &#8211; ConvertBack should never be called in a OneWay Binding.&quot;); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<h3>Parent item expansion</h3>
<p>I decided to add an &#8220;IsExpanded&#8221; property to the RegistryKeyHolder3 class that will help me tie the children expansion on the data side with the visual rotation of the toggle button in the UI.</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Border Width=&quot;{Binding Path=Level, Converter={StaticResource ConvertLevelToIndent}}&quot; /&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ToggleButton x:Name=&quot;tb&quot; ClickMode=&quot;Press&quot; IsChecked=&quot;{Binding Path=IsExpanded}&quot; Style=&quot;{StaticResource ExpandCollapseToggleStyle}&quot;/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=ShortName}&quot; /&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/></p>
<p>If you take a look at the ToggleButton&#8217;s XAML, you will notice that its IsChecked property is bound to the IsExpanded property. The Mode of this binding is TwoWay &#8211; no Mode is defined explicitly, but I know that&#8217;s the default Mode for the IsChecked DP.</p>
<p>Also, if you look at the code that adds items to the list in the PopulateSubKeys method, you will notice that I added KeyHolder_PropertyChanged as the handler for the PropertyChanged event on RegistryKeyHolder3. Here is the code for that event handler:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;void KeyHolder_PropertyChanged(object sender, PropertyChangedEventArgs e) <br/>&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (e.PropertyName == &quot;IsExpanded&quot;) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RegistryKeyHolder3 keyHolder = (RegistryKeyHolder3)sender; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (keyHolder.IsExpanded) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.PopulateSubKeys(keyHolder); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.ClearSubKeys(keyHolder); <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <br/>&nbsp;&nbsp;&nbsp;&nbsp;}<br/></p>
<p>When the application starts, all the items appear collapsed because the IsExpanded property of each data item is initialized to false, and the IsChecked property is bound to IsExpanded. Here is what happens when the user expands an item:</p>
<p>1) When the user clicks on the ToggleButton to expand an item, IsChecked becomes true, and because of the TwoWay binding, the IsExpanded property for the corresponding data item is set to true. </p>
<p>2) RegistryKeyHolder3 raises a PropertyChanged event when its IsExpanded property changes, causing the code in the handler (the KeyHolder_PropertyChanged method in RegistryData3) to be executed. </p>
<p>3) Because IsExpanded is true for this data item, the PopulateSubKeys method on RegistryData3 is called, causing the children of this item to be added to the list and displayed in the ListBox.</p>
<p>You can imagine a similar sequence of events when the user clicks to collapse an item.</p>
<h3>Visibility of the expander</h3>
<p>Lastly, I wanted to make the expander for a particular item hidden whenever that item has no children. I was able to do this by adding a simple DataTrigger that causes the ToggleButton to be hidden whenever the Key&#8217;s SubKeyCount property is zero, and visible otherwise. You can see the complete XAML for the ItemTemplate&#8217;s DataTemplate here:</p>
<p class="code">&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;StackPanel Orientation=&quot;Horizontal&quot;&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Border Width=&quot;{Binding Path=Level, Converter={StaticResource ConvertLevelToIndent}}&quot; /&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ToggleButton x:Name=&quot;tb&quot; ClickMode=&quot;Press&quot; IsChecked=&quot;{Binding Path=IsExpanded}&quot; Style=&quot;{StaticResource ExpandCollapseToggleStyle}&quot;/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;TextBlock Text=&quot;{Binding Path=ShortName}&quot; /&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/StackPanel&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTemplate.Triggers&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;DataTrigger Binding=&quot;{Binding Path=Key.SubKeyCount}&quot; Value=&quot;0&quot;&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Setter Property=&quot;Visibility&quot; TargetName=&quot;tb&quot; Value=&quot;Hidden&quot;/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTrigger&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate.Triggers&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/DataTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ListBox.ItemTemplate&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&#8230;<br/></p>
<h3>Conclusion </h3>
<p>This solution provides true UI virtualization, as you can see in the screenshot below, where I expanded the three first items (in depth first order). If you scroll the third TreeView (or should I say ListBox?), you will see that for a little while the number of UI elements in memory increases, but it quickly settles on a number much lower than the other two TreeViews. This delay happens because we queue in the dispatcher the operation to clean up those items with a low priority so that it doesn&#8217;t make the UI unresponsive.</p>
<p>And just like the solution in my previous post, this solution discards children elements on collapse and provides a partial data virtualization solution.</p>
<p>So, should you all switch your TreeViews to ListBoxes? Well, as with almost everything in life, there is a price to pay for the benefits of this solution: the programming model is more cumbersome than if you were using a TreeView. You will not be able to use HierarchicalDataTemplates to style your items, you&#8217;ll miss the convenience properties and methods of TreeView, you&#8217;ll have to introduce a slightly complex intermediate layer between your UI and your data, and you will have to work hard to minimize the inconsistencies in the UI. In short, you can make a ListBox look like a TreeView, but you can&#8217;t make a ListBox become a TreeView.</p>
<p>Whether this solution is right for you depends on how much you value the performance gain over the disadvantages it brings.</p>
<p><img src="http://bea.stollnitz.com/files/41/TreeViewPerformance.png" class="postImage" class="bigImage"/></p>
<p><a href="http://bea.stollnitz.com/files/41/TreeViewPerformance.zip">Here</a> you can find the project with this code built using VS 2008 RTM.</p>
]]></content:encoded>
			<wfw:commentRss>http://bea.stollnitz.com/blog/?feed=rss2&amp;p=45</wfw:commentRss>
		<slash:comments>27</slash:comments>
		<feedburner:origLink>http://bea.stollnitz.com/blog/?p=45</feedburner:origLink></item>
	</channel>
</rss>
