tag:blogger.com,1999:blog-31578812659698012852024-03-13T15:39:14.013-04:00b.ling on software developmentwhen pragmatism meets minimalismAnonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.comBlogger72125tag:blogger.com,1999:blog-3157881265969801285.post-3260418202812222462013-04-14T10:28:00.003-04:002013-04-14T10:28:32.754-04:00Blog has been moved!All future posts will be @ <a href="http://bling.github.io/">http://bling.github.io</a><br />
<br />Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-79527757153091072012013-02-11T19:50:00.000-05:002013-02-11T19:50:00.709-05:00Writing Macros with VimFirst, let’s start with a Javascript function:<br />
<pre><code>function foo(hello, world,
how, are,
you) {
}</code></pre>
Now let’s convert that to the following:<br />
<pre><code>function foo(parameters) {
var hello = parameters.hello;
var world = parameters.world;
var how = parameters.how;
var are = parameters.are;
var you = parameters.you;
}</code></pre>
Here are the macros I used to do this:<br />
<pre><code>let @r='di(iparameters^[/\{^M2o^[kpg`[v`]Jgv:s/\s//g^M0:try|exe "norm! @q"|endtry^MA;^[V:s/,/;\r/g^Mv``='
let @q='ywivar ^[pa = parameters.^[f,^[l@q'</code></pre>
Note that if you copy paste the above into your vimrc it will not work. The <code>^[</code> and <code>^M</code> found are actually single characters, not two. To input this properly you will need to chord it in input mode with <code><Ctrl-V></code>. So for <code><Esc></code> you would chord <code><Ctrl+V><Ctrl+[></code><br />
<br />
So, when I’m inside the parameters of the function, I can hit @r and it will perform the refactoring. Now let’s break it down step by step.<br />
<br />
<h4>
@q The first macro</h4>
<br />
This is a recursive macro which takes something like <code>a,b,c</code> and turns it into <code>var a = p.a,var b = p.b,var c = p.c</code>. Let’s see how that’s done.<br />
<ol>
<li><code>yw</code> <code>i</code> <code>var </code> <code><Esc></code> <code>p</code> Yanks the word and enter insert mode, type var, exit insert mode and paste the just yanked word.</li>
<li><code>a</code> <code> = parameters.</code> Append and fill in parameters.</li>
<li><code><Esc></code> <code>f,</code> <code>l</code> Exit insert mode, first the next ,</li>
<li><code>l</code> <code>@q</code> Adjust the cursor position and recursively call itself.</li>
</ol>
Recursive macros terminate when the first error occurs. In this macro, that error is when there are no more commas left.<br />
<br />
<h4>
@r The second macro</h4>
<br />
The is the macro that should be invoked, and references the @q macro.<br />
<ol>
<li><code>di(</code> Deletes everything inside the brackets.</li>
<li><code>i</code> <code>parameters</code> Enter insert mode and type parameters.</li>
<li><code><Esc></code> <code>/{</code> <code><CR></code> Leaves insert mode and finds the next brace.</li>
<li><code>2o</code> <code><Esc></code> <code>k</code> <code>p</code> Creates two empty lines and pastes what we deleted into the first line.</li>
<li><code>g`[v`]</code> <code>J</code> Visually select what we just pasted and join them all into a single line.</li>
<li><code>gv</code> <code>:s/\s//g</code> <code><CR></code> Reselect the visually and delete all whitespace.</li>
<li><code>0</code> Move to the beginning of the line.</li>
<li><code>:try|exe "norm! @q"|endtry</code> <code><CR></code> Macros will terminate on the first error, even if referencing another macro. Wrapping the other macro with try|endtry swallows the error and lets the current macro continue.</li>
<li><code>A;</code> <code><Esc></code> Append ; to the end of the line.</li>
<li><code>V</code> <code>:s/,/;\r/g</code> <code><CR></code> Visually select the line, replace with carriage returns.</li>
<li><code>v=` `</code> Visually select from the current cursor position back to where it was originally was, and format.</li>
</ol>
Now is this the best way to do it? Probably not. I would not be surprised if someone was able to do it with less keystrokes or a single macro. But hey, it was fun learning experience, and ultimately I turned all of that into two keystrokes that can be reused many times.<br />
<br />
I posted this on <a href="http://vimgolf.com/challenges/511991607729fb0002000003">vimgolf</a> so let’s see how other people solved the same refactoring!Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com1tag:blogger.com,1999:blog-3157881265969801285.post-44887574431666457992013-02-10T11:56:00.000-05:002013-02-11T10:41:57.475-05:00Love Affair with VimIt wasn't too long ago when I was a full-time C# developer and my environment was Visual Studio eight hours a day. Then, I became a web developer over night cold turkey writing Javascript and CSS. It's one of the benefits of working for a consulting company.<br />
<br />
You might think what does that have to do with the title of this post? Well, originally my plan was to write a blog post contrasting on the differences between Javascript and C#, as well as the development environments and deployment platforms. But really, what I really wanted to write about was Vim.<br />
<br />
Moving from Visual Studio to Vim was a progression through different editors and environments. The first thing I used to write Javascript was Webstorm. Over time I realized that you didn't really need an IDE to write Javascript/CSS. Then, I used Sublime Text for a little bit. But ultimately, I settled on Vim, and stayed there.<br />
<br />
My stubbornness turned out to be beneficial when I was learning Vim because the first month was absolutely painful. I remapped all of my arrows keys to do nothing to force myself to use hjkl. Eventually I got the hang of it, and now I definitely have the muscle memory that makes me much more productive when editing (and reading) text.<br />
<br />
By default Vim is just a text editor. But I work on a project, so like most lazy people I searched for prepackaged plugins and came across two popular distributions: <a href="https://github.com/spf13/spf13-vim">spf13</a> and <a href="https://github.com/carlhuda/janus">janus</a>. When I installed them, it was like someone took over Vim and made it change into a completely different beast. I didn't know how to use it anymore.<br />
<br />
I took a step back. I forgot where I got this advice, but I think anyone using Vim needs to do this: <b>start your own vimrc from scratch</b>.<br />
<br />
I took a look at all the settings that spf13 and janus changed. I copied them to my vimrc <b>one by one</b>, and also <b>:help</b>ing each setting so that I knew exactly what it changed. I must say, Vim's documentation is some of the best and most comprehensive of any tool I've worked with. It was incredibly helpful in my progression.<br />
<br />
Then, I did the same thing for plugins. And the nice thing was that most plugins followed the Vim pattern of having good documentation. After installing <b>fooplugin</b>, I just <b>:help fooplugin</b><i style="font-weight: bold;"> </i>and I got all the information I needed to know about the plugin.<br />
<br />
I became obsessed with optimizing my vimrc, and trying out different plugins on a daily basis. And because I was very adamant with trying one plugin at a time, I got to know them very well. I knew about how to turn certain settings on and off, how to configure their bindings, and more importantly, how it interacted with all of the other plugins I have already installed. Over time my vimrc became a full blown distribution in its own right, highly customized to my personal work habits.<br />
<br />
However, even though I recommend that anyone interested in taking their Vim skills to the next level should do this discovery that I have done, there are certainly classes of plugins that I deem to be <b>must-have</b> for any Vim user and I wanted to highlight them here.<br />
<br />
<h4>
Plugin Management</h4>
<div>
First things first you will need one plugin to rule them all! <a href="https://github.com/tpope/vim-pathogen">Pathogen</a> changed the way people install plugins by utilizing git submodules. This has the pros and cons of git submodules, i.e. they track a specific version of the external git repository, so if plugin authors decide to rewrite everything new users trying out your distribution will not be in for a nasty surprise.</div>
<div>
<br /></div>
<div>
However, this rarely happens and usually you just want the latest version. <a href="https://github.com/gmarik/vundle">Vundle</a> takes the management one step further and will automatically grab source code from Github for you (as well as automatically updating everything to the latest version).</div>
<div>
<br /></div>
<div>
The last one and least known is <a href="https://github.com/Shougo/neobundle.vim">neobundle</a>, which enhances Vundle even further and allows you to specify installation steps like compiling something.<br />
<br />
<h4>
Fuzzy File Searching</h4>
</div>
<div>
This was the major game changer for me and changed the way I worked. Naturally, proper Vim technique forces your hands to be on the home row, which makes reaching for the mouse (or even the arrow keys) to be inefficient. Therefore, the fastest way to open a file is usually to type its name.<br />
<br />
<a href="https://github.com/wincent/Command-T">CommandT</a> (written in Ruby) is noticeably much faster than <a href="https://github.com/kien/ctrlp.vim">CtrlP</a> (pure VimScript), but CtrlP has a lot more features. There's also <a href="http://www.vim.org/scripts/script.php?script_id=1984">FuzzyFinder</a>, which I have not tried.<br />
<br />
<h4>
Autocomplete and Snippets</h4>
</div>
<div>
There are various contenders here. Generally, you'll find that people fall into two camps.</div>
<div>
<br /></div>
<div>
1) <a href="https://github.com/garbas/vim-snipmate">Snipmate</a>/<a href="https://github.com/SirVer/ultisnips">UltiSnips</a> + <a href="https://github.com/ervandew/supertab">SuperTab</a> + (<a href="http://www.vim.org/scripts/script.php?script_id=1879">AutoComplPop</a>)</div>
<div>
2) <a href="https://github.com/Shougo/neocomplcache">neocomplcache</a> + <a href="https://github.com/Shougo/neosnippet">neosnippet</a></div>
<div>
<br /></div>
<div>
Snipmate is an older implementation of snippets which is getting replaced with UltiSnips. Supertab gives you an easy way to trigger omnicompletion with (you guessed it) tab, and AutoComplPop is for automatically showing the popup as you type.</div>
<div>
<br /></div>
<div>
Neocomplcache is a very powerful completion plugin. It runs a little slower than SuperTab because it does a lot more, but I find the performance acceptable so that's what I'm using. And I choose neosnippet over the others simply because it's by the same author and thus has better integration (e.g. available snippets will appear in the list).<br />
<br />
And of course, a good collection of snippets like <a href="https://github.com/honza/snipmate-snippets">honza's</a> collection.<br />
<br />
<h4>
And that's it!</h4>
</div>
<div>
What?! No file browser? No buffer manager? Yes, I have all of those installed as well. In fact, I have over 50 plugins installed in total. But in my opinion, they are not <i>killer</i> features. I can live without them. But if I didn't have fuzzy searching or completion/snippets, I would feel a little too naked.<br />
<br />
Out of the box Vim has some interesting defaults, mainly for backwards compatibility with Vi, but I think it's safe to say that anyone who uses Vim seriously will have a custom vimrc. If you're just starting out and don't know what to change, <a href="https://github.com/tpope/vim-sensible">sensible</a> is a good set of defaults.<br />
<br />
Vim has changed my work habits dramatically. I think and dream Vim. I install Vim plugins in my browsers. And every day, she still teaches me new tricks. It's quite exhilarating!<br />
<br />
If you've read until this point you might be interested in the full set of plugins that I'm using. If so, head over to my <a href="http://bling.github.com/dotvim/">project page</a>!</div>
<div>
</div>
Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com3tag:blogger.com,1999:blog-3157881265969801285.post-35054423319569044962012-07-29T19:59:00.001-04:002012-07-29T19:59:46.815-04:00SnoopShell: Evolution<p>It’s been a while since I last announced <a href="http://blingcode.blogspot.com/2012/07/snoopshell-marriage-of-snoop-wpf-and.html">SnoopShell</a>, where I took some PowerShell and injected that into Snoop. Well, I didn’t stop there! I decided to continue working on it and adding more useful features.</p> <p>Well, a bunch of things have changed. For one, it’s no longer targeted at .NET 4 and PS v3 anymore (and you’ll soon know why). Second, there’s a bunch of new features!</p> <h2>Automatic Profile Loading</h2> <p>Upon startup, the shell will look for a couple well known locations and automatically dot-source them to load them into the current session. This works the same as the standard $profile. The filename needs to be <em>SnoopProfile.ps1</em>, and the search paths are %USERPROFILE%, the <em>WindowsPowerShell</em>, and the <em>Scripts</em> folder deployed with Snoop.exe.</p> <p>This is incredibly useful since you can write your own custom functions and scripts and have them available to you all the time. As an added bonus, because of the dynamic nature of PowerShell, you can make modifications to the <em>SnoopProfile.ps1,</em> save, and then invoke a “. $profile” to reload the profile and update the session with your changes (all without restarting the application).</p> <p>That’s awesome sauce indeed ;-)</p> <h2>PowerShell Provider</h2> <p>This was more of a for-fun thing at first just to see if I could do it. Writing a PS provider is not fun at all, since it’s not very well documented and I actually needed some help from ILSpy to figure out how things really worked. Nonetheless, it’s got some basic functionality that is helpful to navigate around.</p> <p><a href="http://lh6.ggpht.com/-hpRCncySB3g/UBXOagD2xII/AAAAAAAAALs/qBCKmxV-B0M/s1600-h/image%25255B10%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-QMqNxt2DmZ8/UBXOa1SVqwI/AAAAAAAAAL0/ULMweSOFso0/image_thumb%25255B8%25255D.png?imgmax=800" width="654" height="399"></a></p> <p>Yep, the selected grid actually has a path, like how you would navigate the file system. Let’s see what happens with a <em>cd</em>.</p> <p><a href="http://lh4.ggpht.com/-R3wQmpiA7dw/UBXObb7FNsI/AAAAAAAAAL8/z3mgZwnFLok/s1600-h/image%25255B15%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-mdRas9rg4gs/UBXOb4W6z7I/AAAAAAAAAME/pNH-1jzMbGA/image_thumb%25255B11%25255D.png?imgmax=800" width="654" height="399"></a></p> <p>Cool, you can <em>cd</em> into the child “directory”, and it’ll automatically select the item in the tree view as well. What if you’re lazy and don’t want to type?</p> <p><a href="http://lh3.ggpht.com/-QgZ0iBA1AKw/UBXOcPU9uDI/AAAAAAAAAMM/iy9qHsS0zHA/s1600-h/image%25255B20%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-YV-f5-aPnP0/UBXOcRFGPkI/AAAAAAAAAMU/oo85Qg9fohU/image_thumb%25255B14%25255D.png?imgmax=800" width="654" height="399"></a></p> <p>Wildcards are supported. And because the visual tree doesn’t exactly require unique names, I needed to trick it by adding a number after each duplicate item. So the above matches the third Rectangle child of the Grid.</p> <h2>Code Injection</h2> <p>One of the cool things about Javascript is that it’s so darn easy to test. You make a change, save, reload, and you’ll immediately see if something worked or not. This feedback loop is so fast it changes how you work and formulate ideas.</p> <p>In the static world, we don’t really have this luxury, and especially not when you’re working on a large project, which at work, takes just under a minute for a full rebuild. And this is on a monster machine. Because of this, we had to employ tricks and workarounds to speed things up, like messing with build configurations and build output paths to minimize duplicate work. Despite that, it’s still a pain to wait for the application to start and all that jazz.</p> <p>What if we could do the super fast feedback loop development, in a static world? Well, now you can!</p> <p>It starts with a simple function:</p> <div id="codeSnippetWrapper"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet">function replace-command([<span style="color: #0000ff">string</span>]$msg = <span style="color: #006080">'hello world'</span>) {<br> $action = { [system.windows.messagebox]::show($msg) }.GetNewClosure()<br> $cmd = <span style="color: #0000ff">new</span>-<span style="color: #0000ff">object</span> galasoft.mvvmlight.command.relaycommand([system.action]$action)<br> $selected.target.command = $cmd<br>}<br></pre><br></div>
<p>The above function will replace anything that has a <em>Command</em> property on the target, like a Button or MenuItem, with a MessageBox showing a message. For the curious, <em>GetNewClosure</em> is needed so that $msg is available within the inner script block. Unlike C#, closures are not automatic.</p>
<p>Since PowerShell is dynamic, if you need to make a change, simply save the script, reload it with a dot-source, which will overwrite the existing function, and then set the target’s <em>Command</em> property again. Awesome!</p>
<p>The only annoyance is converting PowerShell code back into C# code once you’re done.</p>
<h2>Evolution</h2>
<p>If you made it this far you didn’t forget about my comment about untargeting .NET 4 and PS v3. Well, changes have been <a href="https://github.com/cplotts/snoopwpf/commit/16030418b14778029d10e198b288b4efa9bad65c">merged into the main branch</a>! Soon the masses will be able to experiment with supercharging their applications with PowerShell!</p>
<p>I’ll likely continue working on my <a href="https://github.com/bling/snoopwpf">fork</a> as there’s still more goodies I’d like to add. Stay tuned!</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-27506246427456590922012-07-01T13:28:00.001-04:002012-07-01T13:30:02.695-04:00SnoopShell: The marriage of Snoop WPF and PowerShell<p>I was given the opportunity to review a couple chapters of the excellent book <a href="http://shop.oreilly.com/product/0636920024491.do">PowerShell for Developers</a>, written by my colleague <a href="http://dougfinke.com/blog/">Doug Finke</a>. One of the concepts in the book was embedding a PowerShell console into your application. This idea is ingenious and we added this feature to our client’s software, and so far it has increased our productivity and opened the doors to many possibilities.</p> <p>So what’s so cool about embedding a shell into your application? Well, for starters, one of the immediate advantages is that it gives you the opportunity to test your application at run time. If you are implementing the MVVM pattern then basically anything you can see in the UI is bound to some property in your view model. What if you could expose an instance of your view model to the PowerShell console? Yes, you would be able to interact with it directly, change values, and property change notification will kick in and update the UI.</p> <p>The possibilities start to open up from there. You can start scripting out common tasks – write once, run many times. Or you can write a full fledge test suite as a script, give it to a QA tester, and have them run through it as a special kind of integration testing, one that happens with live, real data. Or how about being able to modify code, <em>at runtime</em>, to try out an implementation without need to recompile or restart the application? Sounds pretty awesome to me!</p> <p>With this, I started thinking why don’t I try and add this to Snoop? It’s a staple tool for any WPF developer, and adding scripting capabilities to Snoop will make it even more useful.</p> <p>So, I sat down for a weekend and took a shot at it. And with that, SnoopShell was born!</p> <p>My fork of Snoop can be found here: <a href="https://github.com/bling/snoopwpf">https://github.com/bling/snoopwpf</a></p> <p>It’s still in super-duper alpha, so features/ideas are still getting formulated, but here’s a glimpse of what it can do now.</p> <p>The $root variable points to the root of the tree. As you can see, Snoop represents this as a ApplicationTreeItem, which has a bunch of properties, the important ones being IsSelected and IsExpanded.</p> <p><a href="http://lh6.ggpht.com/-G1xK0p7454U/T_CIl9YKnlI/AAAAAAAAAJg/z4SEb20go04/s1600-h/image%25255B29%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-NgT7mhbVSBY/T_CImZIe6NI/AAAAAAAAAJk/fkNeG2UGFH0/image_thumb%25255B17%25255D.png?imgmax=800" width="640" height="381"></a></p> <p>Let’s try interacting with the object by setting the IsExpanded to true.</p> <p><a href="http://lh4.ggpht.com/-uKcevwtnUO4/T_CImnGxyWI/AAAAAAAAAJo/5v1AZnM_SHw/s1600-h/image%25255B28%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-STnkYWGudRU/T_CInKa1nyI/AAAAAAAAAJs/qk92Zjm3BWU/image_thumb%25255B16%25255D.png?imgmax=800" width="640" height="383"></a></p> <p>So far so good. Now let’s find my username using Ctrl+Shift. The $selected variable is automatically synchronized with the selected item in the tree.</p> <p><a href="http://lh6.ggpht.com/-uUeaZMYbuDc/T_CInhNnmAI/AAAAAAAAAJw/Q--l4hrlEis/s1600-h/image%25255B27%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-hnDhWbHtn8w/T_CIoIzVSCI/AAAAAAAAAJ0/BiqbUMnfigM/image_thumb%25255B15%25255D.png?imgmax=800" width="640" height="379"></a></p> <p>Let’s do some black magic and change my name.</p> <p><a href="http://lh6.ggpht.com/-hC4FVTnZDxc/T_CIoTrYfiI/AAAAAAAAAJ4/aQQGFbrxCzM/s1600-h/image%25255B26%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-RqOCvfsU6cU/T_CIo6zvtcI/AAAAAAAAAJ8/e8XMWPj60BM/image_thumb%25255B14%25255D.png?imgmax=800" width="640" height="382"></a></p> <p>Finally, let’s find every ListBox in the application. <em>Find-Item</em> is used to recursively find everything in the visual tree which is a ListBox.</p> <p><a href="http://lh6.ggpht.com/-nlycDcpP-bk/T_CIpXMcWxI/AAAAAAAAAKA/gIGruuduR9g/s1600-h/image%25255B25%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-oAB7IIj6LLY/T_CIqDPWS6I/AAAAAAAAAKE/aX1DNZnIOVo/image_thumb%25255B13%25255D.png?imgmax=800" width="640" height="367"></a></p> <p>And from here, it’s as simple as grabbing the <em>DataContext</em> of any control to get access to the view model.</p> <p>By the way, this is targeting PowerShell V3, so you will need to have the RC installed.</p> <p>Try it out and let me know what you think!</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-37997030851226141132012-06-08T09:05:00.000-04:002012-06-16T11:42:11.987-04:00N2N: .NET 2 Node: Part 1Let’s get some actual coding done. If you want some basic prologue, check out the first part of my series <a href="http://blingcode.blogspot.com/2012/06/n2n-net-2-node.html">here</a>.<br>Anywho, let’s get to the meat of what we’re trying to accomplish. I will start with the typical C# class you would write, do the same in Javascript, and then again in C#, but using the style of Javascript using closures.<br>Here’s the use case:<br> <ul> <li>Handle a web request <li>Check the MongoDB database to see if an entry exists and return it if available. <li>Otherwise, query an external API for data, save it into MongoDB, and then return that. </li></ul> <p>This is a pretty standard straight-forward approach in web applications. If you’re not using MongoDB, you’ll be using memcache, Redis, or some other equivalent data store.<br><br>First, let’s implement an pseudo-code ASP.NET MVC controller which does the above:</p> <div id="codeSnippetWrapper"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> QuoteController : Controller<br>{<br> MongoCollection collection;<br> IExternalApi external;<br><br> <span style="color: #0000ff">public</span> ActionResult Get(<span style="color: #0000ff">string</span> symbol)<br> {<br> var quote = <span style="color: #0000ff">this</span>.collection.FindOne(<span style="color: #0000ff">new</span> { symbol });<br> <span style="color: #0000ff">if</span> (quote == <span style="color: #0000ff">null</span>)<br> {<br> var json = <span style="color: #0000ff">this</span>.external.GetQuote(symbol);<br> <span style="color: #0000ff">this</span>.collection.Save(json);<br> }<br><br> ViewBag.Quote = quote;<br> <span style="color: #0000ff">return</span> View();<br> }<br>}<br></pre><br></div>
<p>Error handling and initialization are omitted for brevity, but it’s an otherwise straight-forward synchronous send JSON to the browser response. So how does something like this look in Node? First, let’s throw in a web framework. I picked Express because it seems to have the most traction right now. Again, for brevity I will omit a bunch of boot strapping code, and skip to the core logic.<br><br>
<div id="codeSnippetWrapper">
<div id="codeSnippetWrapper"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet">var QuoteService = function() {<br> var mongoCollection; <span style="color: #008000">// new MongoCollection</span><br> var external; <span style="color: #008000">// new External API</span><br><br> <span style="color: #008000">// req/res are the request/response passed in by Node/Express</span><br> <span style="color: #0000ff">this</span>.get = function(symbol, req, res) {<br> mongoCollection.findOne({ symbol: symbol }, function(error, item) {<br> <span style="color: #0000ff">if</span> (item === <span style="color: #0000ff">null</span>) {<br> external.getQuote(symbol, function(error, result) {<br> mongoCollection.save(result);<br> req.write(result);<br> req.end();<br> });<br> } <span style="color: #0000ff">else</span> {<br> req.write(item);<br> req.end();<br> }<br> });<br> };<br>};<br>module.exports = QuoteService;<br><br></pre><br></div>OK, looks a lot different! The first thing you’ll notice is that the class is actually a function. Javascript doesn’t have real classes like C# or Java, but you can simulate things like private and public members by using closures. In the code snippet above, <em>mongoCollection </em>and <em>external</em> are private variables. However, the <em>get</em> function is public because it’s prefixed with <em>this</em>. Finally, because Javascript has closures you can still access the private variables and retain their state.<br><br>Last but not least, as far as programming in Node is concerned, every function returns void. Node is extremely fast – but it is still single threaded, which means you need to take extreme care not to block on any operation. In summary, you need to get used to working with callbacks. In the example above, the callback for the Mongo query invokes another function, which again uses a callback for the result. Typically any errors are also passed through to the callback, so in place of try/catch blocks, you will be checking to see if the error parameter has a value.<br><br>To conclude, let’s write a C# version that mimics the Javascript style.<br><br>
<div id="codeSnippetWrapper">
<div id="codeSnippetWrapper"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> Main(<span style="color: #0000ff">string</span>[] args)<br>{<br> var QuoteService = <span style="color: #0000ff">new</span> Func<dynamic>(() => {<br> var mongoCollection = <span style="color: #0000ff">new</span> MongoCollection();<br> var external = <span style="color: #0000ff">new</span> IExternalApi();<br> <span style="color: #0000ff">return</span> <span style="color: #0000ff">new</span><br> {<br> get = <span style="color: #0000ff">new</span> Action<<span style="color: #0000ff">string</span>, Request, Response>((symbol, req, res) =><br> {<br> mongoCollection.FindOne(<span style="color: #0000ff">new</span>{symbol}, (error, item) => {<br> <span style="color: #0000ff">if</span> (item == <span style="color: #0000ff">null</span>)<br> {<br> external.GetSymbol(symbol, (e, result) => {<br> mongoCollection.Save(result);<br> req.Write(result);<br> req.End();<br> });<br> }<br> <span style="color: #0000ff">else</span><br> {<br> req.Write(item);<br> req.End();<br> }<br> });<br> })<br> }});<br>}</pre><br></div>100% valid C# syntax.<br><br>Would you ever want to do that in C#? Probably not. But if you need some mental conversion from C# to/from Javascript, I think it’s a good place to start.<br><br>In summary, Javascript is kind of like programming in C# using only anonymous classes, Action/Func, dynamic, declared all in the main method.</div></div></p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-60073288516300005942012-06-03T15:12:00.001-04:002012-06-03T15:12:03.132-04:00N2N: .NET 2 Node<p>Well, it’s been quite a while since I’ve blogged about…well…anything, and I figured it’s about time I get off my lazy butt and do something with my spare time on weekends. What better option than to see what all the hype is about Node? I had to do it sooner or later.</p> <p>As any newbie would do, they go to Google and type “nodejs tutorial”. <a href="http://www.nodebeginner.org/">The Node Beginner Book</a> came up first, so I went with that. It was an excellent tutorial. Prior to this I also skimmed through the book <a href="http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742">JavaScript, The Good Parts</a>, so I had a basic understanding of the language syntax.</p> <p>One of the first oddities I noticed, was that NodeJS seems to have a convention of comma-first. You notice this immediately because most examples start with require(‘module’), and if they require more than one module, the second line is prefixed with a comma (as opposed to the more traditional comma at the end of the line). I apparently missed the <a href="https://gist.github.com/357981">discussion</a> by 2 years! It was still interesting nonetheless.</p> <p>As someone with a strong .NET background, I definitely experienced all the usual ‘gotchas’:</p> <ul> <li>== vs === <li>falsey values <li>variable hoisting</li></ul> <p>Once you understand all of these things, Javascript isn’t so bad. Oh, and of course understanding closures will get you <em>a long</em> way in being effective with Javascript, because that’s what you need to use to do proper scoping. If C# didn’t have lambdas and closures it would have been a much longer journey to “get it”.</p> <p>Not too longer after, I deployed my first Heroku app running on NodeJS.</p> <p>Anyways, enough with the prologue…I won’t bore you with anymore beginner/tutorial stuff.</p> <p>Let’s get on with what I plan on doing over a multi-part blog series. When I build something on my own time, I can’t build something just for the hell of it to learn something….that’s not enough. If I build something it has to be useful – something that I (or someone else) will find valuable.</p> <p>I won’t reveal what it is yet, but it’s going to involve Node/MongoDB on the backend, with Backbone on the front-end. Should be fun :-)</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-43637583291858307112011-10-31T18:45:00.001-04:002011-10-31T21:11:20.458-04:00My Thoughts on MEF<p>Ever since MEF was conceived, despite the authors saying that it is <strong>not<em> </em></strong>an IoC container, it has since evolved to become one of the more popular IoC containers. I’ve always avoided it because I disagree with using attributes, and I’ve had no reason to use it over Autofac or Windsor.</p> <p>Recently, I found a reason to use it – Metro-style applications only support MEF so far. My Twitter client ping.pong uses Autofac as the IoC container. It uses some very basic functionality like factories and hooks. To my surprise, MEF has no support for either of these.</p> <p>Coming across these limitations solidifies my opinion that MEF is a plugin container, not an IoC container.</p> <p>First let’s take a look at automated factories. What I mean is that by registering Foo, like so:</p> <div id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">container.RegisterType<Foo>();</pre><!--CRLF--></div></div>
<p>the container will automatically provide us a Func<Foo> without explicitly having to register it. This can be useful when you want to create an instance of Foo some time in the future rather than at constructor time. You can do this with MEF via an ExportFactory<T>, but it’s limited because you cannot override dependencies at resolve time.</p>
<p>For example, let’s say Foo has a constructor of Foo(Bar1, Bar2, Bar3). With MEF, you have no control at resolution time what the Bars are. A container that has support for automated factories (like Autofac and Castle Windsor), will let you resolve a Func<Bar1, Foo>, which lets you override Bar1 at resolve time. Similarly, you can resolve a Func<Bar1, Bar2, Bar3, Foo> and override all dependencies. Any dependencies not overridden fall back to their configuration in the bootstrapper. This is a <em>very</em> useful feature, and coupled with the scoping features for automatic disposal it opens up many doors for elegant solutions for what otherwise are complicated problems.</p>
<p>On to the second point; MEF has limited extension points. This one sounds odd since MEF is all about designing decoupled plugins so surely it should have extension points! The problem here is that MEF is designed as an explicit API (attributes are required) rather than an implicit API. In Autofac, you can scan an assembly and register every type. In MEF, every class needs to have an [Export] on it. It also baffles my mind why [ImportingConstructor] is required even when there’s only one constructor. All this explicitness means you lose a bunch of “free” extension points that typical IoC containers have, like this:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">b.RegisterAssemblyTypes(GetType().Assembly)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .OnActivated(x => x.Context.Resolve<IEventAggregator>().Subscribe(x.Instance));</pre><!--CRLF--></div></div>
<p>What the code above is saying that every time <em>any</em> component is activated, it will subscribe to the event aggregator. If the component doesn’t IHandle<> any messages, it’s a no-op and continues on. If the instance does IHandle<> messages, this will ensure it’s hooked up.</p>
<p>The closest thing I could find in MEF was IPartImportsSatisfiedNotification (yes, an interface, more explicitness!). It contains a single method OnImportsSatisfied() which gets called when the part is created. Needless to say, the one line of code from Autofac would translate into a method for every implementation of IHandle<>, and since OnImportsSatisfied() contains no contextual information, every component will need IEventAggregator injected just to be able to call Subscribe.</p>
<p>To fully complete this example, Autofac has the following methods when registering a component: <em>OnRegistered, OnPreparing, OnActivating, OnActivated, </em>and<em> OnRelease</em>. Each of these methods gives you complete contextual information at the time it is called like access to the current scope of the container, the instance (if applicable), which component which requested the dependency, etc. This makes it almost too easy to extend the container.</p>
<p>For MEF, the only real extension point is an ExportProvider. It is pretty low level (all it does is parse attributes for you) so to write anything similar for MEF requires a lot more code. To further illustrate this point, compare the interception modules from AutofacContrib and MefContrib. The Autofac implementation is a single file with a couple extension methods. The MEF implementation is an entire namespace, over multiple classes, not the mention that it also relies on other infrastructure code in MefContrib. Basically, the guys that wrote MefContrib had to write a mini-container within MEF.</p>
<p>MEF is great for building <em><strong>extremely</strong> loosely coupled </em>applications. I don’t think it has any business in an application where you know and own all of the dependencies; there are simply better libraries for that.</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com2tag:blogger.com,1999:blog-3157881265969801285.post-71737295618811314632011-10-09T13:32:00.001-04:002011-10-10T22:03:17.117-04:00ping.pong Twitter Client<p>I have a whole <a href="http://blingcode.blogspot.com/2011/09/push-driven-development-with-reactive.html">series</a> dedicated to blogging about how I wrote a Twitter client from scratch, but if I want anyone to actually use it, I better do some advertising :-D.</p> <p>ping.pong is a fast and lightweight Twitter client written in Silverlight. As of this moment it targets v4 but will likely target v5 whenever that is released.</p> <p>Here are some highlights…</p> <h3>Visually Pleasing</h3> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-9wwcsDIu5Yg/TpHayG-NVXI/AAAAAAAAAHg/-Jo9iq0ghOA/image%25255B15%25255D.png?imgmax=800" width="377" height="280"></p> <p>The UI is based on your typical column-based design. The column widths will automatically resize to take up all available horizontal space. There is no horizontal scrolling.</p> <h3><strong>Access to the Streaming API</strong></h3> <p>No more rate limits! Your timeline is connected directly with Twitter’s streaming API, which means whenever someone you follow tweets you will know about it in almost near real-time. Searching is also done through the streaming API.</p> <h3>Fast & Lightweight</h3> <p>ping.pong was built to run fast with low CPU utilization. Even when stream searching for “e” (yes, the letter E), the CPU usage stays under 15%. The maximum number of tweets that Twitter sends appears to be 50 per second.</p> <h3>Conversations</h3> <p>ping.pong will quickly show an entire tweet conversation by navigating reply tags back to the original tweet that started it all.</p> <h3>Free & Open Source</h3> <p>The full source code for ping.pong can be found on <a href="https://github.com/bling/Ping.Pong">GitHub</a>. You can compile it, make modifications as you please, and run it yourself. The only thing missing is the consumer keys which uniquely identifies this client from another. You can generate them through Twitter once you have a developer account.</p> <h3>Installer</h3> <p>The latest and greatest can be quickly installed from <a href="http://dl.dropbox.com/u/2072014/Ping.Pong/PingPongTestPage.html">here</a>. Check back periodically for updates!</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-36585861595353495402011-09-29T22:04:00.001-04:002011-10-02T11:40:33.991-04:00Push Driven Development with Reactive Extensions<p><br>This is going to be the last post that concludes my series on building a real-time push app with Silverlight. Any additional posts would likely be outside the context of writing a push app and more about how I’m adding features to ping.pong, my Twitter app, so I think this is a good place to wrap up and talk generally from a top down overview of building a push-style application.</p> <p>Here’s a recap of everything discussed so far:</p> <p><a href="http://blingcode.blogspot.com/2011/08/building-real-time-push-app-with.html">Part 1</a>: Basics – Creating an Observable around a basic HTTP web stream against Twitter’s streaming API</p> <p><a href="http://blingcode.blogspot.com/2011/08/building-real-time-push-app-with_27.html">Part 2</a>: Subscription and Observation of Observables</p> <p><a href="http://blingcode.blogspot.com/2011/08/building-real-time-push-app-with_28.html">Part 3</a>: Basics of UX design with a look at shadows and gradients.</p> <p><a href="http://blingcode.blogspot.com/2011/09/building-real-time-push-app-with.html">Part 4</a>: Integrating with 3rd party libraries, notably Caliburn Micro and Linq2Twitter and how to achieve polling with observables.</p> <p><a href="http://blingcode.blogspot.com/2011/09/building-real-time-push-app-with_08.html">Part 5</a>: A minor hick up with Linq2Twitter.</p> <p><a href="http://blingcode.blogspot.com/2011/09/building-real-time-push-app-with_13.html">Part 6</a>: Taking advantage of transparencies to improve the design and reusability of UX.</p> <p><a href="http://blingcode.blogspot.com/2011/09/building-real-time-push-app-with_16.html">Part 7</a>: A summary of all things encountered so far, replacing Linq2Twitter with Hammock, first release of code to <a href="https://github.com/bling/Ping.Pong">GitHub</a>, and a binary released capable pulling and streaming tweets from Twitter.</p> <p><a href="http://blingcode.blogspot.com/2011/09/building-real-time-push-app-with_21.html">Part 8</a>: Examples of using Caliburn Micro to easily resolve data bindings that otherwise would be much more effort.</p> <p>And that leads us to this post…</p> <h3>PDD (Push Driven Development)</h3> <p>One of the main goals of this series is to create a performant Silverlight app based on push principles, as opposed to more traditional pull principles. To that effect, ping.pong has performed remarkably well and is limited only by Twitter’s throttling, which currently appears to be maximum of 50 tweets per second via the streaming API.</p> <p>Writing the application from a push-driven mindset was definitely unintuitive at first, and I had to refactor (actually rewrite is more accurate) the code many times to move closer to a world where the application is simply <em>reacting</em> to events thrown at it (as opposed to asking the world for events).</p> <p>To be absolutely clear on what I mean on the differences between push and pull, here’s a comparison:</p> <table border="0" cellspacing="0" cellpadding="2" width="100%"> <tbody> <tr> <td valign="top"><strong>Pulling</strong></td> <td valign="top"><strong>Push</strong></td></tr> <tr> <td valign="top" width="50%">var e = tweets.GetEnumerator();<br>while (e.MoveNext()) <strong>// is there more?<br></strong>{<br> e.Current; <strong>// get current<br></strong> DoSomething(e.Current);<br>}</td> <td valign="top" width="50%">IObservable<Data> data = /* get source */<br><br><strong>// whenever data comes, do something</strong><br>data.Subscribe(DoSometing); <br></td></tr></tbody></table> <p>On the pulling side, the caller is much more concerned with the logic on how to process each message. It needs to <em>repeatedly</em> ask the world, “hey, is there more data?”.</p> <p>On the push side, the caller merely asks the world, “hey, give me data when you get some”.</p> <p>Twitter is a perfect example because their APIs have both a pulling and pushing models. Traditional clients poll continuously all the time, and many had configurable options to try to stay under the 200 API calls per hour limit. Most of Twitter’s API still consists of pulling, but the user’s home line and searching can be streamed in near real time via the streaming API, aka. push. Streaming tweets effectively removes the API call limit.</p> <h3></h3> <h3>Push and Pull with Reactive Extensions</h3> <p>The beauty of Rx is that regardless of whether it is actually pushing or pulling, the API will look same:</p> <div id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">IObservable<Tweet> tweets = _client.GetHomeTimeline();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">tweets.Subscribe(t => { <span style="color: #008000">/* do something with the tweet */</span> });</pre><!--CRLF--></div></div>
<p>As far as the caller is concerned, it doesn’t care (or needs to know) whether the <em>GetHomeTimeline</em> method is polling Twitter or streaming from Twitter. All it needs to know is <em>when</em> a tweet comes it will <em>react</em> and do something in the Subscribe action.</p>
<p>In fact, Subscribe simply means “when you have data, call this”, but that could also be immediately, which would be analogous to IEnumerable.</p>
<p>However, if that was the only thing Rx provided it wouldn’t be as popular as it is, because other pub/sub solutions like the EventAggregator already provide a viable asynchronous solution.</p>
<p>Unlocking Rx’s power comes with its multitude of operators. Here’s an example:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> IObservable<Tweet> GetStreamingStatuses(<span style="color: #0000ff">this</span> TwitterClient client)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">return</span> client.GetHomeTimeline()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Merge(client.GetMentions())</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Concat(client.GetStreamingHomeline());</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">}</pre><!--CRLF--></div></div>
<p><em>GetHomeTimeline</em> and <em>GetMentions</em> initiate once-only pull style API calls, while <em>GetStreamingHomeline</em> will initiate a sticky connection and stream subsequent tweets down the pipe.</p>
<p>The <em>Merge</em> operator is defined as this: “Merges an observable sequence of observable sequences into an observable sequence.”</p>
<p>I think a better description would be “whenever there is data from any of the sources, push it through”. In the example above, this would translate to whenever a tweet comes from either the home timeline or the mentions timeline, give me a Tweet (first-come-first-push), followed by anything from the streaming timeline.</p>
<p>And there lies one of the greatest beauties of Rx. <em>All</em> of the complexity lies solely on setting up the stream and operators. And that, also, is its disadvantage.</p>
<h3></h3>
<h3>Rx Complexity</h3>
<p>Let’s take a look at the <em><a href="http://msdn.microsoft.com/en-us/library/hh212146(v=VS.103).aspx">Concat</a></em> operator, defined as: “Concatenates two observable sequences.” In the remarks sections it states this: “The Concat operator runs the first sequence to completion. Then, the second sequence is run to completion effectively concatenating the second sequence to the end of the first sequence.”</p>
<p>Let’s try it out:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">var a = Observable.Interval(TimeSpan.FromSeconds(1)).Select(x => x.ToString());</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">var b = Observable.Interval(TimeSpan.FromSeconds(1)).Select(x => <span style="color: #006080">"s"</span> + x);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">a.Concat(b).Subscribe(Console.WriteLine);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">// output: 0, 1, 2, 3, 4...</pre><!--CRLF--></div></div>
<p>As expected, only numbers are printed because the first sequence never ends, so it won’t concatenate the second one. Let’s modify it so that it does finish:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">int</span> count = 0;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">var a = Observable.Interval(TimeSpan.FromSeconds(1))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .TakeWhile(_ => ++count < 5)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Select(x => x.ToString());</pre><!--CRLF--></div></div>
<p>Note, that using Observable.Generate is preferred because it doesn’t introduce an external variable, but I stuck with Interval so the code looks similar to the second observable. As expected again, it will print “0, 1, 2, s0, s1, s2”.</p>
<p>OK, let’s spice things up. Let’s make <em>b</em> a ConnectableObservable by using the <em><a href="http://msdn.microsoft.com/en-us/library/hh229126(v=VS.103).aspx">Publish</a></em> operator, and immediately call <em>Connect</em>.</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">int</span> count = 0;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">var a = Observable.Interval(TimeSpan.FromSeconds(1))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .TakeWhile(_ => ++count < 5)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Select(x => x.ToString());</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">var b = Observable.Interval(TimeSpan.FromSeconds(1)).Select(_ => <span style="color: #006080">"s"</span> + _).Publish();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">b.Connect();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">a.Concat(b).Subscribe(Console.WriteLine);</pre><!--CRLF--></div></div>
<p>What do you think the output of this will be? The answer is “0, 1, 2, 3, <strong>s5, s6, s7</strong>, …”</p>
<p>Despite using the same <em>Concat</em> operator, the result can be very different depending on the source observables. If you use the <em><a href="http://msdn.microsoft.com/en-us/library/hh229288(v=VS.103).aspx">Replay</a></em> operator, it would have printed “0, 1, 2, 3, s0, s1, s2, …”</p>
<p>Years and years of working in synchronous programming models have trained us to think in synchronous ways, and I picked Concat specifically because Concat also exists in the enumerable world. Observable sequences are asynchronous, so we never know exactly <em>when</em> data comes at us, only what to do when it does. And because streams occur at different times, when you combine them together there are many many ways of doing so (CombineLatest, Merge, Zip, are just a few).</p>
<p>The greatest hurdle to working in Rx is to know what the different combinations do. This takes time and practice. <a href="http://mnajder.blogspot.com/2010/03/rxsandbox-v1.html">RxTools</a> is a great learning tool to test out what all the operators do.</p>
<h3><strong>Unit Testing</strong></h3>
<p>Last but not least, Rx can make it easier to write unit tests. The concept is easy: take some inputs and test the output. In practice this is complicated because applications typically carry a lot of state with them. Even with dependency injection and mocking frameworks I’ve seen a lot of code where for every assert there is 10 lines of mock setup code.</p>
<p>So how does it make it easier to test? It reduces what you need to test to a single method, Subscribe, which takes one input, an IObservable<T>.</p>
<h3>Conclusion</h3>
<p>Rx is a library unlike any other you will use. With other libraries, you will add them to your solution, use a method here or there, and go on with your life. With Rx, it will radically change the way you code and think in general. It’s awesome.</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-28105077676971036782011-09-21T23:25:00.001-04:002011-09-21T23:25:15.065-04:00Building a Real-time Push App with Silverlight: Part 8<h3> </h3> <h3>Exploring Caliburn Micro</h3> <p>As I hinted in earlier posts, Caliburn Micro has some wicked conventions that makes for writing MVVM super easy, and it also have a very convenient syntax for hooking up events. For example, the following:</p> <div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"><</span><span style="color: #800000">Button</span> <span style="color: #ff0000">Content</span><span style="color: #0000ff">="R"</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">i:Interaction.Triggers</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">i:EventTrigger</span> <span style="color: #ff0000">EventName</span><span style="color: #0000ff">="Click"</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">i:InvokeCommandAction</span> <span style="color: #ff0000">Command</span><span style="color: #0000ff">="{Binding ReplyCommand}"</span> <span style="color: #ff0000">CommandParameter</span><span style="color: #0000ff">="{Binding}"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">i:EventTrigger</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">i:Interaction.Triggers</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"></</span><span style="color: #800000">Button</span><span style="color: #0000ff">></span></pre><!--CRLF--></div></div>
<p>Can be rewritten like this:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"><</span><span style="color: #800000">Button</span> <span style="color: #ff0000">Content</span><span style="color: #0000ff">="R"</span> <span style="color: #ff0000">cal:Message</span>.<span style="color: #ff0000">Attach</span><span style="color: #0000ff">="[Reply($dataContext)]"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--></div></div>
<p>There are some smarts going on here. Caliburn Micro will default to the Click event for buttons. For a full syntax, it would be cal:Message.Attach=”[Event Click] = [Reply($dataContext)]”. As you can imagine, that will call the Reply method and pass in the current data context. You can also pass in other things like $this, $source, or $executionContext for full access to anything and everything Caliburn Micro itself has access to.</p>
<p>The coolest thing about this is it gives you some wicked control over how your data context gets set. Ever struggled with popup windows or data grids and using weird hacks to get the binding correct? Caliburn Micro makes this very easy. Here’s an example.</p>
<ol>
<li>I have a DataTemplate which renders the UI for the model Tweet.</li>
<li>Tweet is just a simple class which holds only properties.</li>
<li>Inside the DataTemplate, I have some buttons that when the user clicks will reply, retweet, quote, or direct message.</li></ol>
<p>The Tweet class is purely for modeling data, so adding any methods would be bad practice. Also, since I’m in a DataTemplate I can’t easily reference another control with ElementName (in this case I need the containing parent’s DataContext). And to add insult to injury, Silverlight 4 doesn’t have RelativeSource ancestor type. So how do I solve this?</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"><</span><span style="color: #800000">StackPanel</span> <span style="color: #ff0000">VerticalAlignment</span><span style="color: #0000ff">="Bottom"</span> <span style="color: #ff0000">cal:Action</span>.<span style="color: #ff0000">TargetWithoutContext</span><span style="color: #0000ff">="shell"</span> <span style="color: #ff0000">Orientation</span><span style="color: #0000ff">="Horizontal"</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Button</span> <span style="color: #ff0000">Content</span><span style="color: #0000ff">="R"</span> <span style="color: #ff0000">cal:Message</span>.<span style="color: #ff0000">Attach</span><span style="color: #0000ff">="[Reply($dataContext)]"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Button</span> <span style="color: #ff0000">Content</span><span style="color: #0000ff">="RT"</span> <span style="color: #ff0000">cal:Message</span>.<span style="color: #ff0000">Attach</span><span style="color: #0000ff">="[Retweet($dataContext)]"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Button</span> <span style="color: #ff0000">Content</span><span style="color: #0000ff">="Q"</span> <span style="color: #ff0000">cal:Message</span>.<span style="color: #ff0000">Attach</span><span style="color: #0000ff">="[Quote($dataContext)]"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Button</span> <span style="color: #ff0000">Content</span><span style="color: #0000ff">="DM"</span> <span style="color: #ff0000">cal:Message</span>.<span style="color: #ff0000">Attach</span><span style="color: #0000ff">="[DirectMessage($dataContext)]"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"></</span><span style="color: #800000">StackPanel</span><span style="color: #0000ff">></span></pre><!--CRLF--></div></div>
<p>The secret is the attached property TargetWithoutContext. As the name implies, it will set the target for all the ActionMessages attached to all the buttons, without setting the context. If I used the Target attached property, it would set all of the Buttons’ data context to the same object – not what we want. Since the Button’s data context remains intact, we can call “Reply($dataContext)”, which calls the Reply method on the target object (set on the StackPanel) and pass in the Tweet. “shell” is the key of the service that I registered into the container.</p>
<p>Originally I wanted this entire series to be able writing a fast push data app with Silverlight and Rx, and now I’m finding that I’m writing an entire Twitter client because it’s so much fun :-).</p>
<p>I’m going to make another release soon. While the first release was merely experimental, the next one will be useful enough to potentially use full time. As you can probably tell with this blog post, it supports all the actions mentioned previously (and it’ll appear on mouse hover):</p>
<p><a href="http://lh3.ggpht.com/-glwUZLmbyns/Tnqql5gBCJI/AAAAAAAAAHI/e-PtgGCjy9Y/s1600-h/image%25255B4%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-RIZG8I0_rik/TnqqmNCK-6I/AAAAAAAAAHM/-xU4_cj6iDg/image_thumb%25255B2%25255D.png?imgmax=800" width="319" height="85"></a></p>
<p>The tweet box is much improved and shows you how many character you have left:</p>
<p><a href="http://lh4.ggpht.com/-cGf_6wVWvPg/TnqqmTUoW9I/AAAAAAAAAHQ/HG2IM82v9sY/s1600-h/image%25255B7%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-2VhCjjYrcoo/TnqqmstbbUI/AAAAAAAAAHU/zK65JB2shQw/image_thumb%25255B3%25255D.png?imgmax=800" width="244" height="92"></a></p>
<p>And it’s smart enough to auto wrap http links via Twitter’s t.co service, and the counter takes that into account. Some interesting things to note is that in the future <em>all links</em> will be wrapped t.co. Looks like Twitter is trying to eat up bt.ly or something.</p>
<p>Clicking on @users and #topics will automatically open a new timeline and subscribe to those tweets. It is almost full featured enough to become my main Twitter client. There are certain features still missing, and it’s purely based on when I have time to port them over.</p>
<p>As always, you can install directly from <a href="http://dl.dropbox.com/u/2072014/Ping.Pong/PingPongTestPage.html">here</a>, or you can grab the code on the <a href="https://github.com/bling/Ping.Pong">GitHub</a> page!</p>
<p>Next post will be about Rx from a very top level perspective and how it influenced my code from beginning to be experienced and all refactorings in between. Stay tuned!</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-88528214395074400252011-09-16T09:26:00.001-04:002011-09-16T22:23:14.785-04:00Building a Real-time Push App with Silverlight: Part 7<h3></h3> <h3>Infrastructure Refactor</h3> <p>A lot of things changed internally, and I mean….a lot….</p> <p>From an infrastructure standpoint, I decided to remove the dependency on LinqToTwitter, and I replaced it with <a href="https://github.com/danielcrenna/hammock">Hammock</a>. A couple things led me to this decision, one being the Silverlight support wasn’t as good as I’d hoped, and the streaming API implementation was limited. After reading the Twitter documentation I realized that the REST API was super simple and I’d be better off writing a simple interface to it.</p> <p>I heard good things about Hammock, so I decided to give that one a try (I wasn’t going to go as far as reimplementing OAuth). It was pretty easy to set up and in the end I was able to get Twitter working again and with less lines of code compared to the beginning of the refactor.</p> <h3>Goals</h3> <p>I had a couple goals for this project:</p> <ul> <li><strong>Learn:</strong> I was a complete newbie to Reactive Extensions when I started but now I understand it enough to hit the ground running with it. I’m still learning about more conventions available to Caliburn.Micro. <li><strong>UX:</strong> I wanted to learn a little more about interface design. I wanted to know how little changes to gradients, shadows, colors, etc. could have a radically effect in the end result. <li><strong>Performance:</strong> It should be fast. It should be able to react to real-time data. And it should do it with low CPU utilization. <li><strong>Concise:</strong> I am a huge advocate for KISS. I like convention over configuration. I like implementing something in 2 lines of code rather than 20 (assuming it’s not cryptic). As I was writing the app and refactoring, if there was an opportunity to remove a line of code, I did it. The result is that the app currently consists of less than 500 lines of code as of this post (excluding XAML).</li></ul> <h3>Tidbits</h3> <p>What are some interest things I learned?</p> <ul> <li>System.Json is an amazing assembly. All you need to do is invoke JsonValue.Parse on a string and it will create a JsonValue for you, which will be a dictionary of key/value pairs. What’s more, by doing something like “string s = json[“text”]” will do an explicit conversion <strong>and unescape JSON characters</strong>, and <strong>only</strong> via the explicit operator. Calling ToString(), even though converting it to a string, will not unescape. This was completely undocumented and only found when I looked at the source code via Resharper’s external sources feature. <li>Rx is awesome. When I ran into performance problems of trying to stream tweets from the world that contained the letter ‘a’ all I had to do was add an operator to improve the performance (in this case it was Buffer). It should be noted that it is <em>very important</em> to understand what Rx is doing underneath the hood to realize its benefits. Rx lets you refactor 30 lines of async code into 1 operator, but it’s still doing that 30 lines of code – you just don’t see it. <li>I really, <em>really</em>, like the conventions available from Caliburn. Some of the features that come out of the box from this very small library saves me from writing a lot of boilerplate code like commands, triggers, and evening bindings (Caliburn will auto bind x:Name to a property). <li>Twitter’s documentation for <a href="https://dev.twitter.com/docs/streaming-api/user-streams">user streams</a> currently sucks and some trial and error was required to get it working.</li></ul> <p>What is the end result of all this effort? We have a styled Twitter app that can update your status, pull your home/mentions timeline, and most importantly will <strong>stream all subsequent tweets</strong>. There’s no pulling and no limits. You will get a tweet of everyone you follow in real-time as it happens.</p> <p>Moreover, there’s a feature to connect to the Streaming API to search Twitter for <em>anything</em>. To get an idea of what we’re talking about, here’s a full screenshot of it:</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-tB6iLGL85aE/TnNOi7hdq9I/AAAAAAAAAHE/wGjkyHQzYKs/image%25255B20%25255D.png?imgmax=800" width="578" height="328"></p> <p>You read that right. I’m streaming any tweet in the world that has the words ‘and’, ‘the’, ‘yes’, <strong>or</strong> ‘no’ in them. This is streaming around 400kB/s continuously and CPU utilization is under 25%. The tweets are coming so fast it’s impossible to read them (at a rate of 50 tweets/second), so ideally you’d want to specify realistic search terms.</p> <p>Moreover, the majority of the performance cost is actually downloading all the profile images. If I take took out pictures I could stream any tweet in the world that has the letter ‘e’ in it at under 10% CPU. It looks like Twitter limits the rate of tweets to 50 tweets/second because that was the rate for this one as well.</p> <p>Features are minimalistic. You can update your status, but you can’t DM, you can’t RT, you can’t do any of the normal things. My original goal was not to write another Twitter client, but it’s actually quite fun to do so, so I’ll probably eventually get all features in.</p> <p>And as promised, it’s up on <a href="https://github.com/bling/Ping.Pong">GitHub</a>, and version 0.0.0.1 <strong>alpha</strong> (yes! expect bugs!!) is available in the downloads section. Or, here’s a direct link to the XAP file on my <a href="http://dl.dropbox.com/u/2072014/Ping.Pong/PingPongTestPage.html">Dropbox</a>. Have fun!</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-81603711843547915792011-09-13T10:00:00.000-04:002011-09-13T10:00:13.951-04:00Building a Real-time Push App with Silverlight: Part 6<p>Back to the UI!</p> <p>For this post I’m going to restyle the tweets. Recall that they currently look like this:</p> <p><a href="http://lh5.ggpht.com/-8dwHklIFVZU/Tm7ibyiDZFI/AAAAAAAAAGk/qJnwiFOB8f0/s1600-h/image4.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-ooxWWcVmX9w/Tm7icHgdN_I/AAAAAAAAAGo/wQ3qnvYAeWw/image_thumb2.png?imgmax=800" width="313" height="83"></a></p> <p>The gradient background is currently #FFEEEEEE to #FFDDDDDD. For this post I’m going to talk about a very powerful tool in a designer’s arsenal: <em>transparency</em>.</p> <p>Your first may be to think “big deal”, but just like programmers can use base classes and injection to share common code, designers can use transparency to achieve a similar effect.</p> <p>Let’s change change the color to be black, and tweak only the alpha. I’m going to set the colors to be #11000000 to #22000000 on a white background. This is the result:</p> <p><a href="http://lh6.ggpht.com/-JEP92O5RoSY/Tm7icVNyd4I/AAAAAAAAAGs/l6-KdEzjCYQ/s1600-h/image9.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-Ev-xA7pMsak/Tm7icnmllQI/AAAAAAAAAGw/xMZM3yNsGZA/image_thumb5.png?imgmax=800" width="313" height="83"></a></p> <p>Looks almost identical doesn’t it? However, by doing this we have dramatically improved the reusability of the gradient. Here’s what happens when I change the background to be a different color:</p> <p><a href="http://lh5.ggpht.com/-XOqa5ur3DBQ/Tm7iciocdYI/AAAAAAAAAG0/6SNKx6PSN3w/s1600-h/image14.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-uPxkKsog7xU/Tm7ic2vJhpI/AAAAAAAAAG4/J-PMDfe_6EM/image_thumb8.png?imgmax=800" width="313" height="83"></a></p> <p>I just changed one variable to do that. If I wanted to provide different themes for my application it would be <em>extremely</em> easy to do that if all the data templates were built with transparencies. In fact, I just slap on a ColorPicker and I’d be done!</p> <p>Even though it’d be easy to do this, any application that is dominated by one color gets boring very quickly. Most applications that look nice tend to focus around two dominant colors that contrast well against each other. Black and white are very common because they contrast well with a large variety of colors, but you can also have things like blue/green, purple/orange, etc. As always, as long as you’re consistent you’ll likely have a good result.</p> <p>Now, the flip side of the equation is also possible. This is where you have something that exists already and then you put a transparent layer on top of it, creating a lightening or dimming effect. In my experience I’ve found this to be inferior because it tends to wash out colors. In the example above, if I applied a slightly transparent layer over top of the tweet, my picture and text would be negatively affected. This is nonetheless a very useful trick, like with mouse over effects where you want a quick and cheap way of conveying information to the user.</p> <p>Now, let’s take a big detour and restyle the entire application and go with a completely different theme. I also wanted try something besides Apple and Microsoft inspired designs, which was more difficult than expected because I guess I’m not as creative as I thought I was :-). Coming up with a good design takes a long time, and frequently you need some sort of inspiration. Twitter in general is a very simple application, so the best designs are simple as well.</p> <p>In an attempt to try to come up with something “cool” and “unique”, I started with the idea of elevated boxes layered on top of each other. Here’s a before and after once I was done:</p> <p><a href="http://lh4.ggpht.com/-s_ruhKWRmL0/Tm7idDwxdsI/AAAAAAAAAG8/XE18TaWC600/s1600-h/image20.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-Mz_J4B2JDLM/Tm7idcOBx-I/AAAAAAAAAHA/7FGeCgmZzek/image_thumb11.png?imgmax=800" width="625" height="303"></a></p> <p>The redesign went through many iterations. I showed it to friends and colleagues and got mixed feelings. Some liked it. Some thought it was too noisy. And herein revealed a problem with complex designs – they are hard to get right! That, and they tend to divide audience into those that really like it, and those that really don’t.</p> <p>Anyways, the beauty of XAML is that I can try something else entirely without any changes to the code, so I’ll try another theme in the future.</p> <p>I’m about 90% ready to release code to GitHub along with the first public alpha version. Stay tuned!</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-12620372560937078292011-09-08T21:26:00.001-04:002011-09-10T10:10:03.540-04:00Building a Real-time Push App with Silverlight: Part 5<p>I planned on this post to be about UI, but I’m going to defer that until the next post. I said from the start of this series that I would document about everything about building the application from scratch, including my struggles.</p> <p>And with that I want to mention something that got me scratching my head one too many times. It was with how I used LinqToTwitter. Here is the source code which you can immediately copy/paste into a blank project to reproduce:</p> <div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum1"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">partial</span> <span style="color: #0000ff">class</span> MainPage : UserControl</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> TwitterContext _context = <span style="color: #0000ff">new</span> TwitterContext();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> ViewModel _vm1, _vm2, _vm3;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> <span style="color: #0000ff">public</span> MainPage()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum8"> 8:</span> InitializeComponent();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum9"> 9:</span> _vm1 = <span style="color: #0000ff">new</span> ViewModel(_context);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum10"> 10:</span> _vm2 = <span style="color: #0000ff">new</span> ViewModel(_context);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum11"> 11:</span> _vm3 = <span style="color: #0000ff">new</span> ViewModel(_context);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum12"> 12:</span> _vm1.Callback += () => Debug.WriteLine(<span style="color: #006080">"Callback of VM1: "</span> + _vm1.LocalState);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum13"> 13:</span> _vm2.Callback += () => Debug.WriteLine(<span style="color: #006080">"Callback of VM2: "</span> + _vm2.LocalState);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum14"> 14:</span> _vm3.Callback += () => Debug.WriteLine(<span style="color: #006080">"Callback of VM3: "</span> + _vm3.LocalState);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum15"> 15:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum16"> 16:</span> _vm1.Start();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum17"> 17:</span> _vm2.Start();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum18"> 18:</span> _vm3.Start();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum19"> 19:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum20"> 20:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum21"> 21:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum22"> 22:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> ViewModel</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum23"> 23:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum24"> 24:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> TwitterContext _context;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum25"> 25:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">event</span> Action Callback;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum26"> 26:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum27"> 27:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span> LocalState;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum28"> 28:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum29"> 29:</span> <span style="color: #0000ff">public</span> ViewModel(TwitterContext context)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum30"> 30:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum31"> 31:</span> _context = context;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum32"> 32:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum33"> 33:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum34"> 34:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Start()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum35"> 35:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum36"> 36:</span> var query = (from s <span style="color: #0000ff">in</span> _context.Status</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum37"> 37:</span> <span style="color: #0000ff">where</span> s.Type == StatusType.Public && s.Count == 10</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum38"> 38:</span> select s);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum39"> 39:</span> Debug.WriteLine(<span style="color: #006080">"Hash code of ViewModel: "</span> + query.GetHashCode());</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum40"> 40:</span> query.AsyncCallback(statuses =></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum41"> 41:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum42"> 42:</span> LocalState++;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum43"> 43:</span> Debug.WriteLine(<span style="color: #006080">"Hash code inside callback: "</span> + GetHashCode());</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum44"> 44:</span> Callback();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum45"> 45:</span> }).FirstOrDefault();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum46"> 46:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum47"> 47:</span> }</pre><!--CRLF--></div></div>
<p>Now, if you run this, you will see that only <strong>one</strong> of the view models will get its state updated. Huh?!</p>
<p>How is that possible? I started getting paranoid so I even added the local state variable “just in case.”</p>
<p>Well, I had to look into the source code of LinqToTwitter to figure out exactly what happened. Here is the code for AsyncCallback:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> IQueryable<T> AsyncCallback<T>(<span style="color: #0000ff">this</span> IQueryable<T> queryType, Action<IEnumerable<T>> callback)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> (queryType.Provider <span style="color: #0000ff">as</span> TwitterQueryProvider)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Context</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .TwitterExecutor</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .AsyncCallback = callback;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">return</span> queryType;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--></div></div>
<p>See what happened? The callback gets overwritten every time you call this method. Even though the call to <em>FirstOrDefault</em>() causes all 3 expressions to evaluate, only the last view model will get values because that’s the with the callback attached.</p>
<p>Lesson of the day: The AsyncCallback extension method for LinqToTwitter is not thread-safe.</p>
<p>So…the question is, how do we make it thread safe? I just replaced wrapped the AsyncCallback with another extension method:</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">readonly</span> AutoResetEvent _twitterEvt = <span style="color: #0000ff">new</span> AutoResetEvent(<span style="color: #0000ff">true</span>);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> AsyncTwitterCallback<T>(<span style="color: #0000ff">this</span> IQueryable<T> twitter, Action<IEnumerable<T>> callback)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> Observable.Start(() =></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> _twitterEvt.WaitOne();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> twitter.AsyncCallback(results =></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">try</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> callback(results);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">finally</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> _twitterEvt.Set();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }).FirstOrDefault();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> });</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">}</pre><!--CRLF--></div></div>Nothing complicated – just a simple wait handle to ensure only 1 thread can go through at a time.
<p>Hopefully upstream fixes this, or at least documents it.</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-43347026277812454362011-09-05T17:26:00.001-04:002011-09-05T17:49:50.244-04:00Building a Real-time Push App with Silverlight: Part 4<p>Originally I wanted to avoid bringing in external libraries to keep the app as lean as possible, but then I realized that I would spend too much time reinventing the wheel. Twitter is deprecating basic authentication in the near future, which makes OAuth no longer optional. Rather than writing yet another Twitter client (if you’re curious I found a great reference <a href="http://chris.59north.com/post/2009/09/16/SilverTweet-e28093-Building-a-Silverlight-Twitter-client-part-1.aspx">here</a>), I fired up <a href="http://nuget.org/">NuGet</a> and brought in <a href="http://linqtotwitter.codeplex.com/">LinqToTwitter</a>, and while I’m there I brought in <a href="http://code.google.com/p/autofac/">Autofac</a> and <a href="http://caliburnmicro.codeplex.com/">Caliburn.Micro</a> as well.</p> <p>Naturally, LinqToTwitter will work nicely with Rx because as name implies it uses LINQ heavily. Caliburn.Micro is a MVVM library which I’ve always wanted an excuse to try because of features like this:</p> <div id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"><</span><span style="color: #800000">ListBox</span> <span style="color: #ff0000">cal:Message</span>.<span style="color: #ff0000">Attach</span><span style="color: #0000ff">="[Event Loaded] = [LoadList($dataContext)]"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--></div></div>
<p>That’s only scratching the surface of what Caliburn can do, so it will be a fresh breath of air to see what else it can do.</p>
<p>By default, Caliburn uses MEF to wire up its bootstrapper. After adding a couple [Import]s and [Export]s, I knew it wasn’t for me. It works well for writing plugins, i.e. <em>external</em> dependencies because of its built-in assembly scanning capabilities, but for injecting <em>internal</em> dependencies, other IoC containers do a much better job of that. I used Castle Windsor in past projects, but for a change I’m going to use Autofac which I haven’t used since v2 came out.</p>
<p>When this was all said and done the View was the only thing that didn’t change. Everything underneath either changed radically or was deleted altogether (because LinqToTwitter provided it). I added OAuth support and registered my application with Twitter, and with that was the birth of Ping Pong.</p>
<p>This took much longer than expected. Silverlight 5 RC just came out and it broke pretty much any container (including MEF) for OOB because of a TypeLoadException. I haven’t been using too many v5 features, so for the time being I downgraded to v4 to get the project working until RC2 comes out.</p>
<p>Integrating LinqToTwitter was a challenge. The project site has a lot of good documentation, but most of it was for desktop, not Silverlight, and because of that I banged my head a couple times. I wish I grabbed the source code earlier because it’s there where you’ll find hundreds of working examples (in code!) to do everything with the library (and in Silverlight).</p>
<p>After all that, PingPong now has 3 columns (home, public, sampling) that <em>dynamically resizes</em> (it’s surprising that <a href="http://metrotwit.com">MetroTwit</a> is the only client that does this….) to the window size.</p>
<p><a href="http://lh3.ggpht.com/-Upxh69yDp78/TmU-eu-P5tI/AAAAAAAAAGY/P3E3TP6sjfk/s1600-h/image%25255B23%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-f1uQ63FwyoI/TmU-fK3WfQI/AAAAAAAAAGc/Y26EvwnXy_4/image_thumb%25255B15%25255D.png?imgmax=800" width="554" height="413"></a></p>
<p>Oh, and there’s pictures now! The streaming time line takes significantly more CPU now that it has to load images, but we’re still sitting at around 5-10% for what is continuously streaming data and loading pictures. Not too shabby! (It took a couple tries to get a PG-13 screenshot from the public/streaming time lines…)</p>
<p>To conclude this post in the series, I’m going to talk about converting an asynchronous operation into an Observable that does not follow any predefined pattern.</p>
<h2>Creating an Observable</h2>
<p>One of Silverlight’s limitations is that almost everything needs to be an asynchronous call. In regards to LinqToTwitter, something like this will fail (but work on desktop):</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">var tweets = (from t <span style="color: #0000ff">in</span> context.Status</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">where</span> t.Type == StatusType.Public</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> select t).ToArray();</pre><!--CRLF--></div></div>
<p>On Silverlight you will get a single empty element. To get it working, there is an extension method that comes with the library, and you use it like this:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">(from t <span style="color: #0000ff">in</span> context.Status</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">where</span> t.Type == StatusType.Public</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> select t)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .AsyncCallback(tweets => { <span style="color: #008000">/* do something with it */</span> })</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .FirstOrDefault();</pre><!--CRLF--></div></div>
<p>Code is self-explanatory. The FirstOrDefault() exists only to initiate the expression, otherwise it wouldn’t do anything. So now the question is how do we convert that into an Rx Observable?</p>
<p>Every time I write an Rx query I try to use the least amount of state as possible. This helps to keep the number unexpected anomalies to a minimum. In the following section of code, I was able to get it down to 2 fields: _sinceId, and Context. There is probably some operator that will let me save the sinceId variable from one observable to the next but I wasn’t able to figure it out. In any case, I came up with this:</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">_subscription =</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> Observable.Create<Tweet>(</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> ob => Observable.Interval(TimeSpan.FromSeconds(60))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .StartWith(-1)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .SubscribeOnThreadPool()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Subscribe(_ =></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">ulong</span> sinceId;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> (<span style="color: #0000ff">ulong</span>.TryParse(_sinceId, <span style="color: #0000ff">out</span> sinceId)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> ? Context.Status.Where(s => s.Type == statusType && s.Count == 200)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> : Context.Status.Where(s => s.Type == statusType && s.Count == 200 && s.SinceID == sinceId))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .AsyncCallback(statuses =></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">foreach</span> (var status <span style="color: #0000ff">in</span> statuses)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> ob.OnNext(<span style="color: #0000ff">new</span> Tweet(status));</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> _sinceId = status.StatusID;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> })</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .FirstOrDefault(); <span style="color: #008000">// materalize the results</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .DispatcherSubscribe(SubscribeToTweet);</pre><!--CRLF--></div></div>
<p>That contains some custom code:</p>
<ul>
<li>Context: is a TwitterContext from LinqToTwitter
<li>DispatcherSubscribe: is a helper extension method which Subscribes on the ThreadPool, Observes on the Dispatcher, and then Subscribes with the specified action
<li>SubscribeToTweet: a method in the base class which adds to a ObservableCollection so the UI gets updated</li></ul>
<p>To translate the code, here is a basic flow of what’s happening:</p>
<ol>
<li>Observable.Create wraps the subscription of another Observable. It provides access to an IObserver <em>ob</em> which lets you explicitly invoke OnNext().
<li>Observable.Interval will raise an observable every 60 seconds.
<li>The subscription of Observable.Interval will query the TwitterContext for the next set of tweets.
<li>Inside the AsyncCallback, it invokes <em>ob.OnNext </em>as well as keeps track of the ID so the next time it queries it only gets newer tweets.
<li>Finally, <em>DispatcherSubscribe</em> will take the <em>Tweet</em> object and add it to an <em>ObservableCollection<Tweet></em>, which notifies the UI.</li></ol>
<p>As always, you should “clean up your garbage”. In this respect I was pretty impressed with Rx as it was able to clean up the entire chain of observables with a single call to <em>_subscription.Dispose()</em>. Nice!</p>
<p>In the next post I’m going to switch back to UI and completely restyle the application. The code will hit GitHub soon as well (I promise!). Stay tuned…</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com3tag:blogger.com,1999:blog-3157881265969801285.post-17495970898500525382011-08-28T18:21:00.001-04:002011-08-28T22:44:44.087-04:00Building a Real-time Push App with Silverlight: Part 3<p>In this part we’re going to fire up Expression Blend (the trial for version 5 can be found <a href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9503">here</a>) and do some UI work.</p> <p>In part 2, I created a simple Twitter client which connected to the streaming API, and connected to the sampling request which brings back random tweets. Here is the data template:</p> <div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"><</span><span style="color: #800000">DataTemplate</span> <span style="color: #ff0000">x:Key</span><span style="color: #0000ff">="TweetDataTemplate"</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Grid</span> <span style="color: #ff0000">DataContext</span><span style="color: #0000ff">="{Binding}"</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Grid.RowDefinitions</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">RowDefinition</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">RowDefinition</span> <span style="color: #ff0000">Height</span><span style="color: #0000ff">="Auto"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">Grid.RowDefinitions</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">TextBlock</span> <span style="color: #ff0000">FontFamily</span><span style="color: #0000ff">="{StaticResource FontFamily}"</span> <span style="color: #ff0000">FontSize</span><span style="color: #0000ff">="12"</span> <span style="color: #ff0000">Text</span><span style="color: #0000ff">="{Binding Text}"</span> <span style="color: #ff0000">TextWrapping</span><span style="color: #0000ff">="Wrap"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">TextBlock</span> <span style="color: #ff0000">Grid</span>.<span style="color: #ff0000">Row</span><span style="color: #0000ff">="1"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">HorizontalAlignment</span><span style="color: #0000ff">="Right"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">VerticalAlignment</span><span style="color: #0000ff">="Bottom"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontFamily</span><span style="color: #0000ff">="{StaticResource FontFamily}"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontSize</span><span style="color: #0000ff">="13.333"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Foreground</span><span style="color: #0000ff">="BlueViolet"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Text</span><span style="color: #0000ff">="{Binding ScreenName}"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">TextBlock</span> <span style="color: #ff0000">Grid</span>.<span style="color: #ff0000">Row</span><span style="color: #0000ff">="1"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">HorizontalAlignment</span><span style="color: #0000ff">="Left"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">VerticalAlignment</span><span style="color: #0000ff">="Bottom"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontFamily</span><span style="color: #0000ff">="{StaticResource FontFamily}"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontSize</span><span style="color: #0000ff">="9.333"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Foreground</span><span style="color: #0000ff">="DarkCyan"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Text</span><span style="color: #0000ff">="{Binding CreatedAt}"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">Grid</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"></</span><span style="color: #800000">DataTemplate</span><span style="color: #0000ff">></span></pre><!--CRLF--></div></div>
<p>This renders into something like this:</p>
<p><a href="http://lh4.ggpht.com/-ul4umD_jKFs/Tlq_U7HuoLI/AAAAAAAAAFg/pOhMiaqmxcE/s1600-h/image%25255B4%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-iRU3VAO9bmM/Tlq_Ve_91LI/AAAAAAAAAFk/JCfO-MSPXhk/image_thumb%25255B2%25255D.png?imgmax=800" width="424" height="84"></a></p>
<p>The text is randomly generated from Blend’s <a href="http://lmgtfy.com/?q=blend+sample+data">sample data</a> capability, which is totally awesome as it allows designers to see what they’re working with, and keeps the sample data separate from the real data.</p>
<p>While design is a matter of personal taste, and you’re bound to get disagreements between different people, if you follow some basic rules you’ll satisfy a greater audience.</p>
<ul>
<li>Subtle gradients and small shadows
<ul>
<li>If you take a look at all the nice interfaces, they tend to use very slight gradients and small shadows. Most of the time you don’t even notice unless you look closely.
<li>I think Microsoft’s Metro design is beautiful. Reason? It emphasizes text over decorations (like gradients and shadows). This tends to lead to very clean design because there’s very little opportunity to abuse gradients and shadows.</li></ul>
<li>Realism and light sources
<ul>
<li>Continuing on with gradients and shadows, they should be realistic. Look at your design from a 3D point of view. Apply a light source from a certain angle, and then apply your shadows relative to that light source.
<li>Convey distance properly</li></ul>
<ul>
<ul>
<li>Darker shadows imply being closer to the background, whereas lighter shadows imply being further away. Use blurring to add emphasis to the distance.<br><a href="http://lh4.ggpht.com/-cdUtFU6yTXs/Tlr6Ob6YaBI/AAAAAAAAAFo/jaMAe5SO7mo/s1600-h/image%25255B26%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-YvKRal5ToEw/Tlr6OrRIS4I/AAAAAAAAAFs/AAg79v6y2Cc/image_thumb%25255B14%25255D.png?imgmax=800" width="240" height="37"></a>
<li>If you overlap planes you should apply these rules to each individual plane. Don’t use the same border for everything. Think about how it would look like in real life if you laid it out like that with pieces of paper. The shadow sizes for that will be different, so you should do the same.
<li>Also keep in mind that the shadows used above are <em>way</em> too much for any application. Be subtle!</li></ul></ul>
<li>Consistent theme
<ul>
<li>This one seems obvious but nothing is worse than having a nice looking application bring up an unskinned dialog.</li></ul>
<li>Usability
<ul>
<li>If the design doesn’t serve a purpose to make it more usable, it shouldn’t be there. Even something as simple as black on white follows this – you do that so you can read text. However, even something as simple as that can be improved. Take a look at why the Kindle is so successful. The readability is better because of the lower contrast between the black and light-brown background.</li></ul></li></ul>
<p>With these starting points, let’s redesign the data template.</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"><</span><span style="color: #800000">DataTemplate</span> <span style="color: #ff0000">x:Key</span><span style="color: #0000ff">="TweetDataTemplate"</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Grid</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Grid.Background</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">LinearGradientBrush</span> <span style="color: #ff0000">StartPoint</span><span style="color: #0000ff">="0.5,0"</span> <span style="color: #ff0000">EndPoint</span><span style="color: #0000ff">="0.5,1"</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">GradientStop</span> <span style="color: #ff0000">Color</span><span style="color: #0000ff">="#FFDADADA"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">GradientStop</span> <span style="color: #ff0000">Offset</span><span style="color: #0000ff">="1"</span> <span style="color: #ff0000">Color</span><span style="color: #0000ff">="#FFC8C8C8"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">LinearGradientBrush</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">Grid.Background</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Grid.RowDefinitions</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">RowDefinition</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">RowDefinition</span> <span style="color: #ff0000">Height</span><span style="color: #0000ff">="Auto"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">Grid.RowDefinitions</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">TextBlock</span> <span style="color: #ff0000">FontFamily</span><span style="color: #0000ff">="{StaticResource FontFamily}"</span> <span style="color: #ff0000">FontSize</span><span style="color: #0000ff">="12"</span> <span style="color: #ff0000">Text</span><span style="color: #0000ff">="{Binding Text}"</span> <span style="color: #ff0000">TextWrapping</span><span style="color: #0000ff">="Wrap"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">TextBlock</span> <span style="color: #ff0000">Grid</span>.<span style="color: #ff0000">Row</span><span style="color: #0000ff">="1"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">HorizontalAlignment</span><span style="color: #0000ff">="Right"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">VerticalAlignment</span><span style="color: #0000ff">="Bottom"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontFamily</span><span style="color: #0000ff">="{StaticResource FontFamily}"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontSize</span><span style="color: #0000ff">="13.333"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Foreground</span><span style="color: #0000ff">="BlueViolet"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Text</span><span style="color: #0000ff">="{Binding ScreenName}"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">TextBlock</span> <span style="color: #ff0000">Grid</span>.<span style="color: #ff0000">Row</span><span style="color: #0000ff">="1"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">HorizontalAlignment</span><span style="color: #0000ff">="Left"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">VerticalAlignment</span><span style="color: #0000ff">="Bottom"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontFamily</span><span style="color: #0000ff">="{StaticResource FontFamily}"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">FontSize</span><span style="color: #0000ff">="9.333"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Foreground</span><span style="color: #0000ff">="#FF003D8F"</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #ff0000">Text</span><span style="color: #0000ff">="{Binding CreatedAt}"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Border</span> <span style="color: #ff0000">Grid</span>.<span style="color: #ff0000">RowSpan</span><span style="color: #0000ff">="2"</span> <span style="color: #ff0000">BorderBrush</span><span style="color: #0000ff">="#FF999999"</span> <span style="color: #ff0000">BorderThickness</span><span style="color: #0000ff">="0,0,0,1"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"><</span><span style="color: #800000">Border</span> <span style="color: #ff0000">Grid</span>.<span style="color: #ff0000">RowSpan</span><span style="color: #0000ff">="2"</span> <span style="color: #ff0000">BorderBrush</span><span style="color: #0000ff">="White"</span> <span style="color: #ff0000">BorderThickness</span><span style="color: #0000ff">="0,1,0,0"</span> <span style="color: #0000ff">/></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff"></</span><span style="color: #800000">Grid</span><span style="color: #0000ff">></span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff"></</span><span style="color: #800000">DataTemplate</span><span style="color: #0000ff">></span></pre><!--CRLF--></div></div>
<p>After these changes, it looks like this:</p>
<p><a href="http://lh3.ggpht.com/-TgxXvJqHk-c/Tlr6O9P56uI/AAAAAAAAAFw/Nc5v5nLapLc/s1600-h/image%25255B31%25255D.png"><img style="background-image: none; border-right-width: 0px; margin: 0px 5px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" align="left" src="http://lh5.ggpht.com/-fTL8wgr5ebs/Tlr6PGkqyeI/AAAAAAAAAF0/O98iakwvqGc/image_thumb%25255B17%25255D.png?imgmax=800" width="304" height="99"></a></p>
<p>Did you notice the gradient? You might think after seeing it here to adjust the gradients more so you can see it. That would be a mistake. See below.</p>
<p> </p>
<p> </p>
<p><a href="http://lh5.ggpht.com/-jLd2xQIBIW8/Tlr6PfKpCBI/AAAAAAAAAF4/x3qjb1XqB0o/s1600-h/image%25255B69%25255D.png"><img style="background-image: none; border-right-width: 0px; margin: 0px 5px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" align="right" src="http://lh6.ggpht.com/--uPF_IEo6LM/Tlr6PpL9YDI/AAAAAAAAAF8/UxqCqYW3Wl4/image_thumb%25255B47%25255D.png?imgmax=800" width="336" height="198"></a></p>
<p>To the right is the exact same thing, but stacked vertically three times. When this happens the subtle difference between the top and bottom of the control is more pronounced, so it looks like multiple panels are aligned together.<a href="http://lh3.ggpht.com/-QRud-f9oNl0/Tlr6PwZxYEI/AAAAAAAAAGA/n4ZgvQHGU2g/s1600-h/image%25255B64%25255D.png"><img style="background-image: none; border-right-width: 0px; margin: 0px 5px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" align="left" src="http://lh3.ggpht.com/-QHLW-sKfPfI/Tlr6P_c9qLI/AAAAAAAAAGE/kJ0Z8VgS3xg/image_thumb%25255B44%25255D.png?imgmax=800" width="336" height="198"></a></p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>However, there’s still a little touch you can add. The white and gray borders are only 1 pixel high, but that’s the little touch needed to make it look crisp.</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>Finally, let’s see the before and after (or eh…rather after and before, because I took the screenshot backwards :P):</p>
<p><a href="http://lh6.ggpht.com/-xEyFAxujR8o/Tlr6QbtqRTI/AAAAAAAAAGI/KKzuwUmnh6o/s1600-h/image%25255B74%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-tzZ8AVtNv0M/Tlr6QmHJHnI/AAAAAAAAAGM/H1SYEqT6cis/image_thumb%25255B50%25255D.png?imgmax=800" width="617" height="342"></a></p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-82103599142083170452011-08-27T17:16:00.001-04:002011-08-28T14:16:47.002-04:00Building a Real-time Push App with Silverlight: Part 2<p>Let’s review the main Rx code from last time:</p> <div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">public</span> IObservable<<span style="color: #0000ff">string</span>> GetJsonStreams()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var request = GetRequest();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">return</span> Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Select(wr => wr.GetResponseStream())</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Select(str => Observable.FromAsyncPattern<<span style="color: #0000ff">byte</span>[], <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>>(str.BeginRead, str.EndRead))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .SelectMany(ParseJson);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">}</pre><!--CRLF--></div></div>
<p>One thing I didn’t like about this was that the web request object was created regardless of whether the Observable gets a subscription or not. This is potentially wasted resources, and I wanted to refactor this to be completely lazy.</p>
<p>And with this I started to run into my first “huh?” moments with Rx: I blocked the UI thread. How did I do that? I started down the path of exploring some more of the Rx methods, which lead me to <em>Create</em>, which lets you manually call <em>OnNext. </em>With this train of thought, I came up with something like this:</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">return</span> Observable.Create<<span style="color: #0000ff">string</span>>(obs =></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var request = GetRequest();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var response = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)().First();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var str = response.GetResponseStream();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var reader = Observable.FromAsyncPattern<<span style="color: #0000ff">byte</span>[], <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>>(str.BeginRead, str.EndRead);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">foreach</span> (var json <span style="color: #0000ff">in</span> ParseJson(reader))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> obs.OnNext(json);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> obs.OnCompleted();<br> <span style="color: #0000ff">return</span> str;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">});</pre><!--CRLF--></div></div>
<p>Great! The initialization of the web request only occurs when subscribed! And it will even dispose the stream (by returning <em>str</em>) upon unsubscription. I ran the app and the UI thread immediately blocked. What happened?</p>
<p>Rx has the concept of subscription and observation, and provides a way to subscribe and observe on different threads. Here is the original code that subscribed:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">s.GetJsonStreams()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .ObserveOnDispatcher()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Subscribe(x => Text = x);</pre><!--CRLF--></div></div>
<p>Can you spot the error? I explicitly told Rx to observe on the dispatcher thread, because I want the action inside <em>Subscribe</em> to be invoked on the UI thread, but I didn’t specify where I want to set up the subscription. Since I left it out, it uses the current thread, which happens to be the UI thread. To solve this, it’s as simple as doing this:</p>
<div id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">s.GetJsonStreams()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .SubscribeOn(Scheduler.ThreadPool)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .ObserveOnDispatcher()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Subscribe(x => Text = x);</pre><!--CRLF--></div></div>
<p>That’s it! Easy! This also follows one of the most important guidelines when using Rx: <strong>Subscription and Observation should be done as late as possible</strong>, typically just before the <em>Subscribe</em>. Anything more and you’ll likely make Rx spawn more threads than are necessary or some other nasty bugs. <a href="http://en.wikipedia.org/wiki/KISS_principle">KISS</a>!</p>
<p>Now with that out of the way, let’s replace the boring TextBlock with something more usable. First, I need to parse all the JSON streams I’m getting into bindable models. To do that, I upgraded my StreamReader component and threw in System.Json for some basic parsing:</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> TweetParser</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">private</span> <span style="color: #0000ff">int</span> _stack;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> StringBuilder _sb = <span style="color: #0000ff">new</span> StringBuilder();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">public</span> IEnumerable<Tweet> Parse(<span style="color: #0000ff">byte</span>[] buffer, <span style="color: #0000ff">int</span> count)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < count; i++)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var current = (<span style="color: #0000ff">char</span>)buffer[i];</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> _sb.Append(current);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">if</span> (current == <span style="color: #006080">'{'</span>) _stack++;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">else</span> <span style="color: #0000ff">if</span> (current == <span style="color: #006080">'}'</span>) _stack--;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">if</span> (_stack == 0 && _sb.Length > 0)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> Tweet tweet;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var <span style="color: #0000ff">value</span> = JsonValue.Parse(_sb.ToString());</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">if</span> (<span style="color: #0000ff">value</span> <span style="color: #0000ff">is</span> JsonObject && Tweet.TryParse((JsonObject)<span style="color: #0000ff">value</span>, <span style="color: #0000ff">out</span> tweet))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">yield</span> <span style="color: #0000ff">return</span> tweet;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> _sb.Clear();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">}</pre><!--CRLF--></div></div>
<p>Nothing overly complicated. Next, the Tweet object:</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> Tweet</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> JsonObject _json;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">bool</span> TryParse(JsonObject <span style="color: #0000ff">value</span>, <span style="color: #0000ff">out</span> Tweet tweet)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">if</span> (<span style="color: #0000ff">value</span>.ContainsKey(<span style="color: #006080">"text"</span>) && <span style="color: #0000ff">value</span>.ContainsKey(<span style="color: #006080">"user"</span>))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> tweet = <span style="color: #0000ff">new</span> Tweet(<span style="color: #0000ff">value</span>);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span>;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> tweet = <span style="color: #0000ff">null</span>;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span>;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">private</span> Tweet(JsonObject json)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> _json = json;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Text</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> get { <span style="color: #0000ff">return</span> _json[<span style="color: #006080">"text"</span>].ToValueString(); }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> ScreenName</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> get { <span style="color: #0000ff">return</span> _json[<span style="color: #006080">"user"</span>][<span style="color: #006080">"screen_name"</span>].ToValueString(); }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">}</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">internal</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">class</span> TweetEx</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">string</span> ToValueString(<span style="color: #0000ff">this</span> JsonValue s)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> <span style="color: #0000ff">return</span> s.ToString().Trim(<span style="color: #006080">'"'</span>);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">}</pre><!--CRLF--></div></div>
<p>To keep things simple I’m only extracting the screen name and text. I won’t bore you setting up the views since it’s just simple ListBox bound to an ObservableCollection<Tweet>, and a DataTemplate for Tweet. When it’s all said and done, we see something like this:</p>
<p><a href="http://lh4.ggpht.com/-WFTaVk0Cjls/TlqFsoenmOI/AAAAAAAAAFY/MEt0MHis3dY/s1600-h/image5.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-sVzoQRx_V2s/TlqFswXVhUI/AAAAAAAAAFc/d9nLfBSrARA/image_thumb3.png?imgmax=800" width="644" height="431"></a></p>
<p>Performance is still good at 2-5% CPU, even though we’re scrolling through 1000 items in near real-time.</p>
<p>Stay tuned for part 3, when we introduce Expression Blend and go into basics of UI design. Also, most of this will hit GitHub very soon.</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-6208582525259351202011-08-26T22:54:00.001-04:002011-08-28T18:22:35.763-04:00Building a Real-time Push App with Silverlight: Part 1<p>This is the beginning of a multi-part series where I’ll be building an interactive application with Silverlight 5 (still beta as of this post). It will be built from the ground up and designed predominantly from a “push data” point of view, where the application is reacting to events in real-time, rather than a more traditional “pulling” point of view. This type of application has exploded with the popularity of social networking and has made a lot of the traditional methods of building applications obsolete.</p> <p>The best example of this shift is from Twitter. When it first came out, it was (and still is predominantly) a “pull” model. You have 200 API requests an hour, and you pull Twitter whenever you want to check if there are tweets of people you follow. That is changing with the <a href="https://dev.twitter.com/docs/streaming-api">Streaming API</a> where data is “pushed” to you as it comes. This changes the way you write your code and requires a mind shift much like from for loops to LINQ.</p> <p>The primary purpose of this series is build a real-time push application from beginning to end, and to show any problems I run along the way, and how I managed to solve them. This will be my first attempt at building a Silverlight application as well as learning <a href="http://msdn.microsoft.com/en-us/data/gg577609">Reactive Extensions</a> (Rx), so I’m bound to run into newbie mistakes. If you have any tips or pointers please let me know!</p> <p>Also, rather than doing this from a purely technical point of view, I’m also going to put on my designer hat and talk about UI design, and make it look good with Expression Blend when I get to designing the UI in later parts of this series. I feel that this is often overlooked and can result in a lot of wasted work and lead to frustration.</p> <p>This first post will be to get up and running and connected to Twitter with the Streaming API. This was chosen primarily because it is so much data can be pushed through and it can be used to demonstrate how to write a high performance Silverlight application. It’s one thing to write something maintainable, and another altogether to make it run fast as well. I’ve worked on too many WPF projects where performance took a back seat, and in WPF particularly this tends to create some massive technical debt. The Silverlight/WPF technology stack is an exception to the rule and you should definitely think about performance from the start.</p> <p>Well let’s get started! We will connect to Twitter, convert it to an Observable, and then display tweets on the UI.</p> <p>First, let’s create our TwitterStream class:</p> <div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"> <div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum1"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> TwitterStream</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum2"> 2:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum3"> 3:</span> <span style="color: #0000ff">static</span> TwitterStream()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum4"> 4:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum5"> 5:</span> <span style="color: #008000">// this allows http authentication to work</span></pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum6"> 6:</span> WebRequest.RegisterPrefix(<span style="color: #006080">"http://"</span>, System.Net.Browser.WebRequestCreator.ClientHttp);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum7"> 7:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum8"> 8:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum9"> 9:</span> <span style="color: #0000ff">protected</span> <span style="color: #0000ff">readonly</span> StreamParser parser = <span style="color: #0000ff">new</span> StreamParser();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum10"> 10:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum11"> 11:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Username { get; set; }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum12"> 12:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Password { get; set; }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum13"> 13:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum14"> 14:</span> <span style="color: #0000ff">protected</span> HttpWebRequest GetRequest()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum15"> 15:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum16"> 16:</span> var request = WebRequest.CreateHttp(<span style="color: #006080">"http://stream.twitter.com/1/statuses/sample.json?delimited=length"</span>);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum17"> 17:</span> request.UseDefaultCredentials = <span style="color: #0000ff">false</span>;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum18"> 18:</span> request.Credentials = <span style="color: #0000ff">new</span> NetworkCredential(Username, Password);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum19"> 19:</span> <span style="color: #0000ff">return</span> request;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum20"> 20:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum21"> 21:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum22"> 22:</span> <span style="color: #0000ff">public</span> IObservable<<span style="color: #0000ff">string</span>> GetJsonStreams()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum23"> 23:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum24"> 24:</span> var request = GetRequest();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum25"> 25:</span> <span style="color: #0000ff">return</span> Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum26"> 26:</span> .Select(wr => wr.GetResponseStream())</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum27"> 27:</span> .Select(str => Observable.FromAsyncPattern<<span style="color: #0000ff">byte</span>[], <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>>(str.BeginRead, str.EndRead))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum28"> 28:</span> .SelectMany(ParseJson);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum29"> 29:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum30"> 30:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum31"> 31:</span> <span style="color: #0000ff">private</span> IEnumerable<<span style="color: #0000ff">string</span>> ParseJson(Func<<span style="color: #0000ff">byte</span>[], <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>, IObservable<<span style="color: #0000ff">int</span>>> reader)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum32"> 32:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum33"> 33:</span> var buffer = <span style="color: #0000ff">new</span> <span style="color: #0000ff">byte</span>[256];</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum34"> 34:</span> <span style="color: #0000ff">int</span> bytesRead;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum35"> 35:</span> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum36"> 36:</span> <span style="color: #0000ff">while</span> ((bytesRead = reader(buffer, 0, buffer.Length).Single()) > 0)</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum37"> 37:</span> {</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum38"> 38:</span> <span style="color: #0000ff">foreach</span> (var json <span style="color: #0000ff">in</span> parser.Parse(buffer, bytesRead))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum39"> 39:</span> <span style="color: #0000ff">yield</span> <span style="color: #0000ff">return</span> json;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum40"> 40:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum41"> 41:</span> }</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #606060" id="lnum42"> 42:</span> }</pre><!--CRLF--></div></div>
<p>And that’s all there is to it! The most complicated part is probably parsing of the JSON documents themselves, which I refactored into its own class. The algorithm in the parser is pretty primitive, as it just looks for opening { and closing } and calls that a document – nothing more, nothing less. Note that this uses the deprecated basic HTTP authentication. Sooner or later it will be shut down and OAuth will be required, so I will upgrade when that happens as I want to keep the amount of written code to a minimum.</p>
<p>Perhaps the more interesting bit is this part with Rx:</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Select(wr => wr.GetResponseStream())</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Select(str => Observable.FromAsyncPattern<<span style="color: #0000ff">byte</span>[], <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>, <span style="color: #0000ff">int</span>>(str.BeginRead, str.EndRead))</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .SelectMany(ParseJson);</pre><!--CRLF--></div></div>
<p>The API takes some getting used to, but once you “tune in” things start to make sense. The <em>FromAsyncPattern </em>essentially wraps the <a href="http://msdn.microsoft.com/en-us/library/ms228969.aspx">APM pattern</a>’s Begin/End calls into a single Func<>, which when invoked gets you the result as if you called End(), however without all the tedious AsyncCallback implementation.</p>
<p>The nice thing about this wrapper Func is that the code starts to look synchronous, even though it is using the ThreadPool behind the scenes. Next, the response is projected into its response stream, which again is converted to another Observable for reading bytes from the stream until finally it goes into the <em>ParseJson</em> method which projects string results. This is both good and bad. Good in that it can make asynchronous code look short and succinct (in this example I’ve wrapped 2 APM pattern calls in 2 lines of code), bad in that anyone inexperienced with Rx will get lost pretty fast.</p>
<p>Rx thus far has been a fairly high learning curve. Maybe it’s just me. When I read about it from others, or from seminars, it all makes sense and I generally don’t have any questions. Trying to use it directly is another story, as there are so many overloads it’s easy to get overwhelmed. I feel like Intellisense is making things worse!</p>
<p>Carrying on, the code above actually doesn’t do anything yet. First, you must <em>Subscribe</em> to an observable before it does something, similar to you must foreach on a IEnumerable before it starts pulling data.</p>
<p>To finish off, let’s create a simple Window with a TextBlock bound to a Text property. This is the constructor:</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"><span style="color: #0000ff">public</span> MainPage()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">{</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> InitializeComponent();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> DataContext = <span style="color: #0000ff">this</span>;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> </pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> var s = <span style="color: #0000ff">new</span> TwitterStream();</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> s.Username = <span style="color: #006080">"blingcoder"</span>;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> s.Password = <span style="color: #006080">"1234567"</span>;</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> s.GetJsonStreams()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .ObserveOnDispatcher()</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"> .Subscribe(x => Text = x);</pre><!--CRLF--><pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px">}</pre><!--CRLF--></div></div>
<p>Perhaps one of the nicest features of Rx is automatic thread switching between background threads and UI threads. Above, there is a call to <em>ObserveOnDispatcher</em>, which as the name implies it observes to events on the Dispatcher thread :-). Rx automatically handles switching to the UI thread so when <em>Subscribe</em> occurs we’re on the UI thread.</p>
<p>Performance is also very good so far, only maxing out at 5% CPU for what appears to be a continuous flood of tweets from random people.</p>
<p>And there you have it! A completely asynchronous streaming Twitter client in roughly 20 lines of code (minus the parsing). Stay tuned for part 2….</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-58341739313684681172011-08-19T13:22:00.000-04:002011-08-21T22:40:22.761-04:00Injecting Logger Instances Via Convention With Unity<p>Let's say you have something like this:</p> <pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> MyClass {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> ILog Logger { <span style="color: #0000ff">get</span>; <span style="color: #0000ff">set</span>; }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">}</pre></pre>
<p>And you simply want Unity to inject an instance of ILog such that its name is requestingDependency.GetType().FullName, which in this case would be Full.Namespace.MyClass.<br><br>I'll leave it up to the exercise of the reader, but if you search for 'log4net and Unity' you will find some working solutions which IMO are way too long to solve this. I have obviously taken for granted the features of Windsor and Autofac too much because I expected something like this to be easily done in 2-3 lines of code.<br><br>After a lot of cursing I've come up with something that's almost as short and gets the job done:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> LoggingExtension : UnityContainerExtension
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">protected</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span> Initialize()
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">this</span>.Context.Strategies.AddNew(UnityBuildStage.PreCreation);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">private</span> <span style="color: #0000ff">class</span> LogBuilderStrategy : BuilderStrategy
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> Stack buildKeys = <span style="color: #0000ff">new</span> Stack();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span> PreBuildUp(IBuilderContext context)
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> var type = ((NamedTypeBuildKey)context.BuildKey).Type;
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">if</span> (type == <span style="color: #0000ff">typeof</span>(ILog))
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">if</span> (<span style="color: #0000ff">this</span>.buildKeys.Count < 1)
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> InvalidOperationException("<span style="color: #8b0000">Log instances cannot be resolved directly.</span>");
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> context.Existing = LogManager.GetLog(<span style="color: #0000ff">this</span>.buildKeys.Peek());
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">this</span>.buildKeys.Push(type);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span> PostBuildUp(IBuilderContext context)
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">this</span>.buildKeys.Pop();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre></pre>
<p>If you noticed, I had to cast to NamedTypeBuildKey, which unfortunately means I'm still stuck with Unity 1.2...</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-46453967980776591222011-08-16T10:51:00.000-04:002011-08-16T10:51:39.844-04:00Adding Scripting to your Tool BeltOne of the most important things for any developer is to be well rounded. They should know about functional, imperative, object-oriented, aspect-oriented programming, among others. They should know the difference between dynamic and static languages. They should know the difference between scripting, interpreted, compiled languages.<br />
<br />
Why? It opens your mindset. It lets you see things in a different light, which ultimately changes the way you will write your code, and for the better.<br />
<br />
The best example is when C# introduced lambdas and LINQ. All of a sudden, the masses saw the benefits of functional programming style and how it <b>greatly</b> makes code succinct.<br />
<br />
Here, I give an example of what knowing a little about Powershell did to save me a lot of time.<br />
<br />
I use git-tfs to make my day job a little less painful every day, because I cannot stand TFS, at all. In their latest release, they decided not to tag every changeset. I like my things consistent, so I decided to go back in history to untag all the changesets.<br />
<br />
Unfortunately, git doesn't have a mass untag, and you must untag each branch individually with "git tag -d name_of_tag".<br />
<br />
This is how you do it with Powershell:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">50000..40000 | % { git tag -d "tfs/default/C$_" }</span><br />
<br />
50000..40000 creates an list of numbers from 50,000 to 40,000, then it gets piped (|) into a foreach loop (%) to run the git tag command by generating the tag name with the current loop item ($_).<br />
<br />
Why was this awesome? I didn't have to open up Visual Studio, or a text editor, or anything. I just typed it into the shell.Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-48970284937407953092011-06-23T20:55:00.001-04:002011-06-24T11:16:56.431-04:00Tools I Use To Do What I Do<p>It’s been a while since I last blogged, but I want to get back into it….I just haven’t thought about what I wanna blog about yet. I’m thinking of doing some sort of multi-part series on a topic but haven’t decided on what yet. Nonetheless, just to get the ball rolling again here’s a quick blog post on some of the tools I use to do my every day job that’s in addition to your typical .NET developer tool chain.</p> <p><strong>General</strong></p> <p><a href="http://www.tcbmi.com/strokeit/">StrokeIt</a>: Funny name aside, on any clean install of Windows this is the <strong>first</strong> thing I install. I load up IE and I download this, followed by Chrome/Firefox. It is a mouse gesture program that affects all of Windows. By holding down the right mouse button, and making gestures you can do common tasks like minimize (drag SW), maximize (drag NE), close (draw a C), and others. It’s much faster than clicking the minimize/maximize buttons. For web browsing, creating and close tabs are drag SE and drag NW respectively. Back is W. Forward is E. Working this way is faster than any keyboard shortcut.</p> <p><a href="http://sourceforge.net/projects/console/">Console2</a>: It’s not a necessity, but it’s nice to be able to use a console with a better looking font. You can easily customize to run another shell, and in my case I have Powershell as the shell.</p> <p><a href="http://virtuawin.sourceforge.net/">VirtuaWin</a>: Ironically I don’t use any virtual desktops, but this app creates a nice middle click on any window that lets you mark it as ‘always on top’.</p> <p><a href="http://www.vim.org/">Vim</a>: It was an awful, painful 2 weeks to learn this thing but I’m sure glad I did.</p> <p><strong>Development/Visual Studio</strong></p> <p><a href="http://www.jetbrains.com/resharper/">Resharper</a>: This one doesn’t need much mentioning as anyone serious about programming in .NET has either heard of it, use it, or use one of its competitors like <a href="http://www.devexpress.com/Products/Visual_Studio_Add-in/Coding_Assistance/">CodeRush</a>.</p> <p><a href="http://visualstudiogallery.msdn.microsoft.com/465a0d53-5133-4edd-a0cd-94484fe3d853">AllMargins</a>: This hidden gem is an awesome extension has brings a bunch of very useful features to the editor. It brings structural highlighting, highlighting matching words under caret, and RockScroll inspired scroll bar, and it does it all with little to no cost in performance.</p> <p><a href="http://visualstudiogallery.msdn.microsoft.com/d0d33361-18e2-46c0-8ff2-4adea1e34fef/">Productivity Power Tools</a>: There’s only 2 that I use here: document tabs, and moving lines. All the rest are either eye candy, negligible enhancements, or just plain too slow to be any use in large solutions (solution navigator I’m looking at you!!).</p> <p><a href="http://visualstudiogallery.msdn.microsoft.com/1a67eee3-fdd1-4745-b290-09d649d07ee0">XAML Intellisense Presenter</a>: This is a must-have for anyone who works with XAML in VS regularly. Resharper 6 may soon make this obsolete, however…</p> <p><a href="http://wiki.sharpdevelop.net/ILSpy.ashx">ILSpy</a>/<a href="http://www.jetbrains.com/decompiler/">dotPeek</a>: Decompilers. ‘nuff said.</p> <p><a href="http://snoopwpf.codeplex.com/">Snoop</a>: Staple tool for WPF developers to inspect the visual/logical tree. There’s also <a href="http://wpfinspector.codeplex.com/">WPF Inspector</a> which has a much nicer UI, but unfortunately doesn’t perform as fast.</p> <p><strong>Profiling</strong></p> <p><a href="http://www.red-gate.com/products/dotnet-development/dotnet-developer-bundle/">ANTS</a>/<a href="http://www.jetbrains.com/profiler/">dotTrace</a>/<a href="http://www.yourkit.com/dotnet/download/">YourKit</a>/etc: I’ve used many performance/memory profilers, and those 3 are the ones I like the most. Unfortunately, they all do things the others don’t, and all these things are very useful, so you still end up needing to use all of them to fix the particular problem you’re working on.</p> <p><a href="http://msdn.microsoft.com/en-us/windows/hardware/gg463009">WinDbg</a>: It isn’t every day I use this, but I’m sure glad to know that such a thing exists and the amount of power it gives me.</p> <p><strong>Scripting</strong></p> <p><a href="http://powergui.org/index.jspa">PowerGUI</a>: Best tool I found for editing PowerShell. The intellisense is great and has all the things you were expect when debugging through scripts.</p> <p><strong>Miscellaneous</strong></p> <p><a href="http://www.baremetalsoft.com/baretail/">BareTail</a>: Amazing program which watches log files in real time and can highlight rows based on a search pattern.</p> <p><a href="http://technet.microsoft.com/en-us/scriptcenter/dd742419">PowerShell</a>/<a href="http://cygwin.org/">Cygwin</a>/<a href="http://www.mingw.org/wiki/MSYS">MSYS</a>: I used to be a Cygwin/MSYS user because back in the day I liked to install a different Linux distribution every week so I learned some basic Bash commands and that carried over to Windows. Since PowerShell now exists, we have a real shell in Windows, so I’ve switched over to learn it, since as a Windows developer it’s more applicable to the kind of work I do.</p> <p> </p> <p>And there’s more but that’s enough for now!</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com2tag:blogger.com,1999:blog-3157881265969801285.post-20914523925707211542011-05-20T22:57:00.001-04:002011-05-20T23:25:09.779-04:00Taking a Quick Look at .NET Decompilers<p>Decompilers in the .NET space has changed a lot since the announcement from Red Gate that they were going to start charging for Reflector, which was the defacto most important/useful tool in any developer’s tool belt.</p> <p>This blog post will be a quick overview to take a look at all the new contenders that have cropped up in the .NET space to see if any of them can dethrone Reflector as the decompiler of choice. This is only taking a look at the tools as a stand alone product and excludes any IDE integration.</p> <p>To start, let’s take a look at Reflector.</p> <p><strong><a href="http://www.reflector.net/">Red Gate Reflector</a> (7.1.0.143)</strong></p> <p>Having been around for years, Reflector is a rock solid product that sets the standard for everything else. It has a simple straight forward interface and has a large following with many 3rd party addins. It has a search which lets you find types, and an analyzer which shows you incoming and outgoing calls for any method. It also has the most options for decompiling, giving choices from C# to VB and even Delphi.</p> <p>Another useful feature is being able change the framework from .NET to WPF to Silverlight.</p> <p>As of this version it looks like they added tabs, but it for me it only used one at a time…perhaps a limitation of the trial version.</p> <p><strong><a href="http://wiki.sharpdevelop.net/ilspy.ashx">ILSpy</a> (1.0.0.822)</strong></p> <p>This open source offering at this point looks the same as Reflector did just before Red Gate took over. It has all the necessities, including search and analyze. It can only view one thing at a time, and doesn’t have support for addins yet. However, progress on this project is blazing fast and it’s only a matter of time before it reaches feature parity with the rest.</p> <p><strong><a href="http://www.telerik.com/products/decompiling.aspx">JustDecompile Beta</a> (2011.1.516.2)</strong></p> <p>First impression was, damn, this is a nice pretty WPF app. Onwards, it has a predefined assembly list of .NET2, 4, and Silverlight, which is nice. What’s nicer is that you can save a predefined list for use later, a feature that’s absent from the others. It has search by type, and search by symbol, however, the search is noticeably slower than everything else I tried. Also, the actual decompiling is much slower than the rest as well. Like ILSpy, it can only view one thing at a time.</p> <p>Decompiling to C# and VB are supported, but no IL yet.</p> <p><strong><a href="http://www.jetbrains.com/decompiler/">dotPeek EAP</a> (1.0.0.1219)</strong></p> <p>I tried an EAP build and I was blown away. I may be biased because I’m a Resharper user, but having almost all of the keyboard shortcuts I have in my muscle memory work in dotPeek was a welcome surprise. The keyboard navigation makes dotPeek <strong>by far</strong> the best tool for navigating. While the others have searches, they only do so at the type level. With dotPeek (just like Resharper), you can search by type, current type, and even private members. Inheritance searching like finding implementations, etc., all of that works. Simply amazing. Oh, and it also has full tab support.</p> <p>It was also fast as well, which was a surprise considering it’s searching through the entire framework. Searching all symbols for “m_” brought a list up in 2 seconds, and subsequent keystrokes narrowing it down occurred within half a second.</p> <p>The downside? No support for decompiling to IL, and lambdas aren’t fully supported yet. I’m sure these will be done by the time it’s released.</p> <p><strong>Conclusion</strong></p> <p>I guess the most important thing I should be looking at is the actual decompiler, rather than the bells and whistles. If that’s what I looked at, this would be a pretty boring post because only Reflector is able to decompile everything out there.</p> <p>JustDecompile failed to decompile some of the assemblies I threw it at it. dotPeek at this stage doesn’t have good lambda support (if at all). ILSpy decompiles very well at this point and I’d have to go out of my way to find an example where Reflector does it better than ILSpy.</p> <p>In conclusion, I love dotPeek just because of the awesomeness that is Resharper keyboard shortcuts. For everything else, ILSpy does the job.</p> <p>I’m actually pretty thankful that Red Gate decided to charge money for Reflector, because that inspired so much competition and we already have 3 viable replacements, all of which are free -- something that wouldn’t have happened if Reflector remained free.</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-35661765415378639012011-05-08T11:51:00.001-04:002011-05-08T12:00:40.724-04:00Simple App to Preview Fonts<p>I spent a good few hours yesterday trying out a new programming font. It’s a personal thing. I like switch fonts every couple weeks, and while Consolas is an excellent font and I have nothing against it, to me, it’s….too simple. For most people, the defaults are just fine, and they can’t be bothered to try another font. But for me, I want my font to speak to me a little. But enough of that voodoo magic stuff.</p> <p>The problem with sites that survey fonts is that it’s hard to compare quickly one with the next. For example, this excellent article comparing <a href="http://www.codeproject.com/KB/work/FontSurvey.aspx">42 fonts</a> on CodeProject is one of the most comprehensive surveys, but even with that I can’t really tell if I prefer one font over another. So, since I’m going to be doing this from time to time, I wrote a quick WPF app.</p> <p>The XAML is simple, only this:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><</span><span style="color: #800000">StackPanel</span> <span style="color: #ff0000">Orientation</span>=<span style="color: #0000ff">"Horizontal"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">ListBox</span> <span style="color: #ff0000">IsSynchronizedWithCurrentItem</span>=<span style="color: #0000ff">"True"</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">SelectedItem</span>=<span style="color: #0000ff">"{Binding CurrentFont}"</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">ItemsSource</span>=<span style="color: #0000ff">"{Binding Fonts}"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">TextBlock</span> <span style="color: #ff0000">FontFamily</span>=<span style="color: #0000ff">'{Binding CurrentFont}'</span> <span style="color: #ff0000">FontSize</span>=<span style="color: #0000ff">"14"</span> <span style="color: #ff0000">Text</span>=<span style="color: #0000ff">"{Binding Code}"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"></</span><span style="color: #800000">StackPanel</span><span style="color: #0000ff">></span></pre></pre>
<p>And the code is just as simple, only this:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> ViewModel
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">{
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">readonly</span> DependencyProperty CurrentFontProperty = DependencyProperty.Register("<span style="color: #8b0000">CurrentFont</span>", <span style="color: #0000ff">typeof</span>(FontFamily), <span style="color: #0000ff">typeof</span>(ViewModel));
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> FontFamily CurrentFont
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">get</span> { <span style="color: #0000ff">return</span> (FontFamily)GetValue(CurrentFontProperty); }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">set</span> { SetValue(CurrentFontProperty, <span style="color: #0000ff">value</span>); }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> List<FontFamily> Fonts { <span style="color: #0000ff">get</span>; <span style="color: #0000ff">private</span> <span style="color: #0000ff">set</span>; }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Code { <span style="color: #0000ff">get</span> { <span style="color: #0000ff">return</span> "<span style="color: #8b0000">insert what you want to see</span>"; } }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> ViewModel()
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> Fonts = <span style="color: #0000ff">new</span> List<FontFamily>();
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> Fonts.AddRange(System.Windows.Media.Fonts.SystemFontFamilies);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">}</pre></pre>
<p>And with this, you get something like this. The coding sample is taken from <a href="http://www.codinghorror.com/blog/2007/10/revisiting-programming-fonts.html">Coding Horror</a>.</p>
<p><a href="http://lh6.ggpht.com/_WZr-_wsEf1c/Tca7-XGu9hI/AAAAAAAAADg/tMTpN66ytCU/s1600-h/image%5B8%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_WZr-_wsEf1c/Tca7-v3TvHI/AAAAAAAAADk/3uSZ9J1z5mk/image_thumb%5B4%5D.png?imgmax=800" width="644" height="422"></a></p>
<p>Yep, my current chosen font is <a href="http://www.fontsquirrel.com/fonts/BPmono">BPMono</a>.</p>
<p>For the lazy, here’s the project on <a href="https://github.com/bling/FontSampler">GitHub</a>.</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-77046053373124794372011-04-17T16:22:00.001-04:002011-04-17T16:22:31.404-04:00Integrating DependencyPropertyWeaver Into Your Build<p>See the <a href="http://blingcode.blogspot.com/2011/04/introducing-dependencypropertyweaver.html">introduction</a> here.</p> <p>The MSBuild task as it stands currently by default will attempt to weave everything that it finds. This is probably not optimal in most use cases, but any changes is a simple modification to the LINQ query in the code. For now, it’s only available in source format since there are bound to be bugs here and there, and once those get ironed out it’ll be easier to release a “point something” release.</p> <p>An example of how its used can be seen in the unit tests, in the project DependencyPropertyWeaver.Tests.Models. If you open it up in your favorite text editor, you will see this crucial line:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><</span><span style="color: #800000">Target</span> <span style="color: #ff0000">Name</span>=<span style="color: #0000ff">"AfterBuild"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">Exec</span> <span style="color: #ff0000">Command</span>=<span style="color: #0000ff">"C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe postbuild.proj /p:Files=DependencyPropertyWeaver.Tests.Models.dll"</span> <span style="color: #ff0000">WorkingDirectory</span>=<span style="color: #0000ff">"$(OutputPath)"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">Target</span><span style="color: #0000ff">></span></pre></pre>
<p>And the contents of <em>postbuild.proj</em> is simply this:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><?</span>xml version="1.0" encoding="utf-8"<span style="color: #0000ff">?></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><</span><span style="color: #800000">Project</span> <span style="color: #ff0000">ToolsVersion</span>=<span style="color: #0000ff">"4.0"</span> <span style="color: #ff0000">DefaultTargets</span>=<span style="color: #0000ff">"Weave"</span> <span style="color: #ff0000">xmlns</span>=<span style="color: #0000ff">"http://schemas.microsoft.com/developer/msbuild/2003"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">UsingTask</span> <span style="color: #ff0000">TaskName</span>=<span style="color: #0000ff">"DependencyPropertyWeaver.DependencyPropertyWeaverTask"</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">AssemblyFile</span>=<span style="color: #0000ff">"DependencyPropertyWeaver.dll"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">Target</span> <span style="color: #ff0000">Name</span>=<span style="color: #0000ff">"Weave"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">ItemGroup</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">Input</span> <span style="color: #ff0000">Include</span>=<span style="color: #0000ff">"$(Files)"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">ItemGroup</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">DependencyPropertyWeaverTask</span> <span style="color: #ff0000">Files</span>=<span style="color: #0000ff">"@(Input)"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">Target</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"></</span><span style="color: #800000">Project</span><span style="color: #0000ff">></span></pre></pre>
<p>The <em>AfterBuild</em> target is a special target with MSBuild, which, as the name implies, will run after the target has been built. In this case, after the project is completed, it will invoke another MSBuild instance to perform the weaving. Invoking a new MSBuild process is required because the <UsingTask> will load the assembly into the current AppDomain, and once loaded it cannot be unloaded.</p>
<p>If you don’t mind the unloading part, you can simply add the <UsingTask> directly into the project file and invoked the <DependencyPropertyWeaverTask> there, and avoid the hassle of having a separate file.</p>
<p>Currently, the DependencyPropertyWeaverTask supports the <em>TypePatternMatch</em> and <em>AttributePatternMatch</em>, which as the names imply will use regular expression filtering on type names or properties with the attribute you want to weave. If these properties are null, they are ignored.</p>
<p>Next on the feature list is weaving attached properties ;-)</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com0tag:blogger.com,1999:blog-3157881265969801285.post-1927504519073543802011-04-15T22:09:00.001-04:002011-04-15T22:18:21.540-04:00Introducing DependencyPropertyWeaver<p>As part of my demonstration for the NYC .NET Meetup in the next couple of days, I prepared some material to show how to use Mono.Cecil to manually weave IL as a post-build step. It solves the problem of what I call “WPF Verbosity Hell”: the thing that makes your eyes bleed and makes God kill a kitten every time you declare a new property.</p> <p>Since <a href="http://code.google.com/p/notifypropertyweaver/">notifypropertyweaver</a> already exists, I figured I’d release the other side of the coin, dependencypropertyweaver. I just pushed my changes onto <a href="https://github.com/bling/dependencypropertyweaver">GitHub</a>.</p> <p>There are a couple of reasons to use dependency properties rather than regular properties with INotifyPropertyChanged. If you’re using WPF they make more sense because they perform faster and they give you a lot of extra bells and whistles for free. However, the INPC route tends to be more popular because it doesn’t bring in the dependency of WPF.</p> <p>IL post-build weaving solves the dependency problem because you can selectively choose to weave only the assemblies that get deployed with WPF, and you could even weave INPC into your service layer, and DPs into your client layer. That’s the power of post-build processes.</p> <p>While this little project initially started out as a simple demonstration of using <a href="http://www.mono-project.com/Cecil">Mono.Cecil</a> to perform low-level aspect oriented principles, it didn’t take much more work to tidy things up and make it ‘releasable’ into the wild, so I did just that, and open sourced it.</p> <p>The concept is simple. You take something like this:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> Student
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Name { <span style="color: #0000ff">get</span>; <span style="color: #0000ff">set</span>; }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span> Age { <span style="color: #0000ff">get</span>; <span style="color: #0000ff">set</span>; }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }</pre></pre>
<style type="text/css">.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; }
</style>
<p>And DependencyPropertyWeaver will turn it into this:</p><pre style="border-bottom: #cecece 1px solid; border-left: #cecece 1px solid; padding-bottom: 5px; background-color: #fbfbfb; min-height: 40px; padding-left: 5px; width: 650px; padding-right: 5px; overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; padding-top: 5px"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> Student : DependencyObject
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">readonly</span> DependencyProperty NameDependencyProperty = DependencyProperty.Register("<span style="color: #8b0000">Name</span>", <span style="color: #0000ff">typeof</span>(<span style="color: #0000ff">string</span>), <span style="color: #0000ff">typeof</span>(Student));
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">readonly</span> DependencyProperty AgeDependencyProperty = DependencyProperty.Register("<span style="color: #8b0000">Age</span>", <span style="color: #0000ff">typeof</span>(<span style="color: #0000ff">int</span>), <span style="color: #0000ff">typeof</span>(Student));
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Name
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> [CompilerGenerated]
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">get</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">return</span> (<span style="color: #0000ff">string</span>)<span style="color: #0000ff">base</span>.GetValue(Student.NameDependencyProperty);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> [CompilerGenerated]
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">set</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">base</span>.SetValue(Student.NameDependencyProperty, <span style="color: #0000ff">value</span>);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span> Age
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> [CompilerGenerated]
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">get</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">return</span> (<span style="color: #0000ff">int</span>)<span style="color: #0000ff">base</span>.GetValue(Student.AgeDependencyProperty);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> [CompilerGenerated]
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">set</span>
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> {
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">base</span>.SetValue(Student.AgeDependencyProperty, <span style="color: #0000ff">value</span>);
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }
</pre><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> }</pre></pre>
<p>Even though the class initially didn’t inherit from DependencyObject, the weaved version does. It will also continually go up the inheritance chain until it finds the abstract base class, and makes that inherit from DependencyObject.</p>
<p>Next on the list is obviously documentation (what open source project doesn’t need more documentation?!), however, I think I can slack off on this one since the core library is only 200-300 lines of code :-)</p> Anonymoushttp://www.blogger.com/profile/12728014380144489730noreply@blogger.com2