Thursday, December 18, 2008

WPF Tips & Tricks Presentation

Yesterday, on Krakowska Grupa Deweloperów .NET, I gave a presentation, together with my friend Arkadiusz Świerczek, on latest Microsoft .NET Framework 3.5 SP1 WPF stuff and WPF Tips & Trick.

We didn't have enough time to cover all the goddies we've prepared, but we managed to introduce some basics around WPF and present WPF effects in pixel shader. Besides, we've presented how to write multi-language, runtime bound UI, skins and a rapid introduction to attached behaviors.

The presentation together with full sources can be downloaded from here. Enjoy!

Saturday, November 29, 2008

patterns & practices: Composite WPF and Silverlight Drop 6

It's been couple of days since I started playing with Prism V2 Drop 6 (You can download it from here). Of course I've been using CompositeWPF aka Prism V1 for a longer time, and I must admin it's an excellent framework, but I wanted to see it's new Silverlight part. Here's what I've found so far.

In general, drop 6 seems working just fine. It contains the following features known from the WPF part:
  • modularity
  • event broker
  • regions
It goes without saying that everything is wired up with P&P Unity 1.2 that also has a port for Silverlight. So if all you need is a good DI container, you can simply grab only the Unity bits from that release.

As I previously said, everything is working fine. One thing disturbs me, however. On the very first version of Prism, I was able to use ordinary classes as views, the view was then defined as a data template. So I could have written something like that:

   48 var downloadItemPresenter = m_Container.Resolve<NavigationItemPresenter>();
   49 downloadItemPresenter.Text = "Download";
   51 m_RegionManager.Regions[RegionNames.NavigationPanel].Add(downloadItemPresenter);

In this example, NavigationItemPresenter represents my custom button. However, there is no explicitly given view - the view is defined via data template. Unfortunately, from unknown reasons, this doesn't work in Silverlight. I receive NullPointerException. After "investigation" it turned out that internally, the view is casted to... DependencyObject!!! Why is that?! I don't known. I even tried to extend the DependencyObject class (yes, it's possible despite the fact many people say it's not), but I got other exceptions. To sum up, I use this release in my small project (small page for the product I'm currently working on, I'll post about it as soon as it's finished), but it's a pity that the cool approach with views defined using data templates doesn't work so far. Highly recommended.

Sunday, November 16, 2008

CLR Hosting Samples

Recently, while digging around my disk, I found two interesting samples concerning hosting Common Language Runtime in a native process. The first one actually shows some absolute basics. It comprises 3 demos showing how to host a CLR inside a C++ console application and how to call a managed code from C++, without using COM interoperability of course. If you are interested in this topic, I recommend you read very good stuff here. Besides, if you understand polish a little, familiarize yourself with my two articles. You will find links in My Articles section on the right.

The second sample is much more advanced, and unfortunately, there is no explanation how it works... This sample, called Deadlock Detector, let's you detect and break deadlock chains that occur inside .NET application. You can grab the bits from here. How can you see the sample working? Go to debug dir, run cmd and issue the following command: DeadlockDetector.exe DedlockedWindowsApplication.exe
You will see a WinForms application with two buttons: Deadlock and Work. The work button represents a real work that an application can perform. The deadlock button, on the other hand, represents a functionality with a bug - when run, it causes a deadlock. If you run this application without the Deadlock Detector host and hit the Deadlock button, all you can do is kill the app. However, if you run it through the Deadlock Detector, the host will print info about the deadlock and automatically kill one of the threads involved in the deadlock chain. Afterwords, the application will work normally. Cool, huh?

Monday, November 10, 2008

WPF Wizard Control - Part I

Writing a custom wizard control (or simply a control of any kind) in many widely used GUI toolkits is usually a challenging task. But it turns out that doing such a control in WPF is rather easy. In this post, I'm going to explain how to create stylable, simple wizard that looks like this:

I recommend briefly examining attached source code before reading the post because the code is not short enough to be pasted on a blog. Still reading "pure" text without the code is far from being nice :)

As you probably know, WPF defines so called look less controls. This means the look and feel of the user controls is completely separated from its behavior. So firstly, I'll go through the wizard's behavior, and at the end I'll explain wizard's style in a few sentences. For now let's only assume that the wizard has navigation buttons such as Next, Previous, Finish, etc., and three places for content: wizard's header, left (side) header and of course a place for displaying main content. Note that I'm not defining yet where these pieces are going to be displayed, for now I only assume they exist somewhere.

The first decision I had to take was the class I needed to inherit from. The options were UserControl or a Control. Because I wanted to give the wizard some styling capabilities (via ComponentResourceKey) and also I wanted it to look more "professional" I decided to inherit from a Control class. There's yet another factor that actually convinced me no to using UserControl. UserControl directly inherits from a ContentControl, and the wizard itself does not have content of any kind! Wizard's content is provided by means of wizard pages and the wizard should display one page at a time. Precisely, wizard will "know" which page it should display (i.e. which page is the current one) and the wizard's template will contain ContentControl that will be databound to the main content of the current page. Of course the same approach will be used for the headers.

As you may have already guessed, single wizard page is represented by WizardPage class. Because it indeed has a content, it directly inherits from ContentControl. And because single WizardPage may provide optional header and side header, it has corresponding dependency properties of type object. Besides, this class defines some other properties like CanXXX which indicate if XXX navigation button is enabled for the page, and PageClose along with PageShow events that are raised whenever a page is closed or shown. As outlined above, the Wizard class contains a collection of WizardPage class.

The Wizard class contains two important dependency properties - ActivePage and ActivePageIndex. I hope their names are self descriptive. Not surprisingly, these two properties depend on each other, i.e. if I change ActivePageIndex, I expect ActivePage will automatically get updated, and vice versa. Moreover, I don't want to receive an error if I accidentally set inappropriate value for these properties. All this can be achieved using change and coerce callbacks. Here's the code:

  226         private static void OnActivePageIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  227         {
  228             Wizard wizard = (Wizard)d;
  229             int index = (int)e.NewValue;
  230             int oldIndex = (int)e.OldValue;
  232             if (index != -1 && index != oldIndex)
  233                 wizard.ActivePage = wizard.WizardPages[index];
  234             else if (index == -1)
  235                 wizard.ActivePage = null;
  236         }
  238         private static object CoerceActivePageIndex(DependencyObject d, object value)
  239         {
  240             Wizard wizard = (Wizard)d;
  241             int index = (int)value;
  243             if (index >= wizard.WizardPages.Count)
  244                 return wizard.WizardPages.Count - 1;
  246             if (index >= 0 && index < wizard.WizardPages.Count)
  247                 return index;
  249             if (index < 0 && wizard.WizardPages.Count > 0)
  250                 return 0;
  252             return -1;
  253         }
  255         private static void OnActivePageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  256         {
  257             Wizard wizard = (Wizard)d;
  258             WizardPage page = (WizardPage)e.NewValue;
  259             WizardPage oldPage = (WizardPage)e.OldValue;
  261             if (page != null && oldPage != page)
  262             {
  263                 // Raise event
  264                 if (oldPage != null)
  265                     oldPage.OnPageClose();
  267                 // update the index
  268                 int index = wizard.WizardPages.IndexOf(page);
  269                 wizard.ActivePageIndex = index;
  271                 // Set boundary values for navigation buttons
  272                 if (index == 0)
  273                 {
  274                     wizard.ActivePage.CanNavigatePrevious = false;
  275                     if (wizard.WizardPages.Count == 1)
  276                         wizard.ActivePage.CanNavigateNext = false;
  277                 }
  278                 else if (index == wizard.WizardPages.Count - 1)
  279                     wizard.ActivePage.CanNavigateNext = false;
  281                 // After page is up and runnig, rais event
  282                 page.OnPageShow();
  283             }
  284             else if (page == null)
  285             {
  286                 // Raise event
  287                 if (oldPage != null)
  288                     oldPage.OnPageClose();
  290                 wizard.ActivePageIndex = -1;
  291             }
  292         }
  294         private static object CoerceActivePage(DependencyObject d, object value)
  295         {
  296             Wizard wizard = (Wizard)d;
  297             WizardPage page = (WizardPage)value;
  299             int index = wizard.WizardPages.IndexOf(page);
  301             // Given page does not exist in the internal collection
  302             if (index == -1)
  303             {
  304                 if (wizard.WizardPages.Count > 0)
  305                     return wizard.WizardPages[0];
  307                 return null;
  308             }
  310             return page;
  311         }

OnActivePageChanged callback is especially important, because beside updating ActivePageIndex, it performs two other things. Firstly, it raises two events on a WizardPage class - it raises PageClose event on a page that is about to be replaced, then the page gets replaced and PageShow event is raised on a new page. And secondly, it checks if the current page is first or last in the wizard and enables or disables Next/Previous buttons accordingly.

Wizard's look & feel is defined in Themes\Generic.xaml using simple grid layout. The most important part of the control's template is how actually content of wizard's active page gets displayed in the wizard. This is accomplished using content placeholders in form of ContentControl. There is one problem with this design, however. All wizard's pages except the active one are NOT part of the logical tree as they are simply not displayed. And if the active page gets replaced, it is automatically removed from the visual tree. This implies two things. The first one is that the page's DataContext propertyis not propagated to the parent, i.e. if you put an instance of some class in the window's DataContext and you bind controls inside pages to this instance, this won't work (yes, I'm talking about PresentationModel pattern). The second thing is that you cannot bind controls with each other. Hopefully, there is an easy solution to overcome the first problem:

   45         /// 
   46         /// Returns a collection of wizard's pages.
   47         /// 
   48         public WizardPagesCollection WizardPages
   49         {
   50             get { return m_WizardPages; }
   51             set
   52             {
   53                 m_WizardPages = value;
   54                 m_WizardPages.CollectionChanged += OnWizardPagesChanged;
   55             }
   56         }

  143         private void OnWizardPagesChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  144         {
  145             // This code glues all wizard's pages to wizard's DataContext. This is done due to the fact that when pages are switched, the
  146             // page that is hidden looses its data context.
  147             foreach (var page in WizardPages)
  148             {
  149                 var binding = new Binding("DataContext") { Source = this };
  150                 BindingOperations.SetBinding(page, DataContextProperty, binding);
  151             }
  152         }

The second one is still unsolved, but because PresentationModel does work with the wizard, it's not a big deal (Update: to see how to solve these problems, see the second post).

You can download full sample from my Code Gallery.

Have fun!

Sunday, November 2, 2008

How to paste source code from Visual Studio into a blog ?

Yes, it's true. This is my first blog post on Blogger :) But if You think it's yet another dummy post, I'll surprise You. In this post, I'm going to share single thought about pasting C# source code directly from Visual Studio 2008 as it is my primary IDE. Let's begin, shall we ? ;]

OK. So U want to share code on a blog... This is actually typical case. Of course you want your code to have proper indentation, and probably syntax coloring. There's excellent piece of a free software that can help U with this - CopySourceAsHtml (CSAH) plugin for VS 2008. U can find it here. It's usage is as simple as selecting desired code, hitting right mouse click and selecting Copy As HTML... And this is how a typical C# hello world application looks like using formatting from this tool:

   20 /// 
   21     /// Some remarks :)
   22     /// 
   23     public class SomeClass
   24     {
   25         public const string MyString = "I'm a constant!";
   27         private static void Main(string[] args)
   28         {
   29             Console.WriteLine(MyString);
   30         }
   31     }

There's a one problem, however. This tool does not work with XAML or more generally with any XML code at all! Hope support will be added ASAP.