Tuesday, 9 August 2011

Window loading layer

To do:

Show the user that a time consuming operation is taking place in the background, while restricting the user interface input.

Nice to have:

A generic solution that can be reused.

Solutions?

The first thing that popped into mind was to show a modal window with some sort of a loading animation and a status text. But the interface to work with, was already in a modal window, so I thought of creating a mask over the existing interface. Now I think this idea came up from the ASP.net field; I remembered the Ajax/Java Script implementation for this effect.

I thought about how it could be done, but then I decided to look a bit over the web, to see what's the most popular way of doing this. I found some articles related to using the Adorner class. But looking over the implementations, it seemed over-complicated, with too much code for what I needed. I felt it could be done simpler with much more xaml, and much less code.

So I did. I started noting down what kind of visual tree structure would be needed. Then I wrote a short class called LoaderLayer, that derives from content control, and which contains two dependency properties called IsLoadingVisible and LoadingText.

Then I defined a simple style for it, with a content which has over it a rectangle, an animation and a status label.

The content will be whatever we want to show is busy, the visibility of the loading layer will be controlled by IsLoadingVisible and the status text will be binded to LoadingText.

I must admit, I have a hard trouble designing something visual that's out of the order, like say... a loading animation. Given this fact, I got the loading animation from A Simple WPF Loading Animation . I thought this looks like a respectable and stylish animation, but you can easily change it if you'd like.

And.. after a few more lines of xaml and code later, this is the result:


The control template is what I find most important:

<ControlTemplate TargetType="{x:Type local:LoaderLayer}">
    <Grid>
        <ContentPresenter Name="Content"
                          Content="{TemplateBinding Content}"
                          />
        <Grid Visibility="{TemplateBinding IsLoadingVisible, 
                           Converter={StaticResource BoolToVisibilityConverter}}">
            
            <Rectangle Opacity="0.7" Fill="{StaticResource LoadingLayerGradient}" />
            
            <StackPanel VerticalAlignment="Center" 
                        HorizontalAlignment="Center"  
                        Focusable="False">
                <local:LoadingAnimation />
                
                <TextBlock Text="{TemplateBinding LoadingText}" 
                           FontWeight="Bold" 
                           HorizontalAlignment="Center" 
                           Margin="0, 20, 0, 0" />
            </StackPanel>
        </Grid>
    </Grid>
</ControlTemplate>


It is very straight forward. The control template has a rectangle and a stack panel right over it's content. The rectangle will act as a mask over the content, and the stack panel will contain some information for the user. Like a loading animation (could also show some percentage of how much is done, if available), and a status text.

The LoaderLayer, as I call it, can be used for an entire window, or just for some inner controls.

The demo project can be found here. Enjoy!





Saturday, 6 August 2011

MultiBinding with inner converter

To do:

Change the visibility of a control based on two boolean properties, one of which I needed to negate.

If a pseudo-code would help:

visibilityCondition = !firstBoolValue && secondBoolValue

if (visibilityCondition)
   control.Visibility = Visible
else
   control.Visibility = Collapsed

And I need to do this using bindings (because I'm a MVVM obsessed person).

Intuitive solution (also the wrong solution) :
<MultiBinding Converter="{StaticResource MultiBoolToVisibilityConverter}">
    <Binding Path="FirstBoolValue" Converter="{StaticResource InvertBoolConverter}" />
    <Binding Path="SecondBoolValue" />
</MultiBinding>

The reason why this will not work, is because the first element in the "values" parameter of MultiBoolToVisibilityConverter, will always be DependencyProperty.UnsetValue.

Now, this issue aggravated me for a while. It seems that if a inner converter is used (in our case InvertBoolConverter), then the return type of that converter should be of the same type like the target property. ( in our case Visibility).

Why this was designed like this, I have no idea. Because the value is passed fine to the top converter, if no inner converter is used.

Once I duplicated the problem in a separate project, I found this post which explained it:

Converter in other multibinding converter - input value is UnSetValue.
"I ran into the same problem, almost with the same setup of booleans, nots and "and"s... weird, how much duplicate code is written around the world."
Weird indeed sir.

Correct solution: 

a) Don't use an inner converter, and use a less generic top converter (I used this one because I got pissed off by the problem)

b) Use inner converts on both inner bindings, which return a Visibility value. And in the top converter, join the two Visibility values.

I made a small app that demonstrates this issue. You can download it here.

First words

I've been thinking of starting a blog for a few years now, basically from the first time I got hired and faced some real world programming scenarios. I used to work on ASP.net, but most of the tips and tricks I learned in those times lie dormant in my long-long-term memory, and I probably won't get to blog about them.

Instead I'll post some of my recent issues faced while working on WPF, which is what I do where I work now.

I'll start off in my next post with the latest issue I encountered, and then I'll try to work my way up the issues I noted during my ongoing learning. I'm thinking of posting mostly tips and tricks, but I may tackle some of my own implementation design choices as well.

I hope this info will help you in some way or another, and any constructive criticism you may have is always welcomed.

Have a nice reading!