<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Yet Another Coding Blog : ui</title><link>http://blogs.msdn.com/avip/archive/tags/ui/default.aspx</link><description>Tags: ui</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Real-time list filtering with Silverlight, MVVM, and PagedCollectionView</title><link>http://blogs.msdn.com/avip/archive/2009/10/30/real-time-list-filtering-with-silverlight-mvvm-and-pagedcollectionview.aspx</link><pubDate>Fri, 30 Oct 2009 03:51:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9915091</guid><dc:creator>Avi Pilosof</dc:creator><slash:comments>6</slash:comments><comments>http://blogs.msdn.com/avip/comments/9915091.aspx</comments><wfw:commentRss>http://blogs.msdn.com/avip/commentrss.aspx?PostID=9915091</wfw:commentRss><wfw:comment>http://blogs.msdn.com/avip/rsscomments.aspx?PostID=9915091</wfw:comment><description>&lt;p&gt;The Model-View-ViewModel pattern is very good for forcing clean UI code. Ideally, you want to end up with zero code in your &lt;em&gt;.xaml.cs&lt;/em&gt; file – everything should be data-bound.&lt;/p&gt;  &lt;p&gt;As nice as this sounds, sometimes it can get so tempting to break this rule in order to do something that should be simple. Here’s one example:&lt;/p&gt;  &lt;p&gt;You have a &lt;em&gt;ListView&lt;/em&gt; and a &lt;em&gt;TextBox&lt;/em&gt; in the view; they are bound to an &lt;em&gt;ObservableCollection&lt;/em&gt; and a string in the ViewModel. You want them to behave such that entering text into that box causes the &lt;em&gt;ListView&lt;/em&gt; to filter its items:&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://blogs.msdn.com/blogfiles/avip/WindowsLiveWriter/RealtimefilteringwithSilverlightMVVMandP_BE44/image_9.png" width="196" height="173" /&gt; &lt;/p&gt;  &lt;p&gt;The initial train of thought is like this: In the event handler for the &lt;em&gt;TextBox&lt;/em&gt;, address the &lt;em&gt;ListView&lt;/em&gt; and hide all the items that don’t match the filter. At least one problem with this is that you’ve broken your promise to keep logic in the ViewModel.&lt;/p&gt;  &lt;p&gt;The second train of thought is: Since the &lt;em&gt;TextBox&lt;/em&gt; is bound to a property on the ViewModel, add code to the property setter to remove all the items from the &lt;em&gt;ObservableCollection&lt;/em&gt; which don’t match the filter.&lt;/p&gt;  &lt;p&gt;This will “work”, and suck. The problem is that you’ve actually removed data, not filtered it. When the user changes the filter text, your collection no longer has the items you’ve removed – you need to reload them.&lt;/p&gt;  &lt;p&gt;This is where the &lt;em&gt;PagedCollectionView&lt;/em&gt; comes in. It’s a wrapper around another collection which serves the following purposes:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;You can bind UI elements to it, and it will work like any other collection. &lt;/li&gt;    &lt;li&gt;When wrapping a collection that implements &lt;em&gt;ICollectionChanged&lt;/em&gt;, it bubbles the notification changes. &lt;/li&gt;    &lt;li&gt;It has properties for setting filters (as well as sorting, paging, grouping). &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;So what you do is insert this object between your UI and your &lt;em&gt;ObservableCollection&lt;/em&gt;, and just modify the filters on it. The data remains untouched, but the UI reacts perfectly.&lt;/p&gt;  &lt;p&gt;Before:&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://blogs.msdn.com/blogfiles/avip/WindowsLiveWriter/RealtimefilteringwithSilverlightMVVMandP_BE44/image_7.png" width="343" height="104" /&gt; &lt;/p&gt;  &lt;p&gt;After:&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://blogs.msdn.com/blogfiles/avip/WindowsLiveWriter/RealtimefilteringwithSilverlightMVVMandP_BE44/image_8.png" width="411" height="86" /&gt; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Here’s a simple example of the steps to take:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1)&lt;/strong&gt; Create both the &lt;em&gt;ObservableCollection&lt;/em&gt; and the &lt;em&gt;PagedCollectionView&lt;/em&gt;, but only expose the latter as a property:&lt;/p&gt;  &lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;// This is the actual container of items that we're storing&lt;/span&gt;
&lt;span class="kwrd"&gt;private&lt;/span&gt; ObservableCollection&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; _RandomItemsBase;

&lt;span class="rem"&gt;// And this is the wrapper around it which we bind to. The PagedCollectionView&lt;/span&gt;
&lt;span class="rem"&gt;// allows for easy filtering, sorting, grouping.&lt;/span&gt;
&lt;span class="kwrd"&gt;private&lt;/span&gt; PagedCollectionView _RandomItems;
&lt;span class="kwrd"&gt;public&lt;/span&gt; PagedCollectionView RandomItems
{
    get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _RandomItems; }
    set
    {
        _RandomItems = &lt;span class="kwrd"&gt;value&lt;/span&gt;;
        RaisePropChange(() =&amp;gt; &lt;span class="kwrd"&gt;this&lt;/span&gt;.RandomItems);
    }
}&lt;/pre&gt;
&lt;style type="text/css"&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2)&lt;/strong&gt; In the constructor, wrap the real collection with the &lt;em&gt;PagedCollectionView&lt;/em&gt;:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;_RandomItemsBase = &lt;span class="kwrd"&gt;new&lt;/span&gt; ObservableCollection&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();
RandomItems = &lt;span class="kwrd"&gt;new&lt;/span&gt; PagedCollectionView(_RandomItemsBase);&lt;/pre&gt;
&lt;style type="text/css"&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3)&lt;/strong&gt; When the entered text changes, call a method to filter things:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; EnteredText
{
    get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _EnteredText; }
    set {
        _EnteredText = &lt;span class="kwrd"&gt;value&lt;/span&gt;;
        RaisePropChange(() =&amp;gt; &lt;span class="kwrd"&gt;this&lt;/span&gt;.EnteredText);
        UpdateFilter(); &lt;span class="rem"&gt;// Real-time filter&lt;/span&gt;
    }
}&lt;/pre&gt;
&lt;style type="text/css"&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4)&lt;/strong&gt; And here’s the code to actually apply the filter to the view:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UpdateFilter()
{
    &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(EnteredText))
        RandomItems.Filter = &lt;span class="kwrd"&gt;null&lt;/span&gt;;
    &lt;span class="kwrd"&gt;else&lt;/span&gt;
        RandomItems.Filter = (o) =&amp;gt; o.ToString()
                  .Contains(EnteredText);

    &lt;span class="rem"&gt;// Make sure to force a refresh of the view&lt;/span&gt;
    RandomItems.Refresh();
}&lt;/pre&gt;
&lt;style type="text/css"&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;p&gt;There is one interesting thing about the above snippet. Notice the lambda expression used to filter takes one argument (“&lt;em&gt;o&lt;/em&gt;”), and I just call &lt;em&gt;ToString()&lt;/em&gt; on it. Since the &lt;em&gt;PagedCollectionView&lt;/em&gt; isn’t templated, the filter delegate is called with an argument of type &lt;em&gt;object&lt;/em&gt;. You can then cast that object to whatever type you know the underlying colleciton uses. In this case, I have an &lt;em&gt;ObservableCollection&amp;lt;&lt;strong&gt;string&lt;/strong&gt;&amp;gt;&lt;/em&gt;, so I just call the &lt;em&gt;ToString()&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;That’s it. It gets even cooler when you start applying sorting, paging and grouping to this view, then bind to it with a &lt;em&gt;DataGrid&lt;/em&gt;, or something similar.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Avi&lt;/p&gt;

&lt;p&gt;&lt;script type="text/javascript"&gt;addthis_pub  = 'avip';&lt;/script&gt;
&lt;a href="http://www.addthis.com/bookmark.php" onmouseover="return addthis_open(this, '', '[URL]', '[TITLE]')" onmouseout="addthis_close()" onclick="return addthis_sendto()"&gt;&lt;img src="http://s7.addthis.com/button0-share.gif" width="83" height="16" border="0" alt="" /&gt;&lt;/a&gt;&lt;script type="text/javascript" src="http://s7.addthis.com/js/152/addthis_widget.js"&gt;&lt;/script&gt;
&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9915091" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/avip/archive/tags/silverlight/default.aspx">silverlight</category><category domain="http://blogs.msdn.com/avip/archive/tags/ui/default.aspx">ui</category></item><item><title>Turn down the ugly...</title><link>http://blogs.msdn.com/avip/archive/2008/07/23/turn-down-the-ugly.aspx</link><pubDate>Wed, 23 Jul 2008 05:42:28 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8765688</guid><dc:creator>Avi Pilosof</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/avip/comments/8765688.aspx</comments><wfw:commentRss>http://blogs.msdn.com/avip/commentrss.aspx?PostID=8765688</wfw:commentRss><wfw:comment>http://blogs.msdn.com/avip/rsscomments.aspx?PostID=8765688</wfw:comment><description>&lt;p&gt;I've always struggled with making my apps look decent. In my head I can picture how it should look, but when it comes down to it, I often end up with stuff that looks like a dog's breakfast.&lt;/p&gt; &lt;p&gt;Here's the cure:&lt;/p&gt; &lt;p&gt;&lt;a title="http://blogs.msdn.com/corrinab/archive/2008/07/22/8764478.aspx" href="http://blogs.msdn.com/corrinab/archive/2008/07/22/8764478.aspx"&gt;http://blogs.msdn.com/corrinab/archive/2008/07/22/8764478.aspx&lt;/a&gt;&lt;/p&gt; &lt;p&gt;That post in particular helped me understand how to use Blend to turn down the ugly; but Corrina's blog in general is good for those of you who use Silverlight.&lt;/p&gt; &lt;p&gt;Lose the borders on your ListBox and your app will already look way better :)&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Avi&lt;/p&gt; &lt;p&gt;&lt;!-- AddThis Button BEGIN --&gt;
&lt;a href="http://www.addthis.com/bookmark.php" onclick="window.open('http://www.addthis.com/bookmark.php?wt=nw&amp;pub=avip&amp;url='+encodeURIComponent(location.href)+'&amp;title='+encodeURIComponent(document.title), 'addthis', 'scrollbars=yes,menubar=no,width=620,height=520,resizable=yes,toolbar=no,location=no,status=no,screenX=200,screenY=100,left=200,top=100'); return false;" title="Bookmark and Share" target="_blank"&gt;&lt;img src="http://s9.addthis.com/button1-share.gif" wid&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8765688" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/avip/archive/tags/silverlight/default.aspx">silverlight</category><category domain="http://blogs.msdn.com/avip/archive/tags/ui/default.aspx">ui</category></item></channel></rss>