<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" version="2.0">
  <channel>
    <title>Mister Goodcat</title>
    <description>A place to purr</description>
    <link>http://www.pitorque.de/MisterGoodcat/</link>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>BlogEngine.NET 2.5.0.6</generator>
    <language>en-US</language>
    <blogChannel:blogRoll>http://www.pitorque.de/MisterGoodcat/opml.axd</blogChannel:blogRoll>
    <dc:creator>Mister Goodcat</dc:creator>
    <dc:title>Mister Goodcat</dc:title>
    <geo:lat>0.000000</geo:lat>
    <geo:long>0.000000</geo:long>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/MisterGoodcat" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="mistergoodcat" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
      <title>Windows Phone: Background Agents Pitfalls (2 of n)</title>
      <description>&lt;p&gt;In the first part of this series, I mainly talked about issues with software design when you work with background agents. We saw that the partly really strict API limitations can have a pretty severe effect on how you need to structure your code to follow all requirements of the validation and certification process. This time, I want to get a bit more technical, when we learn about some other limitations of agents. &lt;/p&gt;
&lt;h1&gt;Part 2: Timeout and Memory Constraints&lt;/h1&gt;
&lt;p&gt;Once again I'm stunned about how little this particular aspect of agents is covered in most articles and tutorials, if at all. Fact is that you're facing a pretty hefty usage cap regarding memory when your code executes in the background, and timeout limits apply as well. The details of this constraint can be found in the MSDN documents &lt;a href="http://msdn.microsoft.com/en-us/library/hh202942(v=vs.92).aspx"&gt;here&lt;/a&gt;. It says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Periodic agents typically run for 25 seconds."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Periodic agents and resource-intensive agents can use no more than 6 MB of memory at any time. [&amp;hellip;] If a Scheduled Task exceeds this memory cap, it is terminated immediately."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: I'm focusing on periodic agents here, not on the special case of resource-intensive or audio agents. Everything that is said however also applies to these cases, accordingly.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So we have a maximum of 6 megabytes at our disposal. That's the equivalent of approximately 0.02 seconds of uncompressed full HD 1080p50 video :). Nah, seriously: we all know that programmers have achieved amazing things with extremely little memory consumption in the past. However, by today's standards, and using a rich-featured environment like Silverlight and the .NET Framework 6 megabytes really don't seem like a lot. This is put into perspective a bit when you think about the fact that your agent will not have any UI elements and visible content. Traditionally, pixels take quite a share of an applications memory, and since we don't have images, animations and controls in our background processing, this does not apply.&lt;/p&gt;
&lt;p&gt;One of the fundamental problems however is that a lot of developers do not have a good sense for the amount of memory they will need for a certain operation or scenario. That's not really their fault. Ten years of managed code and the comfort of automated memory management, combined with the fact that we usually have gigabytes of memory at our hands at any time, have blunted these senses. Some of the younger devs even might never have done manual memory management in their whole lives, at all. Fortunately, we have some possibilities to help us keep track of our memory usage and execution time on the phone.&lt;/p&gt;
&lt;h2&gt;The Debug Dilemma&lt;/h2&gt;
&lt;p&gt;Before we start taking a closer look at the way to analyze the situation, I want to briefly talk about what I call the debug dilemma. The problem with debugging is that things like timeouts and other constraints get in your way when you least want it. When you step through your code to analyze it or look for issues, you don't want to be interrupted by timeout exceptions or trigger memory constraints. Of course the team at Microsoft has recognized this problem too, hence the following can be found in the above docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"When running under the debugger, memory and timeout restrictions are suspended."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This makes it very convenient to thoroughly test your code; the dilemma is that as soon as you are debugging, you don't get the same behavior as in the real world, and if you never test your app without a debugger attached, everything might look perfectly fine at your end. Until your app fails certification that is, or even worse, until after your users download the app and run into crashes and non-working features. So, to test things like timeouts, we need to put some more effort into our code and testing.&lt;/p&gt;
&lt;h2&gt;Tracking Memory Usage&lt;/h2&gt;
&lt;p&gt;To get information about both your application's memory usage as well as the system's limits the class &lt;a href="http://msdn.microsoft.com/en-us/library/hh239075(v=vs.92).aspx"&gt;DeviceStatus&lt;/a&gt; has been added in Windows Phone 7.1. This class has three particularly interesting properties for our purposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.phone.info.devicestatus.applicationmemoryusagelimit(v=vs.92).aspx"&gt;ApplicationMemoryUsageLimit&lt;/a&gt;: Returns the maximum amount of memory your application process can allocate, in bytes. The actual value of this property depends on the current context it is called from (application or background agent) as well as other factors, like the amount available on the device. For background agents, this typically returns 6291456 bytes.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.phone.info.devicestatus.applicationcurrentmemoryusage(v=vs.92).aspx"&gt;ApplicationCurrentMemoryUsage&lt;/a&gt;: Returns the number of currently allocated bytes by your application. This value combined with the previously described limit can be used to determine what amount of memory is left for your process to allocate.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.phone.info.devicestatus.applicationpeakmemoryusage(v=vs.92).aspx"&gt;ApplicationPeakMemoryUsage&lt;/a&gt;:&amp;nbsp; Returns the peak amount of memory your application process has used, in bytes. This is interesting during testing and debugging, to see whether your memory consumption stayed within reasonable/allowed bounds during the whole lifetime of the process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To simplify my testing, I'm using a class that derives from &lt;em&gt;ScheduledTaskAgent&lt;/em&gt; and implements some convenient methods. My actual agent then in turn derives from my helper class, so I get to use these methods easily. By using the &lt;em&gt;Conditional&lt;/em&gt; attribute, I don't have to worry too much about the methods; once compiled using the release configuration (which is required for submission), the method calls are stripped from my application by the compiler:&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;[Conditional(&lt;span class="str"&gt;"DEBUG"&lt;/span&gt;)]&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DebugOutputMemoryUsage(&lt;span class="kwrd"&gt;string&lt;/span&gt; label = &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    var limit = DeviceStatus.ApplicationMemoryUsageLimit;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    var current = DeviceStatus.ApplicationCurrentMemoryUsage;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    var remaining = limit - current;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    var peak = DeviceStatus.ApplicationPeakMemoryUsage;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    var safetyMargin = limit - peak;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (label != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;        Debug.WriteLine(label);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    Debug.WriteLine(&lt;span class="str"&gt;"Memory limit (bytes): "&lt;/span&gt; + limit);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    Debug.WriteLine(&lt;span class="str"&gt;"Current memory usage: {0} bytes ({1} bytes remaining)"&lt;/span&gt;, current, remaining);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    Debug.WriteLine(&lt;span class="str"&gt;"Peak memory usage: {0} bytes ({1} bytes safety margin)"&lt;/span&gt;, peak, safetyMargin);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This method can now be used in my agent anywhere I want to get a snapshot of the current memory usage, for example as the first thing to do in the &lt;em&gt;OnInvoke&lt;/em&gt; override, and again as the last thing to do just before I call the &lt;em&gt;NotifyComplete&lt;/em&gt; base method.&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnInvoke(ScheduledTask task)&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  DebugOutputMemoryUsage(&lt;span class="str"&gt;"Initial Memory Snapshot:"&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  &lt;span class="rem"&gt;// perform actual agent logic&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  DebugOutputMemoryUsage(&lt;span class="str"&gt;"Final Memory Snapshot:"&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;  NotifyComplete();&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now if you expect that the first snapshot will return something close to the 6 megabytes you are granted, you're wrong. For my sample application, this is the output of the first call:&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;Initial Memory Snapshot:&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;Memory limit (bytes): 6291456&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;Current memory usage: 3764224 bytes (2527232 bytes remaining)&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;Peak memory usage: 3764224 bytes (2527232 bytes safety margin)&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, right at the entry point into my custom code, before I even have executed the first line of my own logic, the agent process already is consuming more than half of the 6 megabytes! The overhead of running my code, including the referenced assemblies that are already loaded into memory, ate up more than 3.5 megabytes of memory.&lt;/p&gt;
&lt;p&gt;Then, after loading some local data from isolated storage and performing some service calls, the final snapshot looks something like this:&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;Final Memory Snapshot:&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;Memory limit (bytes): 6291456&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;Current memory usage: 6422528 bytes (-131072 bytes remaining)&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;Peak memory usage: 6422528 bytes (-131072 bytes safety margin)&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Doh! I've already exceeded the allowed quota by 128 kilobytes, and there's a chance that my agent will be terminated when it's running without the debugger attached. Now you might think that I made up this sample and deliberately tried to exceed the limit. Not at all! I encourage you to run these tests on your own agents, and you will see that really not much is required to get into that kind of trouble.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: please do not forget to test this on a real device. In my tests, the values on real hardware always were even slightly worse than in the emulator. Testing in the emulator only is NOT sufficient.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What can you do to solve these problems? Well, the usual things when it comes to saving memory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid loading large amounts of data from isolated storage or remote services. Try to break down the data into small chunks and only load into memory what you really need.&lt;/li&gt;
&lt;li&gt;Try to optimize your data format. For example, instead of loading an Xml file, a binary format might work as well. But be careful not to worsen the situation. Loading a compressed version of the file just to expand it back to its original content in memory, is not exactly helping :).&lt;/li&gt;
&lt;li&gt;Strip down dependencies as much as possible. We've seen in the first part of the series that often you have to restructure your code base for agents anyway. Refactor it into separate assemblies so your agent does not have to load any code or libraries into memory that it doesn't use or need.&lt;/li&gt;
&lt;li&gt;Watch out for typical mistakes people make in .NET/Silverlight, like not using &lt;em&gt;StringBuilders&lt;/em&gt; when concatenating strings and similar things. Make sure the garbage collector can do its work; choose variable scopes wisely, remove references when you don't need them anymore, and mind types that implement &lt;em&gt;IDisposable&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;In critical situations, you may once again have to change your software design. If you cannot reduce memory consumption to reasonable levels no matter what you try, then maybe it's time to consider removing a fancy third party component and look for something simpler, or create an own, more optimized implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One important last thing is that you should always try to test your agent for the worst case (the situation where it consumes most memory), and make sure that you still have a bit of a safety margin left in these scenarios. Remember: &lt;em&gt;if your agent is terminated two consecutive times, it will be unscheduled automatically.&lt;/em&gt; So you want to avoid running into this limit at all costs.&lt;/p&gt;
&lt;h2&gt;Keeping Track of the Time&lt;/h2&gt;
&lt;p&gt;Timeouts usually are the less critical factor for agents. You can really do a lot in 25 seconds. Still, you should keep an eye on execution time too, to be on the safe side. In terms of the base class I talked about above, this translates to some code like:&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="preproc"&gt;#if&lt;/span&gt; DEBUG&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;  &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;long&lt;/span&gt; MaximumMilliseconds = 25000L;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;readonly&lt;/span&gt; Stopwatch _stopwatch = &lt;span class="kwrd"&gt;new&lt;/span&gt; Stopwatch();&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;&lt;span class="preproc"&gt;#endif&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;[Conditional(&lt;span class="str"&gt;"DEBUG"&lt;/span&gt;)]&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DebugStartStopwatch()&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    _stopwatch.Start();&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;[Conditional(&lt;span class="str"&gt;"DEBUG"&lt;/span&gt;)]&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DebugOutputElapsedTime(&lt;span class="kwrd"&gt;string&lt;/span&gt; label = &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    var milliSeconds = _stopwatch.ElapsedMilliseconds;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    var remaining = MaximumMilliseconds - milliSeconds;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (label != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;        Debug.WriteLine(label);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;    Debug.WriteLine(&lt;span class="str"&gt;"Running time: {0} milliseconds"&lt;/span&gt;, milliSeconds);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;    Debug.WriteLine(&lt;span class="str"&gt;"Remaining time (max): {0} milliseconds"&lt;/span&gt;, remaining);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This again allows me to take snapshots of the running time at any point in my agent, to get an idea what potentially critical places in my code are. One important place of course is the time just before you call &lt;em&gt;NotifyComplete()&lt;/em&gt;.&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="rem"&gt;// output memory usage and performance&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;DebugOutputElapsedTime(&lt;span class="str"&gt;"Final Time Snapshot:"&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;NotifyComplete();&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This may result in a sample output like:&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;Final Time Snapshot:&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;Running time: 563 milliseconds&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;Remaining time (max): 24437 milliseconds&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;There's not many recommendations to make here. The biggest dangers are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Features that depend on external resources, like making network calls. Once the network connection is slow or a remote resource is not available, your calls may not return for a long period of time or timeout altogether, which may become a problem.&lt;/li&gt;
&lt;li&gt;Excessive use of isolated storage. Especially writing to the storage is &lt;em&gt;much&lt;/em&gt; slower than what you are used to from desktop computer hard disks or even SSDs. If you generate a lot of data that you want to write to isolated storage, then an agent probably is not the best place to do this.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first problem is not straight-forward to tackle on the phone, because the involved networking classes do not expose simple-to-use properties to e.g. set shorter timeouts. There are some solutions available using wait events or timers to establish custom timeouts when you use classes like the &lt;em&gt;WebClient&lt;/em&gt;; this however exceeds the scope of this post.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When you're working with background agents, especially the memory constraints can quickly turn into a problem that's not very easy to solve. Furthermore, due to the lifted restrictions in your development environment, it can be a challenge to even recognize that a problem exists with your implementation at all. The ways shown here allow you to keep an eye on your agents execution time and memory consumption all the time, so you can take countermeasures early and identify problems before your users (painfully) will.&lt;/p&gt;</description>
      <link>http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-Background-Agents-Pitfalls-(2-of-n).aspx</link>
      <comments>http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-Background-Agents-Pitfalls-(2-of-n).aspx#comment</comments>
      <guid>http://www.pitorque.de/MisterGoodcat/post.aspx?id=df22aa0d-9d5d-4d3b-bf27-58ad2b928124</guid>
      <pubDate>Fri, 24 Feb 2012 00:21:00 +0100</pubDate>
      <category>Programming</category>
      <dc:publisher>Mister Goodcat</dc:publisher>
      <pingback:server>http://www.pitorque.de/MisterGoodcat/pingback.axd</pingback:server>
      <pingback:target>http://www.pitorque.de/MisterGoodcat/post.aspx?id=df22aa0d-9d5d-4d3b-bf27-58ad2b928124</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://www.pitorque.de/MisterGoodcat/trackback.axd?id=df22aa0d-9d5d-4d3b-bf27-58ad2b928124</trackback:ping>
      <wfw:comment>http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-Background-Agents-Pitfalls-(2-of-n).aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.pitorque.de/MisterGoodcat/syndication.axd?post=df22aa0d-9d5d-4d3b-bf27-58ad2b928124</wfw:commentRss>
    </item>
    <item>
      <title>Windows Phone: Background Agents Pitfalls (1 of n)</title>
      <description>&lt;p&gt;There's a ton of resources available on the web that talks about the new features of Windows Phone 7.5 "Mango" for developers, and about background agents in particular. Unfortunately, a lot of these resources use over-simplified samples that have little in common with complex real-world setups, and often the articles you can find don't even mention the several restrictions in place for background agents at all. In this open-ended mini series I am going to talk about the various problems you will potentially run into with anything but the most trivial applications, what effects and consequences this has for your application development, and how you can avoid pitfalls and plan ahead for background agents. This is not a general introduction on the topic; I assume that you are familiar with the concept and have a basic understanding on how agents work. &lt;/p&gt;
&lt;h1&gt;Part 1: API restrictions&lt;/h1&gt;
&lt;p&gt;When you work with background agents, a lot of restrictions on the APIs you are allowed to use are in place. Most of these restrictions are understandable and either simply a logical consequence of the fact that agents do not have any sort of UI or other forms of user interaction, that agents do not run in the same full runtime environment that is spun up for normal applications, or they are in effect to prevent malicious behavior. You can find a list of these unsupported APIs in the MSDN documentation &lt;a href="http://msdn.microsoft.com/en-us/library/hh202962(v=vs.92).aspx"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the pain points with this however is that your agent runs through a static code analysis during submission (or when you use the Marketplace Test Kit) that is not intelligent enough to determine whether you're actually using those APIs from within your agent, or not. This means that any of your code or referenced assemblies or projects in your solution make use of unsupported APIs, you'll receive errors during the capabilities validation, and as a consequence your application will fail the test cases. Let me discuss these problems using a few real-world examples.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;The usual setup when you want to integrate background agents in your application is to refactor your existing code base so both the agent assembly and your main application share a common set of components or libraries. This enables your agent to e.g. use the same code for configuration, loading data etc. as your application:&lt;/p&gt;
&lt;p&gt;&lt;img style="background-image: none; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px; border: 0px;" title="image" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=image_164.png" alt="image" width="376" height="300" border="0" /&gt;&lt;/p&gt;
&lt;p&gt;This simply is following the principles of modular programming (DRY) and should be common sense among developers. As soon as you start working with such a setup, I recommend to make use of the Marketplace Test Kit often, starting early on in your development process, to identify any of the discussed points below as soon as possible. This allows you to apply needed refactoring or change your software design when it's still cheap to do.&lt;/p&gt;
&lt;h2&gt;Example 1: Smelly Code&lt;/h2&gt;
&lt;p&gt;Problems often arise when you don't closely follow best practices like the principle of single responsibility. In many of these situations it's not developers being lazy or inexperienced, but the fact that the limited resources on the phone sometimes seem to require to let go of decoupling and isolation down to the very last detail. Additionally, most of the available online samples in the MSDN docs are not helping with these issues either, as they often simplify and don't put emphasize on the value of these patterns or best practices (which is, given their purpose, understandable).&lt;/p&gt;
&lt;p align="center"&gt;Let's say you want to create a central repository in your application that handles data persistence in a nice way. What you probably need to do is add some initialization and tear down logic that is executed at certain points of the application lifetime (launching/activation vs. deactivation/closing). What I often see when I review apps is that people create that persistence layer in a way that it is able to handle the phone's lifetime events internally. Since we have convenient access to a global &lt;em&gt;PhoneApplicationService&lt;/em&gt; instance everywhere, this is pretty easy to do, and the result is that you have a completely encapsulated package of a repository, that has a very limited public surface for initialization and shutdown.&lt;/p&gt;
&lt;p align="center"&gt;&lt;img style="background-image: none; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px; border: 0px;" title="image" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=image_165.png" alt="image" width="353" height="270" border="0" /&gt;&lt;/p&gt;
&lt;p&gt;The problem is that when you now try to use your persistence implementation in your background agent (which is a perfectly legitimate thing to do), you will receive validation errors, because it's not allowed to hook into the lifetime events of the phone's application service from a background agent. Even if you implement your code so that you can turn off this behavior when it is running in the context of the agent, validation will still fail. The fact that you're referencing the APIs in question, even if they're not actually invoked at runtime, is sufficient to break the tests.&lt;/p&gt;
&lt;p&gt;In these simple cases it's easy to resolve the problem; in our example, you would simply follow the principle of single responsibility, and handle the phone's lifetime events from outside the repository and call its respective methods for initialization and shutdown, so you don't need to reference them from within your code.&lt;/p&gt;
&lt;p&gt;&lt;img style="background-image: none; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px; border: 0px;" title="image" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=image_166.png" alt="image" width="523" height="174" border="0" /&gt;&lt;/p&gt;
&lt;h2&gt;Example 2: Excessive Granularity&lt;/h2&gt;
&lt;p&gt;There are some cases where the granularity of the list of supported APIs is so detailed that the restrictions themselves make it impossible for you to satisfy the requirements in a clean way, even when you follow every best coding practice. Let me give you an example for that. In the list of unsupported APIs I've linked to above, you can find the following entry:&lt;/p&gt;
&lt;table style="margin-left: auto; margin-right: auto;" border="1" cellspacing="0" cellpadding="2"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td valign="top" width="167"&gt;&lt;strong&gt;Namespace&lt;/strong&gt;&lt;/td&gt;
&lt;td valign="top" width="433"&gt;&lt;strong&gt;Unsupported API&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td valign="top" width="167"&gt;Microsoft.Phone.Shell&lt;/td&gt;
&lt;td valign="top" width="433"&gt;All APIs are unsupported except the following:
&lt;ul&gt;
&lt;li&gt;ShellToast class&lt;/li&gt;
&lt;li&gt;Update(ShellTileData) method of the ShellTile class&lt;/li&gt;
&lt;li&gt;Delete() method of the ShellTile class&lt;/li&gt;
&lt;li&gt;ActiveTiles property of the ShellTile class.&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As you can see, the differentiation here goes all the way down to individual methods and properties of certain classes. The motivation of course is perfectly clear and understandable: for obvious reasons an agent should not be allowed to create new tiles, for example. For you as developer this however creates a whole new set of problems. If you want to work with a combination of agents and secondary tiles, you are obviously going to use all parts of the available API: you will create new secondary tiles from within your application, and you also want to update the tile's content from within your background agent when new data is available for display. That fancy class you created for this, which nicely does all the tile creation and updating for you cannot be used from within your agent, because referencing the &lt;em&gt;Create(ShellTileData)&lt;/em&gt; method of the &lt;em&gt;ShellTile&lt;/em&gt; class is not allowed. This is the point where re-using existing code and modularizing your components breaks. How do you solve that?&lt;/p&gt;
&lt;p&gt;In our sample, the public interface of our class (without the implementation details) would probably look something like:&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; TileHelper&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; HasTile { get; }&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CreateTile(&lt;span class="kwrd"&gt;int&lt;/span&gt; itemCount, &lt;span class="kwrd"&gt;string&lt;/span&gt; message);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UpdateTile(&lt;span class="kwrd"&gt;int&lt;/span&gt; itemCount, &lt;span class="kwrd"&gt;string&lt;/span&gt; message);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DeleteTile();&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;Conditional Compilation/Partial Classes&lt;/h3&gt;
&lt;p&gt;One way of solving this is to use linked code files and conditional compilation. Basically, the idea is to add a conditional compilation symbol to your agent project settings (for example "BACKGROUNDAGENT"). Then you can update the code to:&lt;/p&gt;
&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
&lt;div id="codeSnippet" class="csharpcode"&gt;
&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; TileHelper&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; HasTile { get; }&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&lt;span class="preproc"&gt;#if&lt;/span&gt; !BACKGROUNDAGENT&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CreateTile(&lt;span class="kwrd"&gt;int&lt;/span&gt; itemCount, &lt;span class="kwrd"&gt;string&lt;/span&gt; message);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;&lt;span class="preproc"&gt;#endif&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;&amp;nbsp;&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UpdateTile(&lt;span class="kwrd"&gt;int&lt;/span&gt; itemCount, &lt;span class="kwrd"&gt;string&lt;/span&gt; message);&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alteven"&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DeleteTile();&lt;/pre&gt;
&lt;!--CRLF--&gt;
&lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Using Visual Studio's linked file feature, you can then include the file in multiple places (the background agent and your main application), and depending on the context the critical APIs will be included in the compiled result, or not. Similar approaches use linked files in combination with partial classes, to physically structure and separate the individual parts a bit nicer.&lt;/p&gt;
&lt;p&gt;I dislike this version a lot; usually it leads to follow-up problems because due to the required assembly references for background agents the file effectively is included multiple times and produces naming conflicts and potentially confusing errors ("ambiguous reference"). This forces you to for example also change the namespace declaration based on the conditional compilation symbol, so the naming conflict is resolved properly. This leaves two versions of your components in separate namespaces, and can lead to a poorly maintainable and hard to understand code base quickly.&lt;/p&gt;
&lt;h3&gt;Inheritance&lt;/h3&gt;
&lt;p&gt;Another solution is to use inheritance (or, sometimes, aggregation). Often you can split the problematic classes into derived classes, and put those classes into the agent and app (or into separate class libraries used by the agent and app). Then the setup would look like this:&lt;/p&gt;
&lt;p&gt;&lt;img style="background-image: none; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px; border: 0px;" title="image" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=image_167.png" alt="image" width="347" height="204" border="0" /&gt;&lt;/p&gt;
&lt;p&gt;I also don't like this second version very much, but to me it seems like the lesser evil compared to the linked files workaround described above.&lt;/p&gt;
&lt;p&gt;This is a situation where a technical restriction directly affects your software design, and even forces you into giving up some of your perfectly valid code structures. It is another reason you should make use of the Marketplace Test Kit often, so you can identify these problematic places quickly.&lt;/p&gt;
&lt;h2&gt;Example 3: External Libraries&lt;/h2&gt;
&lt;p&gt;This is the most critical aspect, because if you're running into problems with external libraries with your background agents, solutions often require the support of the original author of that library. The potential problems in detail are exactly the same as with your own code: the author of a library needs to be aware of the scenario that their component will potentially be used in a background agent, and accommodate for the multiple limitations and pitfalls in their own code.&lt;/p&gt;
&lt;p&gt;I am guilty of not paying enough attention to this aspect myself; let me quickly explain what happened. I'm the author of a few open source libraries available on CodePlex, and one of that libraries is named "&lt;a href="http://ylod.codeplex.com/"&gt;Your Last Options Dialog&lt;/a&gt;". The idea is to save you the time of creating an option dialog for your Windows Phone application and do that automatically for you (very similar to one of my other projects &amp;ndash; &lt;a href="http://ylad.codeplex.com/"&gt;YLAD&lt;/a&gt; &amp;ndash; which does the same for about dialogs and has reached a bit of popularity). You simply use one of your already existing POCOs, pass it to the component, and it auto-magically creates the options page(s) for you, using a pre-defined set of controls to edit the POCO properties, based on their types. Now, to give you as a developer some ways to influence the appearance and structure of that dialog, I added the possibility to annotate your POCO properties with certain attributes the component understands. And here comes the problem.&lt;/p&gt;
&lt;p&gt;Imagine that the POCO you're using for the YLOD project is a central part of your persistence logic, for example the data container you use in serialization to and from isolated storage. Then you probably want to also use this type in your background agent for one or another reason. However, to use this settings class which is now decorated with YLOD's attributes, you also need to reference the YLOD assembly &amp;ndash; and there you go, since YLOD uses various APIs to navigate between pages and other things, your background agent fails validation.&lt;/p&gt;
&lt;p&gt;&lt;img style="background-image: none; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px; border: 0px;" title="image" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=image_168.png" alt="image" width="564" height="356" border="0" /&gt;&lt;/p&gt;
&lt;p&gt;This is another example of how validation of your own background agent (and hence application) can fail even though you're not actually doing anything forbidden. The simple fact that an external library in turn has a reference to an unsupported API is problematic. And in these cases, like I said, there's often little you can do yourself to solve the problem.&lt;/p&gt;
&lt;p&gt;YLOD of course has since been updated, and its safe core features like the annotation attributes have been refactored into a separate assembly, which now allows you to use your settings POCO container from your background agents even when they're using the YLOD attributes (this improvement is available starting in version 1.0 of YLOD):&lt;/p&gt;
&lt;p&gt;&lt;img style="background-image: none; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; padding-top: 0px; border: 0px;" title="image" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=image_169.png" alt="image" width="342" height="356" border="0" /&gt;&lt;/p&gt;
&lt;p align="left"&gt;This pattern is also suitable to structure your own code into parts that are safe to use from background agents, and those that are only valid to use from the full app.&lt;/p&gt;
&lt;h2 align="left"&gt;Conclusion&lt;/h2&gt;
&lt;p align="center"&gt;I think the examples demonstrate that the really nice new feature of having background agents comes with strings attached. You have to carefully plan ahead some of the aspects and think about what parts of your components you're going to use in your background agents code, or you will face painful and unexpected refactoring work later on in the development process. Sometimes this can be solved with a simple restructuring of your code, sometimes it requires more complex work like creating multiple satellite class libraries, or working with shared files or additional inheritance. The worst case is that you cannot resolve the situation yourself at all when you're using third party libraries that do not support background agents, and that you have to rework large parts of your application logic because of that.&lt;/p&gt;
&lt;p align="center"&gt;I hope you enjoyed this first part of the mini series; if you have additional thoughts or want to comment, feel free to leave me a note below, or contact me directly. In the next part I'll continue to look at additional challenges you as developer have to master with background agents. Stay tuned!&lt;/p&gt;</description>
      <link>http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-Background-Agents-Pitfalls-(1-of-n).aspx</link>
      <comments>http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-Background-Agents-Pitfalls-(1-of-n).aspx#comment</comments>
      <guid>http://www.pitorque.de/MisterGoodcat/post.aspx?id=4a725fd8-1f08-4495-91b8-8c4976653622</guid>
      <pubDate>Sun, 19 Feb 2012 20:24:00 +0100</pubDate>
      <category>Programming</category>
      <dc:publisher>Mister Goodcat</dc:publisher>
      <pingback:server>http://www.pitorque.de/MisterGoodcat/pingback.axd</pingback:server>
      <pingback:target>http://www.pitorque.de/MisterGoodcat/post.aspx?id=4a725fd8-1f08-4495-91b8-8c4976653622</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://www.pitorque.de/MisterGoodcat/trackback.axd?id=4a725fd8-1f08-4495-91b8-8c4976653622</trackback:ping>
      <wfw:comment>http://www.pitorque.de/MisterGoodcat/post/Windows-Phone-Background-Agents-Pitfalls-(1-of-n).aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.pitorque.de/MisterGoodcat/syndication.axd?post=4a725fd8-1f08-4495-91b8-8c4976653622</wfw:commentRss>
    </item>
    <item>
      <title>What's New in Open Source?</title>
      <description>&lt;p&gt;Almost two years ago, Firefox started to isolate plug-ins like Silverlight in a separate operating system process. One of the consequences was that debugging Silverlight applications with Visual Studio became somewhat more tedious, because the Silverlight debug engine was not able to automatically attach to the correct processes anymore. At that time, I started developing a small and simple Visual Studio Add-In named &amp;quot;Firefox Debug Helper&amp;quot;. Later on, when I discovered that Visual Studio also had problems with other browsers like Internet Explorer 9 and Chrome, I extended support for this Add-In to these browsers, and renamed the tool to &amp;quot;Silverlight Debug Helper&amp;quot;. &lt;/p&gt;  &lt;p&gt;Today, the topic is still relevant for Silverlight developers, and this small tool continues to serve me and others well. Every now and then, someone asks for the source code of the Debug Helper Add-In, either to learn about how to extend Visual Studio by a practical example, to extend the existing features, or because they want to track down issues when they use the Add-In. I'm pleased to announce that starting today, the source code of the &amp;quot;Silverlight Debug Helper&amp;quot; is available for free to everyone (the tool itself always was available for free from the first day) on CodePlex. It's not very sophisticated and probably could be improved in some areas, but I still hope those of you interested in it can make good use from this release. The new project page also is the place where new releases will be made available at, if applicable:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://sldebughelper.codeplex.com"&gt;http://sldebughelper.codeplex.com&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;This also means that the existing project page here on this blog will be suspended and link to the new home on CodePlex from now on.&lt;/p&gt;    &lt;h2&gt;YLAD&lt;/h2&gt;  &lt;p&gt;Today, I've also released a new version of &amp;quot;Your Last About Dialog&amp;quot; for Windows Phone. This new release adds support for trial mode, which means that a &amp;quot;buy&amp;quot; button is automatically added to the about page if you are using this feature of Windows Phone. As always, I've tried to make the default behavior suitable for most scenarios, so you usually don't have to worry about it – it simply will work &amp;quot;out of the box&amp;quot;. But if you want to override the default behavior and either force the buy button feature both on or off for your particular scenario, YLAD offers the flexibility to do so. The new version is already available on CodePlex and (recommended) NuGet, and in addition improves the default configuration template as well as fixes a potential bug with multiple successive taps on the &amp;quot;review&amp;quot; button of the about page. Learn more about the release and the new features on the project page and its documentation section:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ylad.codeplex.com"&gt;http://ylad.codeplex.com&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Have fun ;)&lt;/p&gt;</description>
      <link>http://www.pitorque.de/MisterGoodcat/post/Whats-New-in-Open-Source.aspx</link>
      <comments>http://www.pitorque.de/MisterGoodcat/post/Whats-New-in-Open-Source.aspx#comment</comments>
      <guid>http://www.pitorque.de/MisterGoodcat/post.aspx?id=0734b0bd-c2f4-4a59-9838-3f59d157b9ec</guid>
      <pubDate>Sun, 12 Feb 2012 23:24:47 +0100</pubDate>
      <category>Programming</category>
      <category>Tools</category>
      <dc:publisher>Mister Goodcat</dc:publisher>
      <pingback:server>http://www.pitorque.de/MisterGoodcat/pingback.axd</pingback:server>
      <pingback:target>http://www.pitorque.de/MisterGoodcat/post.aspx?id=0734b0bd-c2f4-4a59-9838-3f59d157b9ec</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.pitorque.de/MisterGoodcat/trackback.axd?id=0734b0bd-c2f4-4a59-9838-3f59d157b9ec</trackback:ping>
      <wfw:comment>http://www.pitorque.de/MisterGoodcat/post/Whats-New-in-Open-Source.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.pitorque.de/MisterGoodcat/syndication.axd?post=0734b0bd-c2f4-4a59-9838-3f59d157b9ec</wfw:commentRss>
    </item>
    <item>
      <title>Something's Missing from the WebBrowser Control</title>
      <description>&lt;p&gt;Sometimes seemingly simple things are the most expensive to achieve. One such thing involves the Windows Phone &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.phone.controls.webbrowser(v=vs.92).aspx"&gt;WebBrowser control&lt;/a&gt; that allows you to show arbitrary web content embedded in your application.&lt;/p&gt;  &lt;p&gt;The problem I ran into is that the control does not show any scrollbars when the content is larger than the visible area. This is a bit of a surprise, because the built-in browser of the phone does this nicely:&lt;/p&gt;  &lt;p&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top: 0px; border-right: 0px; padding-top: 0px" title="ie9_webbrowser_control_comparison" border="0" alt="ie9_webbrowser_control_comparison" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=ie9_webbrowser_control_comparison.png" width="640" height="366" /&gt;&lt;/p&gt;  &lt;p&gt;This means that your user has no indication of the total content length or the current scroll position when you show HTML content in your application, which is a bit unfortunate. In the following I talk about the various attempts and ideas I tried to fix this; if you're only interested in a possible work around, you can skip to the end of the post. &lt;/p&gt;  &lt;h2&gt;&lt;/h2&gt;  &lt;h2&gt;First Attempts&lt;/h2&gt;  &lt;p&gt;Some of the obvious things you might try in that situation if you're familiar with Windows Phone or Silverlight development is to set the &lt;a href="http://msdn.microsoft.com/en-us/library/ms605401(v=vs.95).aspx"&gt;VerticalScrollBarVisibility attached property&lt;/a&gt; to the appropriate value, or to wrap the whole WebBrowser control in a &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer(v=vs.95).aspx"&gt;ScrollViewer element&lt;/a&gt; and similar thing. None of these methods will result in anything usable.&lt;/p&gt;  &lt;p&gt;When you search around the web you will find that others are struggling with this too, and there doesn't seem an out of the box solution to it. In &lt;a href="http://forums.create.msdn.com/forums/p/63979/391857.aspx"&gt;this topic&lt;/a&gt; in the MSDN forums Mark Chamberlain even states:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;quot;I verified internally that the WebBrowser control by its very nature does not support the vertical scrollbar, and the mouse events are not exposed that otherwise would allow you to roll your own scrolling mechanism.&amp;quot;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;And, some time later:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&amp;quot;If possible you should consider architecting your application so you don't need a vertical scrollbar in WebBrowser.&amp;quot;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Uhm… okay…? To be honest, I found that answer a bit disappointing, so I dug a bit deeper.&lt;/p&gt;  &lt;h2&gt;Ideal Solution&lt;/h2&gt;  &lt;p&gt;The best solution to the problem would be if you could simply tweak some of the involved elements so the control works with arbitrary content. To get an idea of the internals, I first took a look at the visual tree of a sample app that has the WebBrowser control on a page:&lt;/p&gt;  &lt;p&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top: 0px; border-right: 0px; padding-top: 0px" title="visual_tree" border="0" alt="visual_tree" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=visual_tree.png" width="283" height="156" /&gt;&lt;/p&gt;  &lt;p&gt;As you can see, the tree is pretty simple for the WebBrowser control: &amp;quot;TileHost&amp;quot; seems to be the native IE component, whereas the other interesting control here is &amp;quot;PanZoomContainer&amp;quot; – this one seems to handle all the user interactions and looked like a promising place to start. Even more when you take a look at the element in the debugger:&lt;/p&gt;  &lt;p&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=image_163.png" width="581" height="223" /&gt;&lt;/p&gt;  &lt;p&gt;As you can see, this control maintains all the relevant information to present the current visible area to the user, and it would be perfect to implement an own, custom scrolling visualization. For example, using the information from the content size and current scale (or the scrollable height) and view port would be sufficient to calculate the values and offset of a custom scroll bar at any given time.&lt;/p&gt;  &lt;p&gt;Unfortunately, the PanZoomContainer control is internal, and due to the security restrictions of the platform that means you cannot access any of these values with Reflect-Fu, even the public ones. :(&lt;/p&gt;  &lt;p&gt;By the way, if you dig even further you'll notice that this container internally already maintains both a horizontal and vertical scroll bar too (_scrollH/_scrollV). These controls are ready to go and seem to have the correctly calculated and updated values at runtime – why they are not exposed or used, I don't know.&lt;/p&gt;  &lt;h2&gt;The Work Around&lt;/h2&gt;  &lt;p&gt;The other way to get information about the current scroll offset and total content size is from within the web page that is shown in the WebBrowser control itself. This is nothing new and involves some basic JavaScript. The reason why this is problematic is that you need to have control over the content you're loading into the control (either server-side, or you need to use locally stored content).&lt;/p&gt;  &lt;p&gt;In my case I was using local content anyway, so I injected some JavaScript into the code that I display in the WebBrowser control. A simplified version could look something like this (put in the head tag, for example):&lt;/p&gt;  &lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;   &lt;div id="codeSnippet" class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;function&lt;/span&gt; initialize() { &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;  window.external.notify(&lt;span class="str"&gt;&amp;quot;scrollHeight=&amp;quot;&lt;/span&gt; + document.body.scrollHeight.toString()); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;  window.external.notify(&lt;span class="str"&gt;&amp;quot;clientHeight=&amp;quot;&lt;/span&gt; + document.body.clientHeight.toString()); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;  window.onscroll = onScroll; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;function&lt;/span&gt; onScroll(e) { &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;  &lt;span class="kwrd"&gt;var&lt;/span&gt; scrollPosition = document.body.scrollTop; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;  window.external.notify(&lt;span class="str"&gt;&amp;quot;scrollTop=&amp;quot;&lt;/span&gt; + scrollPosition.toString()); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;window.onload = initialize;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The idea is that when the page loads, we want to notify our hosting phone app about both the height of the visible area and the available scroll height. Then, whenever the current scroll position changes, we want to pass on that information too.&lt;/p&gt;

&lt;p&gt;On the Silverlight side of things, first of all we have to add a separate ScrollBar control next to the WebBrowser in XAML, to fake the scrolling visualization:&lt;/p&gt;

&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
  &lt;div id="codeSnippet" class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ContentPanel&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        &lt;span class="attr"&gt;Grid&lt;/span&gt;.&lt;span class="attr"&gt;Row&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;1&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;        &lt;span class="attr"&gt;Margin&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;12,0,12,0&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;phone:WebBrowser&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ContentWebBrowser&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="attr"&gt;HorizontalAlignment&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Stretch&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;                            &lt;span class="attr"&gt;VerticalAlignment&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Stretch&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="attr"&gt;Margin&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;0 0 5 0&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;                            &lt;span class="attr"&gt;IsScriptEnabled&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;True&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;ScrollBar&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;DisplayScrollBar&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;                    &lt;span class="attr"&gt;Orientation&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Vertical&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="attr"&gt;HorizontalAlignment&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Right&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;                    &lt;span class="attr"&gt;VerticalAlignment&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Stretch&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="attr"&gt;Minimum&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;0&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;                    &lt;span class="attr"&gt;Maximum&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;100&amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Do not forget to set the &amp;quot;IsScriptEnabled&amp;quot; property of the WebBrowser control to true.&lt;/p&gt;

&lt;p&gt;Then we can hook the &amp;quot;ScriptNotify&amp;quot; event that is raised every time JavaScript invokes &amp;quot;window.external.notify&amp;quot; (see above). In this event handler we can inspect the value that is passed to Silverlight and set up the fake ScrollBar accordingly:&lt;/p&gt;

&lt;div id="codeSnippetWrapper" class="csharpcode-wrapper"&gt;
  &lt;div id="codeSnippet" class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&lt;span class="rem"&gt;// e.g. in the constructor, or in XAML&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;ContentWebBrowser.ScriptNotify += ContentWebBrowser_ScriptNotify;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;...&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; _visibleHeight = 0;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; _scrollHeight = 0;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ContentWebBrowser_ScriptNotify(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, NotifyEventArgs e)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;// split &lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    var parts = e.Value.Split(&lt;span class="str"&gt;'='&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (parts.Length != 2)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    &lt;span class="rem"&gt;// parse&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;int&lt;/span&gt; number = 0;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (!&lt;span class="kwrd"&gt;int&lt;/span&gt;.TryParse(parts[1], &lt;span class="kwrd"&gt;out&lt;/span&gt; number))&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;// decide what to do&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (parts[0] == &lt;span class="str"&gt;&amp;quot;scrollHeight&amp;quot;&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        _scrollHeight = number;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_visibleHeight &amp;gt; 0)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;            DisplayScrollBar.Maximum = _scrollHeight - _visibleHeight;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (parts[0] == &lt;span class="str"&gt;&amp;quot;clientHeight&amp;quot;&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        _visibleHeight = number;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_scrollHeight &amp;gt; 0)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;            DisplayScrollBar.Maximum = _scrollHeight - _visibleHeight;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;    &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (parts[0] == &lt;span class="str"&gt;&amp;quot;scrollTop&amp;quot;&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;        DisplayScrollBar.Value = number;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre class="alteven"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This works… &lt;/p&gt;

&lt;p&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px auto; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top: 0px; border-right: 0px; padding-top: 0px" title="webbrowser_with_scrollbar2" border="0" alt="webbrowser_with_scrollbar2" src="http://www.pitorque.de/MisterGoodcat/image.axd?picture=webbrowser_with_scrollbar2.png" width="500" height="440" /&gt;&lt;/p&gt;

&lt;p&gt;… but with an annoying limitation: As long as the user pans around in the WebBrowser control, the JavaScript &amp;quot;onScroll&amp;quot; event is not raised. Not even the &amp;quot;scrollTop&amp;quot; value is updated during that time (so you cannot use a different approach like a timer either). Only when the panning comes to a halt is the new &amp;quot;scrollTop&amp;quot; value of the page updated. This means that as long as scrolling is in progress, you don't see any updates of the scrollbar position in your app, which is kind of irritating and inconsistent compared to the normal behavior we know from the platform.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We have yet to find a way to make this work consistently and in a better way. My current work around is not perfect and slightly irritating in its behavior, but despite several other attempts I couldn't find a better alternative. One thing I haven't tried yet is to intercept user input for the control and make use of that. But without access to the internal state of at least the zoom/scaling that doesn't seem very promising anyway. If you have additional thoughts or can come up with something better, please let me know.&lt;/p&gt;</description>
      <link>http://www.pitorque.de/MisterGoodcat/post/Somethings-Missing-from-the-WebBrowser-Control.aspx</link>
      <comments>http://www.pitorque.de/MisterGoodcat/post/Somethings-Missing-from-the-WebBrowser-Control.aspx#comment</comments>
      <guid>http://www.pitorque.de/MisterGoodcat/post.aspx?id=4c88783a-e747-4768-b066-39630179afe7</guid>
      <pubDate>Mon, 30 Jan 2012 15:40:09 +0100</pubDate>
      <category>Programming</category>
      <dc:publisher>Mister Goodcat</dc:publisher>
      <pingback:server>http://www.pitorque.de/MisterGoodcat/pingback.axd</pingback:server>
      <pingback:target>http://www.pitorque.de/MisterGoodcat/post.aspx?id=4c88783a-e747-4768-b066-39630179afe7</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://www.pitorque.de/MisterGoodcat/trackback.axd?id=4c88783a-e747-4768-b066-39630179afe7</trackback:ping>
      <wfw:comment>http://www.pitorque.de/MisterGoodcat/post/Somethings-Missing-from-the-WebBrowser-Control.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.pitorque.de/MisterGoodcat/syndication.axd?post=4c88783a-e747-4768-b066-39630179afe7</wfw:commentRss>
    </item>
    <item>
      <title>I have joined Applied Information Technologies</title>
      <description>&lt;p&gt;I am proud to announce that I have joined the team at &lt;a href="http://www.aitgmbh.de/"&gt;Applied Information Technologies&lt;/a&gt; in Stuttgart, Germany. Founded in 1991, AIT has gained outstanding achievements over the years. In 2011 it became the first Microsoft Gold Partner for Application Lifecycle Management in Middle Europe, and was awarded as Microsoft DevTools Service Partner of the Year. AIT also is a Microsoft Gold Partner as Independent Software Vendor and has won the BQI Agile Leadership Award last year.&lt;/p&gt;
&lt;p&gt;Every single member of the team is a deeply committed, skilled professional in their respective fields. You may know some of these people from conference talks, MSDN webcasts, print publications, from their leading role in the community as Microsoft MVPs, or simply from one of the countless projects they have completed successfully. Needless to say that I am thrilled to be a part of this company, and to contribute to the success and spirit of the team. Starting February 1st, my involvement will cover the full .NET stack as well as Silverlight, as a consultant, software architect and more.&lt;/p&gt;
&lt;h2&gt;What will this mean for my blog?&lt;/h2&gt;
&lt;p&gt;Short answer: nothing will change. I receive full support from AIT for what I do, and to continue my investments in the community and Windows Phone, so please expect the same quality and amount of blog posts, articles, webcasts and open source work for the community in 2012 as in the years before.&lt;/p&gt;</description>
      <link>http://www.pitorque.de/MisterGoodcat/post/I-have-joined-Applied-Information-Technologies.aspx</link>
      <comments>http://www.pitorque.de/MisterGoodcat/post/I-have-joined-Applied-Information-Technologies.aspx#comment</comments>
      <guid>http://www.pitorque.de/MisterGoodcat/post.aspx?id=08352734-9f64-4af5-83bd-9d1786bc8da1</guid>
      <pubDate>Thu, 26 Jan 2012 19:15:00 +0100</pubDate>
      <category>Personal</category>
      <dc:publisher>Mister Goodcat</dc:publisher>
      <pingback:server>http://www.pitorque.de/MisterGoodcat/pingback.axd</pingback:server>
      <pingback:target>http://www.pitorque.de/MisterGoodcat/post.aspx?id=08352734-9f64-4af5-83bd-9d1786bc8da1</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://www.pitorque.de/MisterGoodcat/trackback.axd?id=08352734-9f64-4af5-83bd-9d1786bc8da1</trackback:ping>
      <wfw:comment>http://www.pitorque.de/MisterGoodcat/post/I-have-joined-Applied-Information-Technologies.aspx#comment</wfw:comment>
      <wfw:commentRss>http://www.pitorque.de/MisterGoodcat/syndication.axd?post=08352734-9f64-4af5-83bd-9d1786bc8da1</wfw:commentRss>
    </item>
  </channel>
</rss>

