Android Addicted Apps Creator

October 15, 2009

Using a web service straight from SQL Server 2005

Filed under: Uncategorized — alinberce @ 17:33
Tags: , , ,

Interesting idea with lots of potential. I managed to consume web services from Window Forms application,from ASP.Net web pages but never directly from a SQL Server Database. I’ve researched a little and I know that it can be done and how it can be done. So let us proceed then.

To be able to use the web service from SQL Server we will use a component called CLR (Common Language Runtime) which has been included since the 2005 version. Using this component, we will create a UDF (User Defined Function) within Visual Studio which will consume the web service and which will be used in a SQL Server Table Valued Function to get the results as a SQL table. It does sound kind of tricky but I’ll try to explain it step by step.

I will make this into 2 parts, one that will take place in Visual Studio 2005 and one that will take place in SQL Server Management Studio.

Part I – The Visual Studio side of the story

Everything starts with creating a new project in Visual Studio pf SQL Server Project type. Its name will be CLRWebService

image

Before creating the UDF object we need to get a reference to the web service. The web service I will use for exemplification is offered free by www.infovalutar.ro , a website owned by a friend of mine.

To add a reference right click on the name of the solution and choose from the menu the option Add web reference. In the window that appears, it he URL field write the web service’s address

http://www.infovalutar.ro/curs.asmx?wsdl

image

In the right site of the window you can observe all the methods offered by the service and the parameters required. In The Web reference name filed will rename it to infovalutar and will press the button Add Reference

After the window closes you can see in Solution explorer that the reference has been added.

image

Now we can create the UDF object. Add a new item to the solution:

image

As I said before, the template used will be User-Defined Function and it will have the name GetCursuriValutare. The main scope of this function will be to access the getall(Date) method of the web service and get the exchange rate for the date sent as parameter.

Before writing any code we must analyze and see what exactly this function must achieve:

  • it must be read only, since it only shows data
  • it will have a method to populate the data (GetCursuriValutare_FillRow)
  • will return a table that will contain the fields returned by web service’s method

Let’ start by defining the functions attributes using SQLFunction command

[SqlFunction(
       DataAccess = DataAccessKind.Read,
       FillRowMethodName = "GetCursuriValutare_FillRow",
       TableDefinition =   "IDCurrency NVARCHAR(10), " +
                           "Value float  "
       )
   ]

We have a method that returns the final result

public static IEnumerable GetCursuriValutare()
    {
        DateTime dt = DateTime.Now.AddDays(-1);
        return new Curs().getall(dt).Rows;
    }

Here we take care of the DateTime parameter requested. This method returns an object Of type IEnumerable for the web service Curs() contained in the namespace infovalutar.

The GetCursuriValutare_FillRow methods populates UDF the record set from the object Curs returned as IEnumerable

public static void GetCursuriValutare_FillRow(
        object CursObj,
        out SqlString IDCurrency,
        out SqlDouble Value
        )
    {
        DataRow r = (DataRow)CursObj;
        IDCurrency = new SqlString(r[0].ToString());
        Value = new SqlDouble(Convert.ToDouble(r[1]));
    }

The final piece of code should look like this:

using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Xml;
using CLRWebService.infovalutar;

public partial class UserDefinedFunctions
{
    [SqlFunction(
       DataAccess = DataAccessKind.Read,
       FillRowMethodName = "GetCursuriValutare_FillRow",
       TableDefinition =
      "IDCurrency NVARCHAR(10), " +
       "Value float  "
       )
   ]

    public static IEnumerable GetCursuriValutare()
    {
        DateTime dt = DateTime.Now.AddDays(-1);
        return new Curs().getall(dt).Rows;
    }

    public static void GetCursuriValutare_FillRow(
        object CursObj,
        out SqlString IDCurrency,
        out SqlDouble Value
        )
    {
        DataRow r = (DataRow)CursObj;
        IDCurrency = new SqlString(r[0].ToString());
        Value = new SqlDouble(Convert.ToDouble(r[1]));
    }

};

Because we managed to finalize the function we must now obtain the required dll files to be able to use it in SQL Server. A very important thing to mention here, the XML object aren’t automatically serialized so we need to do this our self. This is being done using a tool included in Visual Studio: sgen.

To automate things a little bit will use Solution’s Build events to tale care of serialization. Right click on Solution name -> Properties ->Build Events and in the Command line for the Post-build event add the following

“C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\sgen.exe” /force “$(TargetPath)”

image

From the Build menu choose Build CLRWebService and pray that there will be no errors.

If everything went well, in the project folder should be 3 generated files:

image

If you have those files it means that the UDF creation is done. Go grab a snack and come back for the second step of this tutorial.

Step II – What does SQL Server has to say

To make things easyier will make a new folder on C:\ named CLRGetCurs in which we will copy 2 of the 3 generated files: CLRWebService.dll and CLRWebService.XmlSerializers.dll

Open SQL Server Management Studio, connect to the desired SQL Server instance and open a new Query. For exemplification we will use the AdventureWorks database.

use AdventureWorks
go
-- allows access from exterior
ALTER DATABASE AdventureWorks SET TRUSTWORTHY ON;
GO

--checking that the objects exists before creation
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'GetCursuriValutareWS')
   DROP FUNCTION GetProductWS
go
IF EXISTS (SELECT [name] FROM sys.assemblies WHERE [name] = N'XmlSerializers')
    DROP ASSEMBLY [XmlSerializers]
IF EXISTS (SELECT name FROM sys.assemblies WHERE name = 'GetCusturiValutareCLR')
   DROP ASSEMBLY GetProductCLR
GO

-- create assemblys for the  2 dll files from folder CLRGetCurs
Create ASSEMBLY GetCusturiValutareCLR FROM 'C:\CLRGetCurs\CLRWebService.dll'
WITH PERMISSION_SET = External_Access

CREATE ASSEMBLY [XmlSerializers] from
'C:\CLRGetCurs\CLRWebService.XmlSerializers.dll'
WITH permission_set = SAFE
GO

We are one step closer to the finish line. We only need to create a Table Valued Function that makes use of the external UDF function:

CREATE FUNCTION GetCursuriValutareWS() 

RETURNS TABLE (
IDCurrency NVARCHAR(10),
Value FLOAT
   )
AS EXTERNAL NAME GetCusturiValutareCLR.UserDefinedFunctions.[GetCursuriValutare]
GO

And now, let us enjoy the results of our work:

image

Impressive, I was really amassed to see the web service result in the SQL Server Management Studio. It makes you feel you can do anything you want. I wonder if these UDF functions can be used to alter the content of the database ? Hmm… I’ll check and see.

A good source of information for this article I’ve found on www.databasejournal.com.

I hope you enjoyed this short tutorial. Until next time, Happy coding.

P.S. I know I’ve started a series of tutorials regarding n-tier concepts. Now I left this aside and I started learning about DDD (Domain Driven Design) and how these concepts can be implemented using C# techniques. Still layers but in a more effective “proven” way and a more documented concept.  I would like to THANK Emanuel for guidance and for pointing me into the right direction. There will be some interesting tutorials so stay put.

For the romanian version of the tutorial check this address http://www.infovalutar.ro/howto/sqlserver.aspx

Advertisements

September 18, 2009

How to make a business rule in BLL and use it wisely

Filed under: WPF — alinberce @ 21:08
Tags: , , , , ,

Based on previous tutorial we will try to make a business rule for user name. The simplest rule possible is that the Username must not be empty. We must be able to check for this in the BLL and, in case of error, display a message in UI. Sounds simple ? Now let’s make it.

In this case it’s a simple rule, but just imagine that for our method GetUser(…) we can have many validations (Username must be at least 5 characters, Password can’t be blank… etc) so, after checks are being done a list of errors could appear, this means we must have possibility to store all errors and make them available to the UI. For achieving this purpose we’ll create a new Class called BusinessRule in which we will store some information regarding the failed checks:

public class BusinessRule
    {
        private String _RuleType;
        public String RuleType
        {
            get { return _RuleType; }
            set { _RuleType = value;}
        }

        private String _Message;
        public String Message
        {
            get { return _Message; }
            set { _Message = value; }
        }

        private String _Property;
        public String Property
        {
            get { return _Property; }
            set { _Property = value; }
        }

        public BusinessRule()
        {
        }

        public BusinessRule(String _RuleType, String _Message, String _Property)
        {
            RuleType = _RuleType;
            Message = _Message;
            Property = _Property;
        }
    } 

We could have different type of checks which will return different type of messages: Warnings, Errors or Info. This will be stored in RuleType. The message is pretty self explanatory, the message displayed on rule fail. The property will store the property name that failed the checks (Username in our case) and will help the UI to focus the problematic controls.

Now let’s get back to the userBo class and make some changes to use the BusinessRule class.

public List<BusinessRule> BrokenRules = new List<BusinessRule>();  

        public User GetUser(string UserName)
        {

            if (string.IsNullOrEmpty(UserName))
            {
                BrokenRules.Add(new BusinessRule("Error","User name cannot be empty","UserName"));
                BrokenRules.Add(new BusinessRule("Warning", "You have 3 more retries", "UserName"));
                BrokenRules.Add(new BusinessRule("Info", "User name has a minimum length of 5 characters", "UserName"));
            }

            if (BrokenRules.Count.Equals(0))
            {
               UserDO usrDO = new UserDO();
               return  usrDO.GetUser(UserName);
            }
            else
                return null;
        }

If check fails, all the errors will be added to the BrokenRules list. This list is Public, so it can be used by the UI.

Making the UI use the BrokenRules list

Imagine that a list of BrokenRules will be available for all classes in UserBO, so we’ll need to make a window and provide a datasource to it programatically. The layout of the window will be as follow:

image

<Window x:Class="NoiaUI.Message"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Message" Height="300" Width="300" WindowStartupLocation="CenterScreen">
    <Window.Resources>
        <CollectionViewSource x:Key="MessagesSource"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Text="Atentie" FontSize="14" Margin="5"/>
        <ListBox Name="lstMessages"  ItemsSource="{Binding Source={StaticResource MessagesSource}}" Grid.Row="1">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image Margin="5"  Source="{Binding Path=Image}" Stretch="Fill" Width="16" Height="16"/>
                        <TextBlock Margin="5" Text="{Binding Path=Message}"/>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ListBox>
        <Button Name="btnContinue" Grid.Row="2" MaxWidth="100" MaxHeight="25" Click="btnContinue_Click" Margin="4">Continue</Button>

    </Grid>
</Window>

We declare a CollectionViewSource as a Window Resource that will be the ItemsSource of the listbox. Here you can see something interesting, in the ItemTemplates I added a StackPanel in which we have 2 controls, a Image and a Textbloc. To make the window better looking, will add some icons for RuleTypes, it’s much nicer to see a Error icon instead of the text “Error”.

In the cs file we take care of everything

public partial class Message : Window
    {
        public Message()
        {
            InitializeComponent();
        }

        private class MessageFormat
        {
            public BitmapImage Image { get; set; }
            public string Message { get; set; }
            public string Property { get; set; }
        }

        public void SetMessagesSource(List<BusinessRule> lstMessages)
        {
            List<MessageFormat> finalMessage = new List<MessageFormat>();
            DirectoryInfo ImageDir = new DirectoryInfo(@"..\..\Images");

            foreach (BusinessRule br in lstMessages)
            {
                FileInfo fi = new FileInfo(@"..\..\Images\"+br.RuleType+".png");
                Uri uri = new Uri(fi.FullName);
                finalMessage.Add(new MessageFormat { Image = new BitmapImage(uri), Message=br.Message, Property=br.Property });
            }

            CollectionViewSource MessagesSource = (CollectionViewSource)this.Resources["MessagesSource"];
            MessagesSource.Source = finalMessage;

        }

        private void btnContinue_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
    }

A class, MessageFormat is used to store the ruleType as BitmapImage. A public methon, SetMessageSource, is used to read the image files, add the entire content to the finalMessage list and set the CollectionViewSource source. In this way, we instantiate the windows, call the SetMessageSource method and then display it on the screen.

Let’s see how to use it in real life enviroment. In the Login window, btnLogin click event will look like this:

private void btnLogin_Click(object sender, RoutedEventArgs e)
        {

            UserBO usrbo = new UserBO();
            User usr = usrbo.GetUser(txtUserName.Text);

            if (usr == null)
            {
                if (usrbo.BrokenRules.Count.Equals(0))
                {
                    MessageBox.Show("Wrong username");
                }
                else
                {
                    Message msg = new Message();
                    msg.SetMessagesSource(usrbo.BrokenRules);
                    msg.ShowDialog();
                }
            }
            else
                MessageBox.Show("Wellcome " + txtUserName.Text);
        }

Run it, don’t type anything, just press the login button. The window should look like this:

image

The app must not allow to continue until the BrokenRules list is empty. Furthermore, based on property field in list, we could set focus to controls that failed checks. I hope you enjoyed this tutorial. Until next time, Happy coding.

And of course  the video, this time in HD 😉

August 31, 2009

SQLtoCS Class Generator – an SQL Table to C Sharp class generator.

Filed under: WPF — alinberce @ 18:47
Tags: , ,

Well, I’m back and I’m bad… as someone used to say. I’m really not that bad, but, for sure I know more. Because I’ve started to work on a new project for a customer of mine I felt the need to develop a tool to help me do stuff faster. One big pressure came from the fact that I develop this app in my free time which isn’t really that much. So while eating, sleeping, rebuilding house, staying with my wife, waiting for our unborn little girl, I managed to crop some lines of code. I also finalized reading a WPF book from start till last page. And man… I must say it again: WPF Rocks!!! Tananana it rocks.

What does this app do you ask: in nontechnical words, it does what I hate do do (repeated  work) 🙂 in technical words it creates two classes for an Sql Server table. I’ve always been a fan of nTier architecture and the way WPF and C# works it this concept pleases me. And in big lines, this program does the business layer (Business objects) of a nTier architecture. Or at least, this is what I like to think it does.

Here is a screenshot:

Capture

The classes are generated in a few seconds. Just enter the credentials for the SQL Server or SQL CE connection, select Database and the table and press the Generate button. There are a few customizable parameters: class name, collection name and the namespace. The cs classes are saved in the exe’s directory.

The program detects what keys (PK and FK) are contained by the table and generates methods based on them.There are a lot of tools that do this on the net.  I’ve tested quite a few. There was always something bothering me and this is the main reason I started doing one for myself.

Please take your time and test it. If you have suggestions, please sent it to me.

UPDATE: The application can be downloaded from here: SqlToCs

A short, bad quality Youtube presentation:

Ah,  and about that PayPal button.. hehe… Donations are never required but they are more than welcomed.

June 5, 2009

WPF Localization – Do you speak English ? Sprechen Sie Deutsch? 你讲中文?

Filed under: WPF — alinberce @ 19:59
Tags: , , ,

    By using localization, you allow controls in a application to change their content (particularly text labels and images) according to user preferences or according to current culture settings of the Windows operating system. In WPF unit of localization is in the XAML file (the compiled BAML resource). This means that for every language supported by the application you will have a BAML resource.

Let’s begin by creating a new WPF Application named WpfLocalization. In it we will add two buttons and a text block each with it’s English text. The Windows1.xaml file will look like this:

<Window x:Class="WpfLocalization.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <Button  Margin="30" Name="button1">Save</Button>
        <Button  Margin="30" Name="button2">Cancel</Button>
        <TextBlock Margin="30">Item saved succesfully</TextBlock>
    </StackPanel>
</Window>

As you can notice, I’ve already started to create the English version of the application.

First thing we need to do is add localization support for our project. To do this, just open the WpfLocalization.csproj file and add the following line

<UICulture>en-US</UICulture>

in the <PropertyGroup> section of the file. Reload the project and let’s continue. We need also to set the NeutralResourceLanguage assembly attribute. The simplest way to do this is open the AssemblyInfo.cs file in the Properties Folder and uncomment the following line:

[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]

By setting this we tell the CRL loader how to find the correct resource DLL for the selected culture. If it can’t find the indicated culture he will use the neutral one.Run the application and go to the Debug folder. In there we have the exe file and a folder en-US which contains the US culture resource DLL.

All the controls that need to be localized must have the Uid attribute. This means that we must modify in our .xaml files, all the controls. Pretty awful stuff to do manually. But we are lucky, Microsoft thought of this and helps us by offering the msbuild tool that will do the job automatically for us.

Start a command prompt and go to the following path: C:\Windows\Microsoft.net\Framwork\v3.5 in here is the msbuild.exe which we will use.

Run the following command:

msbuild /t:updateuit C:\WpfLocalization\WpfLocalization.csproj

The result should look like this:

image

And if you reload the project, in window1.xaml the stack panel is updated and it should have the Uid attribute for it’s elements.

<StackPanel x:Uid="StackPanel_1">
        <Button x:Uid="button1" Margin="30" Name="button1">Save</Button>
        <Button x:Uid="button2" Margin="30" Name="button2">Cancel</Button>
        <TextBlock x:Uid="TextBlock_1" Margin="30"> The item was saved succesfully</TextBlock>
    </StackPanel>

If you think that this was hard to do, wait to see what’s coming. Now we must extract the resources as a .csv file and translate the text as wanted. To do this we will use the Locbaml.exe tool. Copy the LocBaml.exe to bin\Debug\en-US folder of the project. Also copy the executable file WpfLocalized.exe to the same folder. Open a command prompt and go to the en-US folder and run the following command.

Locbaml /parse WPFLocalization.resources.dll /out:translate.csv

The result of this command will be the creation of a translate.csv file. Open this file and replace the English words with the German translation:

  • Save -> Sichern
  • Cancel -> Abbrechen
  • The item was saved succesfully! -> Posten erfolgreich gespeichert

After the translation is done, we need to generate the localized satellite DLL file and install it. Create a sister directory to en-US and name it de-DE. To do this we need to use the Locbaml file again. Open a command window and navigate to the folder where translate.csv is. Run the command:

Locbaml /generate /trans:translate.csv /out:..\de-DE /cul:de-DE WpfLocalization.resources.dll

We managed to complete the localization of the code. The result isn’t visible yet. Because we don’t want to change the Windows region settings to see the localized version, we will programmatically make the switch in the app.xaml.cs file.

public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            //string sCulture = "de-DE";
            string sCulture = "en-US";
            System.Threading.Thread.CurrentThread.CurrentUICulture =
                new System.Globalization.CultureInfo(sCulture);
            System.Threading.Thread.CurrentThread.CurrentCulture =
                new System.Globalization.CultureInfo(sCulture);
            base.OnStartup(e);
        }    
    }

Final result is encouraging :)  Happy coding.

image image

The video lesson: (sorry about the video, it’s quality is absolutely horrible, I uploaded at high quality but it looks really rubish)

 

May 28, 2009

WPF – the almighty Button Tooltip

Filed under: Uncategorized — alinberce @ 20:28
Tags: , ,

A very brief tutorial about making a Button Tooltip that contains not just text, but images too. WFP Rocks!

May 27, 2009

WPF Lesson One – Layouts (The beginning)

Filed under: Uncategorized — alinberce @ 16:25
Tags: , ,

(as usual, videos are at the bottom of the post)

Hello. I decided to make a turn on my learning curve, so I decided to switch to WPF – XAML… Funny name it sounds like “zama”, a word that means some sort of food in my native language.

Because I’m a beginner, I will start, of course, with the basics.

What I can say about WPF is that it represents a pretty dramatic change in the way I approach user interfaces. In first version of .NET controls where fixed using hard coded coordinates (Top, Left). So… not a really "changeable" interface. The only things that saved the day were anchoring and docking properties. In the second .NET
we had FlowLayoutPanel and TableLayoutPanel. By using this, the interface became more flexible and more web-like.
WPF introduces a new layout system in which coordinates are placed on a second place and flow layouts becomes the winner. This gives us the possibility to create flexible interfaces which aren’t that dependent on resolution or size.

The main idea beneath the WPF window is that it can contain only one element. First thing that comes to my mind is: How I’ll do all the complex interfaces I think of if the window can hold only one element. Answer is simple: just place a container in the windows… and add all the elements in that container.

The  WPF layout containers are panels and can be:

  • StackPanel – Places elements in a horizontal or vertical stack.
  • WrapPanel – Places elements in a series of wrapped lines.
  • DockPanel – Aligns elements against an entire edge of the container.
  • Grid – Arranges elements in rows and columns according to an invisible table.

All this new names and properties may sound wired to you… it did sounded wired when I first read about them, but we’ll take it step by step and in the end, everything will be clear as daylight.

Let’s begin by creating a new Project of type Wpf Application, named WPFLesson1. By default Visual Studio creates a new window called Window1.xaml. Because the Grid panel is the almighty one, it is added by default.

 

<Window x:Class="WPFLesson1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        
    </Grid>
</Window>

The StackPanel – For the beginning I will start with the StackPanel. Delete the <Grid></Grid> and add a StackPanel with one label and three buttons in it.

<Window x:Class="WPFLesson1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <Label>Hello... This is StackPanel Label</Label>
        <Button>First Button </Button>
        <Button>Second Button </Button>
        <Button>Third Button </Button>
    </StackPanel>
</Window>

Run the project and it should look like this:

1

Without adding any code, the label and buttons were arranged from the top to the bottom. All these elements are stretched to full width of the panel, and their height is automatically set to be enough to display the text inside them.

If I want items to be displayed Horizontally, then all I must do is modify the <StackPanel> to this:

 <StackPanel Orientation="Horizontal">

Run the project and see if it looks like this:

2

As can be seen, the Height of element’s height are stretched to panel’s weight, and their width is set to accommodate the text.

Let’s revert on how was before and try to make some alignment. We’ll make the label on the Left, First button on the center and last button on the right. To do this, we’ll use the HorizontalAlignament of the elements.

<StackPanel>
        <Label HorizontalAlignment="Left">Hello... This is StackPanel Label</Label>
        <Button HorizontalAlignment="Center">First Button </Button>
        <Button>Second Button </Button>
        <Button HorizontalAlignment="Right">Third Button </Button>
    </StackPanel>

3

Well, well, well as you can see the result is as intended, but important thing: no more stretch for the elements we aligned.

There are two more important things for me to tell you: margins and Minimum/Maximum sizes. If I want some space between a control and the controls around it, I use the Margin property. Setting explicit sizes for a control in WFP is not really recommended, but setting a range of size for it, is recommended.

 <StackPanel>
        <Label Margin="5" MaxWidth="400" HorizontalAlignment="Left">Hello... This is StackPanel Label</Label>
        <Button Margin="5" MaxWidth="400" HorizontalAlignment="Center">First Button </Button>
        <Button Margin="5" MaxWidth="200" >Second Button </Button>
        <Button Margin="5" MaxWidth="400" HorizontalAlignment="Right">Third Button </Button>
    </StackPanel>

4

The WrapPanel – arranges controls in the available space, one line or column at a time.

<WrapPanel>
        <Label Margin="5" MaxWidth="400" HorizontalAlignment="Left">Hello... This is StackPanel Label</Label>
        <Button Margin="5" MaxWidth="400" HorizontalAlignment="Center">First Button </Button>
        <Button Margin="5" MaxWidth="200" >Second Button </Button>
        <Button Margin="5" MaxWidth="400" HorizontalAlignment="Right">Third Button </Button>
    </WrapPanel>

5

Resize it to see how the WrapPanel does it’s job.

The DockPanel – do you want controls stretched on one side of a panel ? Don’t worry, DockPanel is here.

    <DockPanel>
        <Label DockPanel.Dock="Top" Margin="5" MaxWidth="400" HorizontalAlignment="Left">Hello... This is StackPanel Label</Label>
        <Button DockPanel.Dock="Left" Margin="5" MaxWidth="400" HorizontalAlignment="Center">First Button </Button>
        <Button DockPanel.Dock="Bottom" Margin="5" MaxWidth="200" >Second Button </Button>
        <Button DockPanel.Dock="Right" Margin="5" MaxWidth="400" HorizontalAlignment="Right">Third Button </Button>
    </DockPanel>

6

The Grid – the grid panel separates elements into a grid of rows and columns. With it we can do pretty much all that we can do with previous explained panels. Using this panel implies two steps: first chose the numbers of rows and columns, second assign each element to the appropriate row and column. Grid lines are invisible, but I will enable it for our lesson.

<Grid ShowGridLines="True">
           <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label  Grid.Row="0" Grid.Column="0" Margin="5" MaxWidth="400" HorizontalAlignment="Left">Hello... </Label>
            <Button  Grid.Row="1" Grid.Column="0" Margin="5" MaxWidth="400" HorizontalAlignment="Center">First Button </Button>
            <Button   Grid.Row="0" Grid.Column="3" Margin="5" MaxWidth="200" >Second Button </Button>
            <Button  Grid.Row="1" Grid.Column="1" Margin="5" MaxWidth="400" HorizontalAlignment="Right">Third Button </Button>
    </Grid>

7

As you may think already, these panels won’t be used alone. To develop ergonomic user friendly interfaces these panels must be combined. So we’ll start to use our knowledge to create something useful: a classic text edit window. Not only that we will have it done, but we’ll do it in 2 ways. And I’m sure that there are some more ways to do it. (As someone said: each thing in a program can be done in at least 4-5 different ways).

First attempt: using a DockPanel and a StockPanel

The windows will have the following format: a big textbox at the top and two buttons at the bottom for Save and Cancel. For the buttons we’ll use a StockPanel. This panel will be put inside of a DockPanel at the bottom side of it. Set LastChildFill to true, so you can use the rest of the windows to fill in another content.

    <DockPanel LastChildFill="True">
        <StackPanel DockPanel.Dock="Bottom" HorizontalAlignment="Center" Orientation="Horizontal">
            <Button Margin="8,8,4,8" Padding="3">Save</Button>
            <Button Margin="4,8,8,8" Padding="3">Cancel</Button>
        </StackPanel>
        <TextBox DockPanel.Dock="Top" Margin="10">Write some text in here.</TextBox>
    </DockPanel>

Run the project and see what happens

8

Was this easy or what ? But what if I tell you that this can be done even easier ?  What ?  You don’t believe me ?

Second attempt: the mighty Grid Panel

    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBox Margin="10" Grid.Row="0">Write some text in here.</TextBox>
        <StackPanel Grid.Row="1" HorizontalAlignment="Center" Orientation="Horizontal">
            <Button Margin="10,10,2,10" Padding="3">Save</Button>
            <Button Margin="2,10,10,10" Padding="3">Cancel</Button>
        </StackPanel>
    </Grid>

9

Pretty easy, huh ?  I told you so…

And we came to the end of a very nice lesson. We’ve learn about WPF layouts what they are and how we use them. A very good book for learning is :”Pro WPF in C# 2008” by Matthew MacDonald

I really hope that I’ve been helpful. So, until next time: Happy coding!

Because of the restricted length in youtube videos of 10 minutes, today you’ll have 2 parts

May 14, 2009

Custom Combo Box with colors and text – Part II

Filed under: Windows forms — alinberce @ 17:39
Tags: ,

(Warning: for bored people, who are too tired to read all this bla bla, just go to the end of the post for the video)

         Yesterday we did pretty cool things with the ComboBox control. Today we’ll enhance it a little bit more. The previous solution works great if you don’t need many ComboBoxes. But if you do, than it would be uncomfortable to write DrawItem methods for each one. To get rid of this issue, we’ll create a custom ComboBox which will do the job for us. Sounds nice ?

Add a new class to the project and name it CboColors.cs. This will contain a custom ComboBox control with it’s overridden methods, in order to spare us from doing the same things over and over again.

Here is the code:

using System.Drawing;
using System.Windows.Forms;

namespace qPlanner.UserControls
{
    public partial class cboColored : ComboBox
    {
        protected override void OnCreateControl()
        {
            base.OnCreateControl();

            this.DrawMode = DrawMode.OwnerDrawFixed;
            this.DropDownStyle = ComboBoxStyle.DropDownList;
            this.MaxDropDownItems = 11;
        }

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            base.OnDrawItem(e);

            e.DrawBackground();
            if (e.Index >= 0)
            {
                object current = this.Items[e.Index];
                object currentValue = current.GetType().GetProperty(this.ValueMember).GetGetMethod().Invoke(current, null);
                object currentDisplay = current.GetType().GetProperty(this.DisplayMember).GetValue(current, null);
                Color clr = currentValue is Color ? (Color)currentValue : Color.Black;
                using (Brush brush = new SolidBrush(clr))
                {
                    e.Graphics.FillRectangle(brush, e.Bounds.X + 1, e.Bounds.Y + 1, 13, 13);
                }
                using (Brush brush = new SolidBrush(e.ForeColor))
                {
                    e.Graphics.DrawString(currentDisplay.ToString(), e.Font, brush, e.Bounds.Left + 16, e.Bounds.Top);
                }
                e.DrawFocusRectangle();
            }

        }
    }
}

The changes from the previous part are not very obvious, but they make the difference. As you can see, now I won’t take the information about Color and Name from the ComboBox’s DataSource but from the current drawn item itself.

Build the solution. Now get back to the Form1 in Design mode. In the ToolBox there should be a brand new item named cboColored.

image

Drag this item into the form and rename it to cboColored. The only thing that remains to do is give this custom ComboBox a DataSource.

private void GetPriorityList()
        {
            lstPriority = PriorityCollections.GetPriorities();
            cboPriority.DataSource = lstPriority;
            cboPriority.ValueMember = "Color";
            cboPriority.DisplayMember = "Name";
          //added for the custom combo
            cboColored.DataSource = lstPriority;
            cboColored.ValueMember = "Color";
            cboColored.DisplayMember = "Name";

        }       

Run the project and it should look like this:

image

Wow. It works. It’s that simple, just add a DataSource, ValueMember and DisplayMember and you’re done. Happy coding.

May 10, 2009

Combo Box with colors and text – Part I

Filed under: Windows forms — alinberce @ 17:46
Tags: ,

First things, first: A BIG BIG THANK YOU, for my friend Claudiu which always gives me the right solution when I’m stuck and has the nonhuman patience to teach me . Respect.

I’ve seen in some applications pretty nice combo boxes that contained beside the text some graphics too. So, I thought, why not make one myself? It can’t be that hard… no? Well, not very hard but, very interesting. In order to achieve this, I will take a simple example from the qPlanner application: each task has a certain priority, let’s say “High”, “Medium”, “Low”. Each priority type has a specific color: Red, Orange, Green. The combo box control will have to be able to display a small square with the color and the text of the priority.

Get ready, take a big breath, and let’s try to do it together.

First of all let’s create a new project, of type Window forms application. I will name it ColoredCombo. Open Form1 and add to it a ComboBox, name it cboPriority and set it’s DropDownStyle to DropDownList and it’s DrawMode to OwnerDrawFixed.

image

Things become a little more interesting. The DataSource of the cboPriority will be a List<> containing the PriorityObjects we choose to. This may sound weird now, but in a few lines of code you will get the idea. Add a new item to the project of type class and name it PriorityObject.cs. Add the following code into it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace ColoredCombo
{
    public class PriorityObject
    {
        public string Name { get; set; }
        public Color Color { get; set; }

        public PriorityObject(string name, Color color)
        {
            Name = name;
            Color = color;
        }
    }
}

Each object of type PriorityObject will have a Name  and a Color. To have a list of these object we will add a new class to the project, named PriorityCollections.cs

using System.Collections.Generic;
using System.Drawing;

namespace ColoredCombo
{
    public class PriorityCollections
    {
        public static List<PriorityObject> GetPriorities() 
        {
            List<PriorityObject> lst = new List<PriorityObject>();
            
            lst.Add(new PriorityObject( "High", Color.Red));
            lst.Add(new PriorityObject( "Medium", Color.Orange));
            lst.Add(new PriorityObject( "Low", Color.YellowGreen));
            
            return lst;
        }
    }
}

This being done, we will proceed in completing the combo box master piece. Go back to Form1, right click on cboPriority and choose Properties. On Properties window press the Events button (you know… the little one with the lightning on it) and add a new event for Draw item (cboPriority_DrawItem).

In this event, using GDI+, we will draw each combo box item manually. Each item will be composed from a small square which will be filled with the right color and a text contain the priority. But what is GDI+ ? You may ask… Microsoft Windows GDI+ is a class-based API that enables applications to use graphics and formatted text on both the video display and the printer. Applications based on the Microsoft Win32 API do not access graphics hardware directly. Instead, GDI+ interacts with device drivers on behalf of applications.

First we need to give a DataSource to the ComboBox. For this I will make a simple method and call it in form load.

 private void GetPriorityList()
        {
            lstPriority = PriorityCollections.GetPriorities();
            cboPriority.DataSource = lstPriority;
            cboPriority.ValueMember = "Color";
            cboPriority.DisplayMember = "Name";
        }    

where  lstPriority is declared as a private variable;

Run the project and see the results. It should look like this:

image Nothing spectacular until now. Prepare yourself to do some drawing. In the cboPriority_DrawItem created before add the next code:

private void cboPriority_DrawItem(object sender, DrawItemEventArgs e)
        {
            e.DrawBackground();
            if (e.Index >= 0)
            {
                Color clr = lstPriority[e.Index].Color;
                using (Brush brush = new SolidBrush(clr))
                {
                    e.Graphics.FillRectangle(brush, e.Bounds.X, e.Bounds.Y, 14, 14);
                }

                using (Brush brush = new SolidBrush(e.ForeColor))
                {
                    e.Graphics.DrawString(lstPriority[e.Index].Name.ToString(), e.Font, brush, e.Bounds.Left + 16, e.Bounds.Top);
                }
                e.DrawFocusRectangle();

            }
        }

As you can seem in clr variable I take the current drawn item Color property. Creating a solid brush with this color, I draw a rectangle of 14×14 pixels. I also create a string with current item Name property and I draw it beside the created rectangle (e.bounds.Left+16).

Run the project and prepare yourself to be amazed:

image