Saturday, December 18, 2010

Silverlight Control template for content controls

One of the strongest things about silverlight is that it gives you the complete control over your application visual appearance.

Unlike win-forms controls or asp.net controls, the look of the control and it's behavior are two different things.

For example, a button is Clickable but it doesn't have to look like a button, it can be a bunch of circles or whatever you like.

So how you change the control appearance?

It depends on what you want to do… let's say we want to create a round Button.

We can try to create a Border element around it, like this:

 <Border BorderThickness="5" CornerRadius="15" BorderBrush="Black">
            <Button Content="Click me!" />
 </Border>

Now we have a round border around our button, but it's still a regular button:

image

So we have to get into the actual control and change it's complete appearance , and how? by changing the control's template.

The control template defines the visual-tree of the control.

In order to create our round button I created a style that defines the visual tree of the button as an ellipse.

 <Style TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <Ellipse  Opacity="0.8">
                            <Ellipse.Fill>
                                <SolidColorBrush x:Name="Brush" Color="LightGreen"/>
                            </Ellipse.Fill>
                        </Ellipse>
                        <ContentPresenter Margin="{TemplateBinding Padding}" VerticalAlignment="Center"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

So what did I do here?

I've changes the button to be a grid that has an ellipse and something to holds it's content (Content presenter) on top of the ellipse.

When someone that use's this template define a content for the button(it can be some text,another control etc..), the content will be presented on the ellipse.

The button with text content:

image

The button with 2 ellipses as a content:

image

Note: this is still a button! when you click on it, it fires the "Click" event!

Another important thing to mention is that when you create your own template for the control, you actually run-over the default template, and by doing so, loses some things like the management of visual states of the control, which means you need to create them yourself (like I did in this post).

The code with the visual states(simple mouse-over) will look like this:

<Style TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal">
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="Brush"    
                                                        Storyboard.TargetProperty="Color" Duration="0:0:0"/>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="Brush" 
                                                                    Storyboard.TargetProperty="Color"
                                                                    To="Pink"  Duration="0:0:0"/>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Ellipse  Opacity="0.8">
                            <Ellipse.Fill>
                                <SolidColorBrush x:Name="Brush" Color="LightGreen"/>
                            </Ellipse.Fill>
                        </Ellipse>
                        <ContentPresenter Margin="{TemplateBinding Padding}" VerticalAlignment="Center"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

Last thing, this post is for content controls, and why is that? because we use Content Presenter as a placeholder for the content.

But what if it's a TextBox that does not have content? well,this is for the next post… סמיילי

Friday, December 3, 2010

Comparing Lambda expression in unit tests

A couple of days ago i had an interesting debug session at work.

Some guy try to test a method that gets a lambda expression as a parameter. he tried to create a stub for the method but had some difficulties.

The code looked like this:

public class MyClass
{
    public int MyMethod(Expression<Func<int,bool>> expression)
    {
      //Do something
    }
}

....

[Test]
public void MyMethodTest()
{
    var myStub = MockRepository.GenerateStub<MyClass>();
    myStub.Stub(x=> x.MyMethod(y=>y==3)).Return(10);
    myStub.MyMethod(y=>y==3);
}

The problem was that when the code tried to run MyMethod he didn't recognized the stub because the lambda expressions was not equal… why? because the default "Equals" compare references.

To solve the problem we started by searching for something that will help us identify in a unique way lambda expressions.

If you have read my post about expression trees you will know that an Expression object has the following properties:

1)Body - the expression
2)Parameters
3)NodeType - LambdaExpression
4)Type - Func<int, bool>

The only thing that identify the expression is the body, which is an expression itself. so the solution was not here.

The Only solution we thought of is to implement the Equals ourselves(as we let re-sharper do it for us in our  own entities), but we didn't know how. we searched for some Constraints and we found the following solution which was exactly what we needed:

myStub.Stub(x=>x.MyMethod(Arg<Expression<Func<in,bool>>>.Matches(Property.AllPropertiesMatch(y=>y==3))).Return(10);

The constraint Property.AllPropertiesMatch does the trick and compare the expression by it's properties.

Note: the expression y=>y==3 does not equals to x=>x==3!

*This post is dedicated to Jones,that gave me an interesting afternoon of debugging and coding.