07 janeiro 2010

Building a Shell Application using Caliburn

I'm planing to use Caliburn for a large project this year, and the first thing I need to do is to implement the infrastructure for the WPF applications. My favorite user interface model is, for years now, the Shell model, which allows one active screen at a time plus some secondary popup screens and eventual dialog windows. Caliburn is highly customizable and one of the pieces you can choose to replace is its DefaultWindowManager, which is the object responsible for displaying views in a dialog or popup screen. I will use this post to show my implementation of the Shell model using Caliburn, which I think can also be used as a Caliburn Getting Started. I will assume you have already read the Caliburn documentation and is familiar with the MVVM pattern and the Shell Application Model.

Step 1: Create the project: The first thing to do is to open VS2008 and create a WPFApplication. I'm assuming you have caliburn installed, so add references to the following assemblies:
  • Caliburn.Core;
  • Caliburn.PresentationFramework;
  • Microsoft.Practices.ServiceLocation.
These assemblies are available in the Bin folder of the Caliburn instalation.

Step 2: Create the infrastructure types: The second step is to create three types that will contain all the infrastructure code for using Caliburn as a Shell application. WindowManager Caliburn uses a DefaultWindowManager to show dialog and popup windows, but it does not allow us to customize these windows' properties. In order to do so, will extend the DefaultWindowManager and configure Caliburn to use our WindowManager instead of the default.
public class WindowManager : DefaultWindowManager, IWindowManager {
    public WindowManager(IViewStrategy viewStrategy, IBinder binder)
        : base(viewStrategy, binder) {
    }

    //Display a view in a dialog (modal) window
    public new bool? ShowDialog(object rootModel, 
                                object context, 
                                Action<ISubordinate, Action> handleShutdownModel) {
        var window = base.CreateWindow(rootModel, context, handleShutdownModel);
        window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
        window.WindowStyle = WindowStyle.ToolWindow;
        window.Title = ((IPresenter)rootModel).DisplayName;
        return window.ShowDialog();
     }

    //Display a view in a popup (non-modal) window
    public new void Show(object rootModel, 
                         object context, 
                         Action<ISubordinate, Action> handleShutdownModel) {
        var window = base.CreateWindow(rootModel, context, handleShutdownModel);
        window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
        window.Title = ((IPresenter)rootModel).DisplayName;
        window.Show();
    }
}
This window manager reintroduces the Show and ShowDialog methods, which we can use to customize the popup and dialog windows. BaseShellViewModel Caliburn allows us to inform a view model to be used as an application shell. Let's define this view model:
public abstract class BaseShellViewModel : MultiPresenterManager {
    protected IServiceLocator Locator { get; private set; }

    public BaseShellViewModel(IServiceLocator locator) {
        this.Locator = locator;
    }
    public void Show<T>() where T : IPresenter {
        this.ShutdownCurrent();
        this.Open(Locator.GetInstance<T>());
    }
    public void ShowDialog<T>() where T : IPresenter {
        Locator.GetInstance<IWindowManager>().ShowDialog(
            Locator.GetInstance<T>()
        );
    }
    public void Popup<T>() where T : IPresenter {
        Locator.GetInstance<IWindowManager>().Show(
            Locator.GetInstance<T>()
        );
    }
}
Look that this base shell view model publish methods to open views in the shell, in a popup window and as a dialog. BaseApplication One of the ways to use Caliburn is to make your WPF application extend the CaliburnApplication class. Here we'll create a base application class, which extends CaliburnApplication, and make our WPF applications extend this base application, keeping all Caliburn configuration within this base class.
public abstract class BaseApplication<TShellViewModel> : CaliburnApplication 
    where TShellViewModel : BaseShellViewModel {

    protected override object CreateRootModel() {
        var binder = Container.GetInstance<DefaultBinder>();
        binder.EnableBindingConventions();
        binder.EnableMessageConventions();
        return Container.GetInstance<TShellViewModel>();
    }

    protected override void ConfigurePresentationFramework(
        PresentationFrameworkModule module
    ) {
        module.UsingWindowManager<WindowManager>();
    }
}
As we need to inform Caliburn which is our shell view model, I introduced a generic parameter for that. Note also the call to UsingWindowManager, which informs Caliburn that it must use our WindowManager instead of the default one.

Step 3: Setup the application Now that we have the infrastructure code, let's configure our WPF application to use it. The ShellViewModel The first thing to do is to create our ShellViewModel. Create a folder named ViewModels in the project and a class named ShellViewModel inside it. Make this class extend our BaseShellViewModel.
public class ShellViewModel : BaseShellViewModel {
    public ShellViewModel(IServiceLocator locator)
        :base(locator) {
    }
}
The ShellView Now that we have a ShellViewModel, let's create our ShellView. Create a folder named Views in the project and a WPF Window called ShellView inside it. Change this view according to the code below. This will make our view act as a dock station for the application views.
<window minheight="768" minwidth="1024" title="Symbion" 
    windowstartuplocation="CenterScreen" windowstate="Maximized" 
    x:class="CaliburnDemo.Views.ShellView" 
    xmlns:cal="http://www.caliburnproject.org" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <dockpanel>
        <dockpanel background="Gray" name="DockViewPanel">
            <itemscontrol itemssource="{Binding Presenters}">
                <itemscontrol.itemtemplate>
                    <datatemplate>
                        <contentcontrol cal:view.model="{Binding}"></contentcontrol> 
                    </datatemplate>
                </itemscontrol.itemtemplate>
            </itemscontrol>
        </dockpanel>
    </dockpanel>
</window>
App.xaml and App.xaml.cs The last thing to do is to make our application to extend our BaseApplication.
public partial class BaseApplication : BaseApplication<ShellViewModel> { 
}

public partial class App : BaseApplication {
}
As WPF does not support generics, we need to workaround it by creating a concrete base class to our application, fixing our ShellViewModel as the generic parameter. Now change the app markut to reflect the above change.
<this:baseapplication x:class="CaliburnDemo.App" 
    xmlns:this="clr-namespace:CaliburnDemo"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <application.resources>

    </application.resources>
</this:baseapplication>
Everthing set up!

Step 4: Create a test view Now that we have everything set up, let's create a test view and run our application. Create a view model, in the ViewModels folder, named TestViewModel. Make it implement the IPresenter interface and be sure to remove the exceptions from the generated interface implementation methods. Give it a DisplayName too. Create a UserControl, in the Views folder, named TestView and paint it some color to mark it. Add a method to our ShellViewModel as follows:
public void ShowViews() {
    Show<TestViewModel>();
    Popup<TestViewModel>();
    ShowDialog<TestViewModel>();
}
Add a button to the ShellView as follows:
<button cal:message.attach="ShowViews" height="50" name="ShowViews" width="178">Show Views</button>
Run it and have fun!

09 novembro 2009

What is Software Architecture

When people question me about my main interests in software development, or about exactly what I do in my job, I use to say I like to act as a Software Architect, which is a pompous title, and most people don't take it seriously, by finding it is just a job title and not a real role in the development process. Probably most of you who know from inside a software development process know what is the job of a Software Architect, but some ones might not. This week I found this great reference, which explains exactly what I feel to be a Software Architecture, as well as what is the job of a Software Architect. So, that is the answer for what I like to do for living.

09 setembro 2009

Scott Hanselman's 2009 Ultimate Developer and Power Users Tool List for Windows

As I said in my first post, this blog will also serve as a kind of delicious for me, so you will see me posting many references to other blogs' posts. One reference I couldn't let pass is this post of Scott Hanselman's blog: An amazing list of tools for Windows developers and power users. For sure they worth a try.

28 agosto 2009

My Own Code Snippet Formatter

Some weeks ago when I needed to publish code snippets here for the first time, I looked for plug-ins for Windows Live Writer and found two good alternatives, from which I decided to use the simpler one.

After that, I started to build my own code snippet formatter. Only for C# snippets, and using JavaScript, that is far from what I'm used to write.

After some weeks working on it in my spare time, it's finally usable.

It is available here.

But, if you want a true syntax highlight tool, try this one.

Enjoy!

23 agosto 2009

How to define the priority of a bug

Some time ago a tester told me we didn't have a formal criteria to define the priority of a bug, and that the few criteria he know were not easy to use. After some thoughts, we decided to work together to define a criteria that would be good for us.

The result was a compact definition for five priority levels for bug fixes:

  • Minimal: A bug is said to be of minimal priority when its occurrence:
    • Do not cause any damage to the stored data;
    • Do not interrupt the usage of the software;
    • Is hard to be observed* and does not bother the user.
  • Low: A bug is said to be of low priority when its occurrence:
    • Do not cause any damage to the stored data;
    • Do not interrupt the usage of the software;
    • Is easy to be observed but does not bother the user.
  • Normal: A bug is said to be of normal priority when its occurrence:
    • Do not cause any damage to the stored data;
    • Do not interrupt the usage of the software;
    • Is easy to be observed and bothers the user.
  • High: A bug is said to be of high priority when its occurrence:
    • Causes some damage to the stored data or interrupts the usage of the software;
    • Is hard to be observed.
  • Maximal: A bug is said to be of maximal priority when its occurrence:
    • Causes some damage to the stored data or interrupts the usage of the software;
    • Is easy to be observed.

* A bug is said to be of hard observation when it only occurs in extraordinary use cases, it means, in situations that are rarely provoked by the user.

15 agosto 2009

Parsing a Delphi DFM File

Some time ago a friend asked me to help him to parse a DFM file into .NET objects. We tried to find something in the web that does the job but no success, so I accepted the challenge and wrote the parser he needed.

If for some reason you need a DFM parser, or just for curiosity, you can download it from here.

Here is an example of how to use it:

class Program { 
    static void Main(string[] args) { 
        var fileName = @"c:\Unit1.dfm"; 
        var dfmParser = new DfmParser(fileName); 
        dfmParser.ReadFile(); 
        foreach (var node in dfmParser.Nodes) { 
            if (node.PropertyType == "string") { 
                node.PropertyValue = "'Hello World!';"; 
            } 
        } 
        dfmParser.SaveFile(); 
    } 
}

The example above loads this file:

object Form1: TForm1
  Left = 0
  Top = 0
  object Label1: TLabel
    Left = 8
    Top = 40
    Caption = 'Label1';
  end
  object Label2: TLabel
    Left = 48
    Top = 40
    Caption = 'Label1';
  end
end

And rewrites like this:

object Form1: TForm1
  Left = 0
  Top = 0
  object Label1: TLabel
    Left = 8
    Top = 40
    Caption = 'Hello World!';
  end
  object Label2: TLabel
    Left = 48
    Top = 40
    Caption = 'Hello World!';
  end
end

If you find some use for this, enjoy :)

23 julho 2009

Best Practices for Individual Contribution

Today, in his blog, Scott Hanselman is talking about how to be a good individual contributor.

Whatever it means, what he shows is a good bunch of best practices for how to be a good professional. I was surprised to see how much I agree with his points, and I only did not see me in the situations he describes for the one about twitter, cause I don’t tweet, but yet in this case, I completely agree with the general rule.

Below I’m quoting his points, but you need to see the original post, as there are many good contributions also in the comments section.

  • Consciously manage your personal brand.
    • You work here to help the company, but also yourself. No one will manage your “personal brand” except you. How are you perceived? Do you know? Take negative feedback gracefully, and implement change. Rinse, repeat.
  • Push the Limits
    • Chris Sells told me once, If you’re not getting in trouble with your boss at least twice a year, you’re likely not pushing the envelope hard enough. Two slaps a year might be the cost for 10 successes. If you’re not moving forward, well, you’re not moving forward.
  • Conserve your keystrokes.
    • When you’re emailing a single person or a reasonably sized cc: list, ask yourself, are you wasting your time? Is this a message that 10 people need to see, or 10,000? Is email where you should be spending your time. Actively be aware of the number of people you communicate with , and the relative level of influence. Is a blog post seen by 50,000 more or less valuable than a single email to your skip-level? Only you can answer, but only if you’re consciously trying to conserve your keystrokes. Your fingers DO have an expiration date; there’s a finite number of keystrokes left, use them wisely.
  • Don’t give bile a permalink.
    • While you’re on the clock, think about what you tweet and FB. It only takes one bad link to undo a year’s work. Same goes for tweeting product launches before they’ve launched.
  • Write down what you’re trying to accomplish and hang it on the wall.
    • Make T-Shirts. Tell your spouse and kids. If you’re working towards a goal, tell people. It’ll keep you honest and it’ll motivate you. Saying things out loud help make them reality.
  • Manage Up
    • Are your commitments aligned with your boss and your bosses boss? Do you have visibility into their commitments? If not, ask for them. Make sure your accomplishments are making yourself, and your boss, look good.
  • Have a System to Manage Information Flow
    • If you’ve got 1000 emails in your Inbox, it’s not an Inbox. It’s a pile of crap. Have a system, any system, to triage your work. Any item in your inbox should be processed: Do it, drop it, defer it, delegate it. There are no other actions to take. Are you effectively managing your information flow? Try scheduling time for email on your calendar.

Remember, don’t lose the original post.