Confessions of a .NET Developer!

A rounded edged button in WPF

In this post, i shall create a cool rounded button using WPF.

For changing the appearance of a control, you need to create a control template which overrides the default template of that control.
It will look something like this(Click on the link below):

Style Button
Cool isn’t it?!!

Ohkay lets get started!
Here is the XAML for the style :

So we have defined a style, specified a key for it and also specified the TargetType which in our case is a Button.
Next a setter is given with “Template” as the property and its value as “ControlTemplate” to restyle our button.
You can see inside our ControlTemplate is a Border. It means that we are actually replacing the Button with a Border and giving other properties. You can place your own shape or control inside the template like an ellipse to create a circular button.
Next take a look at how i have specified the Background for the Border.

Here is the XAML for the Brush :

LinearGraidentBrush

Brush

You can learn about LinearGradientBrush from the following MSDN :
http://msdn.microsoft.com/en-us/library/ms754083.aspx
http://wpf.2000things.com/tag/lineargradientbrush/

Difference is that my way of specifying GradientStop. In my way you can give RGB colors directly. RGB can be easily calculated using MS Paint->Edit Colors->Define Custom Colors

Next is the ContentPresenter which holds the content of the Button. Test by removing it and you can see the Text Hello has disappeared!
You can replace the ContentPresenter with a TextBlock or even replace it with a ViewBox.
But textblock can only place text and no images inside the button. Taking the case of the ViewBox, well ContentPresenter is more lightweight compared to the former.

Now you can apply the style to a button like this :

Staticresource

After applying you can see the Style has changed!
But wait! Before you start rejoicing, you can click it, the click event will work but the clicking effect of zoom won’t come.

For that we can use Transformations here, i shall apply a RenderTransform here to get a feel of clicking it.

Here is the complete XAML :

Complete

Complete XAML

What i have done here is, adding a Trigger. Its quite clear what it says, When the IsMouseOver property of the Button is True, then it will apply the ScaleTransform. The zooming effect is brought using the ScaleX and ScaleY factors.
Don’t forget to add the Setter “RenderTransformOrigin” to 0.5,0.5. This will ensure that the zooming starts form the middle of the Button. Change the values and play with it! You can see the difference.

That’s it!
It was fun making this button!
In case of any doubt or suggestions, leave a comment and i shall try my best to rectify it!
Thanks for reading!
Have a nice day!

February 22, 2011 Posted by | WPF | 5 Comments

Create a Custom Panel in WPF – Part II

So in our previous post we explained the basics of creating our own custom panel. In this post i will implement those basics.

So i will create an AlternatePanel. Here the items of the Panel will be shown in an alternate way vertically.
Like, first item will be aligned to the left, the second will be aligned to the right, the third to left etc.

Here is the class :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace MakeNewPanel
{
    public class AlternatePanel:Panel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            Double cHeight = 0.0;
            Double cWidth = 0.0;
            Double secondMax = 0.0;
          
            foreach (UIElement child in this.Children)
            {
                child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                if (child.DesiredSize.Width > cWidth)
                {
                    secondMax = cWidth;
                    cWidth = child.DesiredSize.Width;
                }            
                cHeight += child.DesiredSize.Height;
            }

            if (Double.IsPositiveInfinity(availableSize.Width))
            {
                return new Size(cWidth + secondMax, cHeight);
            }
            else
            {
                return new Size(availableSize.Width, cHeight);
            }
        }

Above is the code for MeasureOverride. What i am trying to achieve here is that, i will calculate the total height of the children and calculate the sum of maximum and the second maximum width of the child. This total height and width will be used to define the size of the panel.

protected override Size ArrangeOverride(Size finalSize)
        {
            Boolean isRight = false;

            Double x = 0.0;
            Double y = 0.0;

            foreach (UIElement child in this.Children)
            {
                if (isRight == false)
                {
                    Rect rec = new Rect(new Point(0, y+1), child.DesiredSize);
                    child.Arrange(rec);
                    isRight = true;
                }
                else
                {
                    Rect rec = new Rect(new Point(x, y+1), child.DesiredSize);
                    child.Arrange(rec);
                    isRight = false;
                }
                y += child.DesiredSize.Height;    //Positions y points down from the top
                x = child.DesiredSize.Width;       //Gets the previous child's width
            }
            return finalSize;
        }
    }
}

This shows the ArrangeOverride function which was a bit tricky. The first child will be aligned to left and the next child to the right. The right alignment is given to the child from the left + previous child’s width.

The positioning of the Rectangles is done by providing the Point(x,y) with respect to the Panel and the Child.DesiredSize as the width and height of the Child.
I have added an extra 1 to the y point of child to give a small space between the top and below children while arranging the items.

Like shown below:
Arrange items

Here is the XAML that i am using :
XAML :

As you can see, i have used a ListBox with my custom panel as ItemsPanel. You can add images instead of items. But this will not be fast and UI may hang because its not a VirtualizingPanel. For loading thousands of images, you should use a VirtualizingStackPanel. I am currently trying to create a custom panel called VirtualizingWrapPanel but its very difficult to implement one. Microsoft has not provided a VirtualizingWrapPanel. Here is an excellent blog which implements VirtualizingPanel written way back in early 2006!
VirtualizingTilePanel

That’s It!!
This was a simple example of creating a custom panel. Likewise you can create your own panel which can like arrange the items diagonally or change my code to arrange the items alternatively in a horizontal fashion.

Here is the project :
(I will try to upload the project file once i know how to do it!! :P)

Just remove the extension “.doc” .

Here are a few links which have their own implementation of Panel.

An Animated WrapPanel

Hope you enjoyed it! Happy coding! 🙂

February 21, 2011 Posted by | WPF | 1 Comment

Create a Custom Panel in WPF – Part I

In this post I shall explain how to create a custom panel to suit your needs using WPF.

We have a good set of panels or layouts available like Grid, StackPanel, WrapPanel, DockPanel, Canvas etc.
But sometimes you might feel that its not enough! You may feel like adding your own functionalities such as
arranging your items diagonally. For this, we need to make our own custom panel.

First lets go to the basics:
The Panel class is the class which handles the layout of its children.
The items of the Panel are basically arranged in the layout as rectangles. Basically each child in our panel is an instance of a UIElement. The Panel class stores the children in a UIElementCollection which is a collection class that only has the instances of the UIElements.

To create a custom panel, we need to create a class which inherits the Panel class. Add a namespace
using Systems.Windows.Controls; to inherit Panel. We shall be overriding two functions namely, MeasureOverride and ArrangeOverride which will help us to measure and arrange the children of our Panel.

How it works?
The parent(layout) and its children basically “talk” to each other. First the parent asks its children, how much space do you want? This step is done in the MeasureOverride function.
MeasureOverride:
We shall implement the function like this:

protected override Size MeasureOverride(Size availableSize) 
           {
              foreach(UIElement child in this.Children)
              {
                   child.Measure(new Size(...,...));
              }
              return new Size(...,...);
           }                 

It really isn’t that simple!! There is more code to be done but this is just the important things to make note
of.
The parameter availableSize is the size that is available to you to layout the children. For example, if you have a Panel of size 100*100 with margin set as 10,10,10,10, then the available size will be 80*80.
But if you have a scrollviewer around the panel, then the availableSize will be with the width and height as Infinity.

Next we are here enumerating over the Children collection and measuring the Size of each Child.
In the measure function you can pass the size that you want considering the want, even passing Infinity or availableSize itself. After this call, the desired size of the child is set which will be useful while arranging the children in the layout.
Note: If you pass Infinity as size in the measure call, the desired size will be its original size that it takes.

Next you can return the overall size of the panel by adding the width and height of the children, but it totally depends on you. Its a bad practice if one returns availableSize from MeasureOverride. For example, if you have a ScrollViewer around your panel or if you plan to use the panel in a ListBox with ScrollViewer.HorizontalScrollBarVisibility and VerticalScrollBarVisibility as “Auto”, then the availableSize will be Infinity and hence you will get an exception like this :
Layout measurement override of element ‘MakeNewPanel.AlternatePanel’ should not return PositiveInfinity as its DesiredSize, even if Infinity is passed in as available size.

ArrangeOverride:
It looks something like this :

Protected Override Size ArrangeOverride(Size finalSize)
          {
              foreach(UIElement child in this.Children)
              {
                    child.Arrange(new Rect(...,...));    //there are 6 overloads
              }
              return new Size(...,...);
          }

The parameter finalSize will never be of Size Infinity, and is usually the size of the Panel.
The important part is in the for loop. As i mentioned earlier, each child will be arranged as a rectangle, so basically we will be creating the rectangles and position or rather arrange them in the layout according to the way we want.
The return parameter is of least concern and you can pass finalSize as the parameter.

This is the end of Part I. In the next part I will show you how to implement the above basics to create our own custom panel! Part 2

Thank you for reading!

February 21, 2011 Posted by | C Sharp, WPF | Leave a comment