Windows Forms (WinForms)

WinForms is a mature .NET Framework technology for creating Windows graphical user interfaces. It provides many of the standard Windows controls and extends upon them.

In this section we will see how to create a simple WinForms application, featuring only a label, textbox and a button. We will use this application to explain the basics of the WinForms technology. Later, we will create a more complex WinForms application with a DataGridView and a database connection.

Simple Windows Forms Application

To create a new Windows Forms Application, start Visual Studio, go to “File” menu and select “New” > “Project”. A new dialog window appears. Select “Windows Forms Application”, type the name of the application and click “OK”:

New Project

The new project gets created. You should see an empty Windows Form in the middle of the screen and a “Toolbox” window on your left:

Toolbox and Form

The form itself is a user control. You can influence its appearance and behavior by setting its properties. You should be able to see the “Properties” window on the right side of the screen:

Properties

The Properties window shows all the properties of the currently selected control. You should be able to see a property named Text. By changing its value to “Simple Windows Forms Application” you should be able to change the title of the form:

Setting Text on Form

Now, let us add a new control – button. Find the “Button” control in the Toolbox and drag it to the Form. Using the Properties window once again, change its Text property to “Click me”. You should get the following result:

Click Me Button

When the user clicks our new button, we want something to happen. Let us say we want to show a message box window saying “Hello!”. We will achieve this by using events.

Apart from properties, controls also have events. These events are triggered by user actions. You can “listen” to these events by subscribing to them. When the event occurs (fires), you can react to it (handle it) with your own method. This method is also called an event handler. You can create and subscribe your event handlers through the “Properties” window. Make sure, however, that you first switch it to “Events” mode first:

Simple Windows Forms Application - layout

We will subscribe to a “Click” event. Find the “Click” event in the Properties window and double click it. This should create a new event handler and open a new window for you. This window is one of the code files behind your form:

Click Event

In the new “button1_Click” event handler, type the following command to open up a new “MessageBox” window:

MessageBox.Show("Hi There!");

We are now ready to run the application. Hit F5 and test the new button on the form. As soon as you click the button, a new window should pop up with a message saying “Hi There!”:

Hi There Message Box

We have already seen one of the code files associated with the form. This is a code file where we typed the command to open up the message box. There is also a second code file for every form. This code file is generated automatically based on what we set in the Properties window or in the form designer directly.

You can see the automatically generated file by clicking the “Form1.Designer” file in the Solution Explorer. The file may be hidden under the “Form1.cs” file item itself – if so, expand it. When examining the code file, you can see changes we have performed so far. For example take a look at the new button that we have added:

// 
// button1
// 
this.button1.Location = new System.Drawing.Point(97, 98);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "Click me";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);

You can see the Text property being set as well as a new Click event handler being registered. Again, this file is auto-generated by Visual Studio. Never change this file manually! Always find a way to change it through the designer.

Let us go back to the form designer and add a couple more controls. Drag a new “Label” and a “TextBox” onto the form. With help of the Properties window, change the “Text” property of the new label to “Name:” and organize the controls in a row by dragging it on the form:

Simple Windows Forms Application - layout

Now, go to the click event handler we created before and change its contents to:

MessageBox.Show("Hi " + textBox1.Text + "!");

Once you have finished all of these modifications, run the application again. Type your name in the textbox and click the button. The application will greet you by name.

These are the most fundamental operations you need to understand in order to create a very basic Windows Form. There is one more thing to remember, however:

Always make sure you name your new controls! Do not keep their default names as we have done in this example. You can rename any control by changing its Name property in the Properties window. Always use a notation, where the name starts with something descriptive and ends with the type of the control. A camel case should be used. Our button, for example, could be named “clickMeButton”. Our textbox could be called “nameTextBox”.

By the way, have you noticed the good old Program.cs file in the Solution Explorer? If you take a look inside, you will see a Main method – just like the one we used to have in our console applications. It is still the main entry point for the application! All it does now is creating a new Application object with a new instance of our form. The Application object will live until the user closes the window. In the meantime, it just sits there and waits for user to interact with it through events.

The complete Simple Windows Forms Application is available for download.

Advanced Windows Forms Application

The more advanced Widows Forms Application touches on additional topics, such as the usage of ComboBox, Grid, anchoring, databinding and asynchronous operations.

From a functional perspective the application lets the user select a product category through a combobox. Once a category is selected a grid below the combobox gets populated with a list of products that belong to the selected category. The user can the select a particular product from the grid to populate yet another grid with a list of orders that contain the selected product. This is how the application is going to look like:

Advanced Windows Forms Application - running

Preparations

Let us start again by creating a new Windows Form Application. Once you have the application created, add the Entity Framework NuGet package and create a model based on MyCSharpDotNet database. Everything is done exactly the same way as we did it in an EntityFramework console application.

Data classes with asynchronous support

Once you have the Entity Framework set up, let us create a new folder “DataObjects” inside the project add a couple of classes we will use.

CategoryDO.cs:

class CategoryDO
{
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
 
    public static async Task<List<CategoryDO>> GetCategoriesAsync()
    {
        using (MyCSharpDotNetEntities context =
            new MyCSharpDotNetEntities())
        {
            return await context.Categories
                .Select(x => new CategoryDO()
                {
                    CategoryID = x.CategoryID,
                    CategoryName = x.CategoryName
                })
                .ToListAsync();
        }
    }
}

ProductDO.cs:

class ProductDO
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
 
    public static async Task<List<ProductDO>> GetProductsAsync(
        int categoryID)
    {
        using (MyCSharpDotNetEntities context =
            new MyCSharpDotNetEntities())
        {
            // Simulate longer loading
            await Task.Delay(1000);
 
            return await context.Products
                .Where(x => x.CategoryID == categoryID)
                .Select(x => new ProductDO()
                {
                    ProductID = x.ProductID,
                    ProductName = x.ProductName
                })
                .ToListAsync();
        }
    }
}

ProductOrderDO.cs:

class ProductOrderDO
{
    public int OrderID { get; set; }
    public string CompanyName { get; set; }
    public int Quantity { get; set; }
 
    public static async Task<List<ProductOrderDO>> GetProductOrdersAsync(
        int productID)
    {
        using (MyCSharpDotNetEntities context =
            new MyCSharpDotNetEntities())
        {
            // Simulate longer loading
            await Task.Delay(1000);
 
            return await context.Order_Details
                .Where(x => x.Product.ProductID == productID)
                .GroupBy(x => new
                {
                    OrderID = x.OrderID,
                    x.Order.Customer.CompanyName
                })
                .Select(x => new ProductOrderDO()
                {
                    OrderID = x.Key.OrderID,
                    CompanyName = x.Key.CompanyName,
                    Quantity = x.Sum(y => y.Quantity)
                })
                .ToListAsync();
        }
    }
}

Notice that all of these classes use Entity Framework asynchronous methods. Every asynchronous method we create needs to use the “async” keyword and return a generic “Task” object. We will see how to use these methods in our event handlers shortly.

To be able to use Entity Framework Async methods, make sure you add this using to the top of the code file: "using System.Data.Entity;".

New controls

Drag two new labels, combobox and two data grid views from the toolbox onto the form and make them look like this:

Advanced Windows Forms Application - layout

Data bindings

Once the controls are in place, we need to create binding sources for them. First, make sure you can build the project and build it (use F6 for example). Now, let us create a new project data source by clicking on the little arrow located in the top right corner of our combobox. A new dialog opens:

ComboBox Data Binding

Open the “Data Source” combobox and select “Add Project Data Source” link. In the new wizard, select “Object”, locate the “CategoryDO” object, select it and click “Finish”. Staying in the dialog window, select “CategoryName” as the Display Member and “CategoryID” as the Value Member – like this:

ComboBox Data Binding - finished

In a similar way, let us add data sources for both our grids. Use “ProductDO” object for the first grid:

Products Grid Data Binding

The second grid should use “ProductOrderDO” object:

Product Orders Grid Data Binding

You can tweak the columns appearance through the “Edit columns” link. The result should look like this:

Advanced Windows Forms Application - final layout

Set a couple of properties

First, make sure you check the initial state of the Visible property for all our controls. Only the “Category:” and “Loading, please wait…” labels should have their Visible set to true. All other controls need to be set to false.

To disable user input in the ComboBox, set its “DropDownStyle” property to “DropDownList”.

To make grids read-only, set “AllowUserToAddRows” and “AllowUserToDeleteRows” to false. Set “ReadOnly” to true. You can also tweak the “SelectionMode”, “RowHeadersVisible”, and “MultiSelect” properties.

Anchor

To make sure the controls on the form resize together with the form, experiment with the Anchor property. Every control on the form has it.

Once you anchor a control to both opposing sides, its dimensions will change when the form resizes. If you anchor the control to one side only, the control will always keep the same distance from the respective form boundary.

Event handlers

Finally, create new event handlers. Notice that all event handlers use the “async” keyword. The asynchronous methods themselves are called using the “await” keyword.

Form Load event handler to load categories when the form loads:

private async void MainForm_Load(object sender, EventArgs e)
{
    categoryDOBindingSource.DataSource =
        await CategoryDO.GetCategoriesAsync();
 
    categoriesComboBox.Visible = true;
}

ComboBox SelectedValueChanged event handler to refresh the products grid based on the ComboBox selection:

private async void categoriesComboBox_SelectedValueChanged(
    object sender,
    EventArgs e)
{
    loadingLabel.Visible = true;
    productsDataGridView.Visible = false;
    productOrdersDataGridView.Visible = false;
 
    if (categoriesComboBox.SelectedValue != null)
    {
        productDOBindingSource.DataSource =
            await ProductDO.GetProductsAsync(
                (int)categoriesComboBox.SelectedValue);
 
        productsDataGridView.Visible = true;
    }
}

Products Grid SelectionChanged event handler to reload the order details grid once a different product is selected:

private async void productsDataGridView_SelectionChanged(
    object sender,
    EventArgs e)
{
    if (productsDataGridView.SelectedRows.Count == 1)
    {
        ProductDO selectedProduct =
            (ProductDO)productsDataGridView.SelectedRows[0]
                .DataBoundItem;
 
        loadingLabel.Visible = true;
        productOrdersDataGridView.Visible = false;
 
        productOrderDOBindingSource.DataSource =
            await ProductOrderDO.GetProductOrdersAsync(
                selectedProduct.ProductID);
 
        productOrdersDataGridView.Visible = true;
        loadingLabel.Visible = false;
    }
}

All of the event handlers above use the DataSource property of XyzBindingSources. This is how you bind controls to data. To inform the user that the data is being loaded, the event handlers also keep changing the Visible property of the loading label.

You should now be ready to run the application. The full source code of the Advanced Windows Forms Application is available for download.

Continue to: Windows Presentation Foundation (WPF)

Go up to: Windows Applications


Should you have any questions or found a mistake that needs correcting, feel free to send an email to: info [at] mycsharp [dot] net


Advertisements :