Welcome to MSDN Blogs Sign in | Join | Help

Data Validation in Silverlight DataGrid

One of the features added to datagrid in Silverlight 3 Beta is Validation.  This can be done

-          Once we modify the content of a particular cell (cell validation)

-          Once we modify the content of a particular datagrid row (would like to commit changes to row) (Row Validation)

In this blog, I will try to give an intro both cell validation and row validation by using examples.

 

Cell Validation

Cell Validation can be done in two ways

-          Raising ValidationException in the property setter

-          Using ValidationAttributes and calling ValidateProperty in property setter

Cell Validation by throwing ValidationException in the property setter

In order to understand this concept, let’s consider that our datagrid is populated with a collection of Employee (with the following properties) objects  on which we want to do Cell Validation

        public String firstName;

        public String lastName;

        public int age;

        public String phone;

        public String city;

        public String state;

The validations that we want to do on these properties are

-          Firstname, lastname,  phone, city, state should not be null or empty

-          Age has to be in the range  18 to 50

We define validation rules for these properties (in the setter properties) in the following way.  The ValidationException (belonging to System.ComponentModel.DataAnnotations ) thrown is displayed as Cell Validation Error.

        public string LastName

        {

            get { return lastName; }

            set

            {

                if (value != lastName)

                {

                    if (value == null || value == string.Empty)

                    {

                        throw new ValidationException("Last Name cannot be empty");

                    }

                    lastName = value;

                    NotifyPropertyChanged("LastName");

                }

            }

        }

 cellvalidation1

        public int Age

        {

            get { return age; }

            set

            {

                if (value != age)

                {

                    if (value < 18 || value > 50 )

                    {

                        throw new ValidationException("Age should be between 18 and 50");

                    }

                    age = value;

                    NotifyPropertyChanged("Age");

                }

            }

        }

 cellvalidation2

Using Validation Attributes and calling ValidateProperty in property setter

The properties on which we want to do validation are decorated by validation attributes and we call the validationobject.validateproperty method in the setter of the property.  This tells the validator to verify on the decorated validation attribute when validateProperty is called. 

In the following code snippet,  the Required, Range and RegularExpression validation attributes are used.  The image below them shows the validation error when a validation rule is not met

        [Required]

        public string FirstName

        {

            get { return firstName; }

            set

            {

                if (value != firstName)

                {

                    Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "FirstName" });

                    firstName = value;

                    NotifyPropertyChanged("FirstName");

                }

            }

        }

 cellvalidation3

 

        [Required]

        [Range(18,50)]

        public int Age

        {

            get { return age; }

            set

            {

                if (value != age)

                {

                    Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "Age" });

                    age = value;

                    NotifyPropertyChanged("Age");

                }

            }

        }

cellvalidation4 

 

        [Required]

        [RegularExpression("\\d{3}[-]\\d{3}[-]\\d{4}")] // (e.g - "999-999-1234")

        public string Phone

        {

            get { return phone; }

            set

            {

                if (value != phone)

                {

                    Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "Phone" });

                    phone = value;

                    NotifyPropertyChanged("Phone");

                }

            }

        }

cellvalidation5 

Row Validation

In addition to doing validation when we edit a particular cell, we can perform validation when we commit a particular row or entity in the datagrid.  In order to do this,we decorate our BusinessObject class with the CustomValidation attribute that specifies the Validation class and the method that are used for validation. The row validation errors are shown at the bottom of the data grid in a error listbox.  Clicking on an error in the listbox focuses on the cell that that has validation error. Resolving the validation error dynamically removes the error from the listbox.

In addition to specifying the CustomValidation attribute on the business object, we can also use the validation attributes on the Properties to show invalid entries as Row Validation errors.

        [Required]

        public string FirstName

        {

            get { return firstName; }

            set

            {

                if (value != firstName)

                {

                    firstName = value;

                    NotifyPropertyChanged("FirstName");

                }

            }

        }

I will use the Employee class with the following properties as an example.  Note that I also show the decorated validation attributes on the properties of Employee just for demo purpose (In actuality the property definition looks similar to the above).

        [Required]

        public string FirstName

 

        [Required]

        public string LastName

 

        [Required]

        public DateTime? BirthDate

 

        [Required]

        [Range(18,50)]

        public int? Age

 

        [Required]

        public String City

 

        [Required]

        public String State

 

        [Required]

        [RegularExpression("\\d{3}[-]\\d{3}[-]\\d{4}")] // (e.g - "999-999-1234")

        public string Phone

 

Please note that we are not explicity calling the Validator.ValidateObject method or throwing ValidationExceptions in the setter for invalid values (Doing so will show cell validation errors).

The business object Employee class is decorated with the following customvalidation attributes

    [CustomValidation(typeof(EmployeeValidator), "IsValidBirthDay")]

    [CustomValidation(typeof(EmployeeValidator), "IsValidBirthDayAge")]

    public class Employee : INotifyPropertyChanged

The EmployeeValidator Class is static and we define validation methods inside it.  The class looks like below for this particular example.

    public static class EmployeeValidator

    {

        public static bool IsValidBirthDay(object employeeObject, ValidationContext context, out ValidationResult validationResult)

        {

            validationResult = null;

            Employee employee = employeeObject as Employee;

            DateTime date = employee.BirthDate.Value;

            int dateComparison = date.CompareTo(DateTime.Today);

            if (dateComparison > 1)

            {

                List<string> properties = new List<string>() { "BirthDate" };

                validationResult = new ValidationResult("Birthday cannot be further than today!", properties);

            }

 

            return !(dateComparison > 1);

        }

 

        public static bool IsValidBirthDayAge(object employeeObject, ValidationContext context, out ValidationResult validationResult)

        {

            validationResult = null;

            Employee employee = employeeObject as Employee;

            DateTime date = employee.BirthDate.Value;

            int age = DateTime.Today.Year - date.Year;

            if (age != employee.Age)

            {

                List<string> properties = new List<string>() { "Age", "BirthDate" };

                validationResult = new ValidationResult("Age does not match with Birthday! Your age should be " + age, properties);

            }

            return (age == employee.Age);

        } 

    }

If you observe the IsValidBirthDay validation method above, this checks if the propertry “Birthdate” is not further from today.     In the following code snippet

List<string> properties = new List<string>() { "BirthDate" };

                validationResult = new ValidationResult("Birthday cannot be further than today!", properties);

 

rowvalidation1

we tell the validator that in this validationmethod, we are checking the property BirthDate only.  We can check more than one property and associate them with a validation error.  This is done for example in the IsValidBirthDayAge  where the (Age,BirthDate) properties are checked for which the code which does this looks like below.    When we click on the validation error in the listbox, we toggle between the properties (Age, BirthDate).

                List<string> properties = new List<string>() { "Age", "BirthDate" };

                validationResult = new ValidationResult("Age does not match with Birthday! Your age should be " + age, properties);

rowvalidation2

As you can see, developers can use these validation features that datagrid supports to develop compeling apps.

Posted by nagasr | 7 Comments

SortDescriptions in Silverlight Datagrid

In Silverlight 3, we can specify add sortdescriptions indatagrid so that these columns are sorted initially when the datagrid loads. This can done in XAML as well as in code. The following code snippets demonstrate this behavior.

Specifying SortDescriptions in XAML

<UserControl x:Class="SortDescriptions3.MainPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

    xmlns:scm="clr-namespace:System.ComponentModel;assembly=System.Windows"

    >

    <Grid x:Name="LayoutRoot" Background="White">

        <data:DataGrid x:Name="dg">

            <data:DataGrid.SortDescriptions>               

                <scm:SortDescription PropertyName="City" Direction="Descending" />

                <scm:SortDescription PropertyName="Department" Direction="Ascending" />

            </data:DataGrid.SortDescriptions>

        </data:DataGrid>

    </Grid>

</UserControl>

In the above code, we specified the column “City” to be sorted in Descending order, and the column “Department” to be sorted in Ascending order.

Specifying SortDescriptions through code

Defining SortDescriptions on PagedCollectionView

            List<Employee> list = PopulateData(100);

            PagedCollectionView cv = new PagedCollectionView(list);

            cv.GroupDescriptions.Add(new PropertyGroupDescription("City"));

            dg.ItemsSource = cv;

            cv.SortDescriptions.Add(new SortDescription("FirstName", ListSortDirection.Ascending));

            cv.SortDescriptions.Add(new SortDescription("Department", ListSortDirection.Descending));

 

We can combine sort Descriptions as well as  GroupDescriptions (to specify column grouping) as indicated in the above code.

Defining SortDescriptions on datagrid

            List<Employee> list = PopulateData(100);

            dg.ItemsSource = list;

            dg.SortDescriptions.Add(new SortDescription("City", ListSortDirection.Descending));

            dg.SortDescriptions.Add(new SortDescription("Department", ListSortDirection.Ascending));

 

DataGridSortDescriptions 

Hooking up the code pieces together and building the application and running it will show us sorting in datagrid. 

 

Posted by nagasr | 7 Comments

Grouping in Silverlight DataGrid

Silverlight 3 Beta has got a very  good response at Mix 09 conference.  There have been some additions to datagrid since Silverlight 2 RTW.  One of them  is support for Grouping – developers can specify columns by which they want to group by. 

The Group definitions can be added through XAML as well as through Code. 

Adding Group Definitions through xaml

    <UserControl x:Class="GroupingQuickStart.MainPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

    xmlns:compmod="clr-namespace:System.Windows.Data;assembly=System.ComponentModel"

    Width="700" Height="700">

    <Grid x:Name="LayoutRoot" Background="White">

        <data:DataGrid x:Name="dg" >

            <data:DataGrid.GroupDescriptions>

                <compmod:PropertyGroupDescription PropertyName="Department" />

                <compmod:PropertyGroupDescription PropertyName="City" />

            </data:DataGrid.GroupDescriptions>

        </data:DataGrid>

    </Grid>

</UserControl>

In the above example we are grouping data on “Department” column and then by "City" column.  PropertyGroupDescription class in the System.Widows.Data namespace of System.ComponentModel assembly takes care of this.

Before, we see how we add grouping through code, we should make a note of PagedCollectionView class.  This implements the IPagedCollectionView interface and allows grouping, sorting, etc… on the data collection.  The most common way of populating data in a pagedcollectionview class is through a collection object like list that implements IEnumerable

Adding Group Descriptions through Code

We can add grouping through code in two ways

Defining group descriptions on the PagedCollectionView

            PagedCollectionView collectionView = new PagedCollectionView(PopulateData(100));

            collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Department"));

            collectionView.GroupDescriptions.Add(new PropertyGroupDescription("City"));

            dg.ItemsSource = collectionView;

 Defining group descriptions on the datagrid

            List<Employee> list = PopulateData(100);

            dg.GroupDescriptions.Add(new PropertyGroupDescription("Department"));

            dg.GroupDescriptions.Add(new PropertyGroupDescription("City"));

            dg.ItemsSource = list;

PopulateData method that I used in the above code snippet populates a list collection with Employee Class objects. The Employee class implements INotifyPropertyChanged and exposes the following properties

        public String FirstName;

        public String LastName;

        public String City;

        public String Department;

        public String State;

 datagridgrouping

Hooking up these code pieces together and building the application and running it will show us groups in datagrid. 

Posted by nagasr | 12 Comments

Silverlight Datagrid add/delete operations and ObservableCollection

In my previous post,the collection I used is a List.  So, each time I add a new item to the list, the itemsource of the datagrid need to be modified/reset to reflect the latest changes to the collection.

Using the ObservableCollection gets this functionality comes free of cost/extra code for us.  The observable collection implements InotifyCollectioChanged which notifies the underlying control whenever anything has changed in the list.

Here is the page.xaml snippet

Here is the page.xaml.cs snippet

    public partial class Page : UserControl

    {

        ObservableCollection<Customer> clist = new ObservableCollection<Customer>();

        public Page()

        {

            InitializeComponent();

            int count = 25;

            for (int i = 0; i < count; i++)

            {

                Customer cust1 = new Customer("custfname" + i, "custlastname" + i, i, "address" + i, "city" + i, "country" + i);

                clist.Add(cust1);

            }

            dg.ItemsSource = clist;

        }

 

        private void addbutton_Click(object sender, RoutedEventArgs e)

        {

            int count = clist.Count;

            Customer cust1 = new Customer("custfname" + count, "custlastname" + count, count, "address" + count, "city" + count, "country" + count);

            clist.Add(cust1);

            dg.ScrollIntoView(clist[clist.Count-1], dg.Columns[5]);

        }

 

        private void deletebutton_Click(object sender, RoutedEventArgs e)

        {

            int deleteIndex = clist.Count - 1;

            clist.RemoveAt(deleteIndex);

            dg.ScrollIntoView(clist[clist.Count - 1], dg.Columns[5]);

        }

    }}

Thus, using the ObservableCollection helps us view in datagrid without any writing extra code the changes made to the underlying collection.

More about Silverlight ObservableCollection at http://msdn.microsoft.com/en-us/library/ms668604(VS.95).aspx 

Posted by nagasr | 1 Comments

ScrollIntoView in Silverlight Datagrid

                Silverlight 2.0 Datagrid ships with a very cool function known as ScrollIntoView.  This function provides the functionality to bring into view (show) a particular row or column in the datagrid.  I was playing with adding rows to the datagrid, and everytime I add a row, the row gets added to the end of the grid, but the focus is at the top of the Datagrid.  In order to see the added row, I have to manually scroll to the bottom of the grid.  This function helps to scroll programmatically.

Lets see an example

Page.xaml is defined in this way showing a datagrid and a button

    <Grid x:Name="LayoutRoot" Background="White">

        <Grid.RowDefinitions>

            <RowDefinition Height="400"/>

            <RowDefinition Height="50"/>

        </Grid.RowDefinitions>

        <data:DataGrid x:Name="dg" Grid.Row="0">

           

        </data:DataGrid>

        <Button x:Name="button1" Content="Add" Click="button1_Click" Grid.Row="1"></Button>

    </Grid>

I am using a Customer  Class

    public class Customer

    {

        public String FirstName { get; set; }

        public String LastName { get; set; }

        public int Age { get; set; }

        public String Address { get; set; }

        public String City { get; set; }

        public String Country { get; set; }

    }

Initially, the datagrid is populated is populated with 25 customer objects.

            clist = new List<Customer>();

            int count = 25;

            for (int i = 0; i < count; i++)

            {

                Customer cust1 = new Customer("custfname" + i, "custlastname" + i, i, "address" + i, "city" + i, "country" + i);

                clist.Add(cust1);

            }

            dg.ItemsSource = clist;

In the button click event, a new Customer object is added to the list

            int count = clist.Count;

                Customer cust1 = new Customer("custfname" + i, "custlastname" + i, i, "address" + i, "city" + i, "country" + i);

            clist.Add(cust1);

            dg.ItemsSource = null;

            dg.ItemsSource = clist;

 

When the button is clicked, the row is added to the end of the datagrid.  We have to manually scroll down to the bottom of list to see the added row.

To scroll to the added row programmatically, we can use the ScrollIntoView function

dg.ScrollIntoView(clist[count], dg.Columns[5]);

The first argument is the row object that should be visible and the 2nd argument specifies the column

 

As you can see, with the scroll into view added to the button click event, we can see the added row and also the column specified is brought into view

Happy DataGrid’ing!

Posted by nagasr | 3 Comments

Master-detail app Using two Datagrids in Silverlight

Master Detail apps are one of the popular usages of a datagrid control.  The datagrid that is shipped as part of Silverlight 2.0 RTW can be used for doing this.  Coming from the client world (winforms), I wanted to try out creating a master-detail app using the SilverLight 2.0 datagrid control.  The database I chose is the Northwind database and the tables are Customer, and Orders. 

                I believe most of you know the relationship between these two tables – (A customer can have many orders).  So, what I would like to show is -> (In the first datagrid, show all the customers, in the 2nd one, show the orders belonging to the selected customer);

Here is a tutorial that uses WCF web service and LINQ to SQL to get the data from the database and allows to capture the data to be displayed in the datagrid - http://silverlight.net/learn/tutorials/sqldatagrid.aspx ; In the link, the database that is connected to is Adventureworks DB, but in my sample, I connect to the NorthWindDB.  To the dbml file, I added Customers and Orders tables.  To the IService WCF Interface, I added these methods

        [OperationContract]

        List<Customer> GetCustomers();

 

        [OperationContract]

        List<Order> GetOrdersForCustomer(string customerId);

The implementation of these methods in the WCF Service class looks like  

        public List<Customer> GetCustomers()

        {

            DataClasses1DataContext db = new DataClasses1DataContext();

            var matchingCustomers = from cust in db.Customers

                                        select cust;

            return matchingCustomers.ToList();

        }

 

        public List<Order> GetOrdersForCustomer(string customerId)

        {

            DataClasses1DataContext db = new DataClasses1DataContext();

            return db.Orders.Where(o => o.CustomerID.ToString() == customerId).ToList();

        }

Notice, that I have LINQ to SQL to retrieve data from database – the first one using LINQ query syntax and the other using lambda expressions.

In the client project, I populated the Master data grid in the same way as mentioned in the link – calling the events

            ServiceReference1.Service1Client webService = new ServiceReference1.Service1Client();

            webService.GetCustomersCompleted += new EventHandler<SilverlightApplication6.ServiceReference1.GetCustomersCompletedEventArgs>(webService_GetCustomersCompleted);

            webService.GetCustomersAsync();

In the webService_GetCustomersCompleted event, the master datagrid is populated.

        void webService_GetCustomersCompleted(object sender, SilverlightApplication6.ServiceReference1.GetCustomersCompletedEventArgs e)

        {

            theDataGrid.ItemsSource = e.Result;

        }

Populating the details datagrid.

For this, I made use of datagrid’s SelectionChanged event to populate the details table

        void theDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)

        {

 

            SilverlightApplication6.ServiceReference1.Customer customer = theDataGrid.SelectedItem as SilverlightApplication6.ServiceReference1.Customer;

            if (customer != null)

            {

                ServiceReference1.Service1Client webService = new SilverlightApplication6.ServiceReference1.Service1Client();

                webService.GetOrdersForCustomerCompleted += new EventHandler<SilverlightApplication6.ServiceReference1.GetOrdersForCustomerCompletedEventArgs>(webService_GetOrdersForCustomerCompleted);

                webService.GetOrdersForCustomerAsync(customer.CustomerID);

            }

        }

        void webService_GetOrdersForCustomerCompleted(object sender, SilverlightApplication6.ServiceReference1.GetOrdersForCustomerCompletedEventArgs e)

        {

            ordersDataGrid.ItemsSource = e.Result;

        }

The Master – Detail datagrid is now functional.  You can also select a different Item (Row) using the up-down arrows and can see the details drid change accord to the Item selected in the Master grid.

In short, the steps that we have done are

1.       Select different tables that we want to show and drop them in the dbml file

2.       Define the methods we want to implement in the WCF Service Interface file

3.       Implement the logic for getting the required data in the database in the class that implements the WCF interface

4.       Access the Webservice from the client project and populate the datagrid.

We can extend this further by showing the order_details as a master detail relation ship with Orders table.  For this, you need to

1.       Drop the order_details table to the dbml file

2.       Define the new methods WCF Service Interface

3.       Follow steps 3 and 4 above.

Posted by nagasr | 0 Comments

WebService that returns a Dataset

In this post, I describe how to create a WebService that returns a DataSet, deploying the web service, accessing the data from the webservice, and using it with winforms controls

 

Part1 : WebService Creation

 

  1. Open VS
  2. Create new ASP.net Web Service Application
  3. Open Server Explorer
  4. Add connection to “pubs” database from the Server Explorer
  5. Add a DataSet to the Solution
  6. From the Server Explorer Window, drop the authors table of the Server explorer onto the DataSet Designer
  7. Build the Solution.
  8.  Open Service1.asmx file in the designer
  9. Drop the DataSet1, authorsTableAdapter components from the toolbox onto the Service1’s designer
  10. Build the App
  11. Add the following code to the Service1 class
    1.         [WebMethod]
    2.         public DataSet1 getDS()
    3.         {
    4.             InitializeComponent();
    5.             this.authorsTableAdapter1.Fill(dataSet11.authors);
    6.             return dataSet11;
    7.         }
  12. Build the Application.

 

Part II. WebService Deployment on the server

The web service can be deployed using iis (Internet Information Server). 

The following steps have to be followed.

  1. Copy the Web service to the inetpub\wwwroot folder
  2. Type ‘inetmgr’ in the run window. Internet Information Services Window appears
  3. Browse to the folder containing the asmx file of the web service (Internet Information Services -> Computer Name -> Web Sites -> Default Web Site -> WebService1 -> WebService1)
  4. Right click on the WebService1 node and click on properties. “WebService1 Properties” dialog appear
  5. Click the “Create” button to create the application. Click “Apply” and then “ok” buttons
  6. Now WebService1 Application is created on the server.
  7. Exit iis
  8. Restart iis using the following commands
    1. aspnet_regiis –i –enable
    2. iisreset
  9. from run command, type inetmgr

 

Part III. Accessing from the Client

 

This is described in my previous blog post

http://blogs.msdn.com/nagasatish/archive/2007/10/17/adding-a-web-reference-to-a-vs-2008-windows-forms-project.aspx

 

Part IV Binding to Winforms Controls

After Doing the steps in step III, you will be able to bind winforms controls to the dataset returned from the web service.

 

In the project from step3,after building the project

  1. Drop  “Service1” from the toolbox onto the form
  2. Drop “DataSet” under the Data area from the toolbox on to the form. Select TypedDataSet with name “WindowsApplicationName.WebServerName.DataSet1”
  3. Drop a textbox onto the form. Bind to the author’s table au_id field.

In this way you can access a web service and bind to windows forms controls.

 

 

Posted by nagasr | 0 Comments

Adding a Web Reference to a VS 2008 Windows Forms Project

 

First, we will see how to add a web service in VS2005, and then we will see what changed in VS2008 orcas.

VS2005

In Visual 2005 (Whidbey), adding a Web Reference is pretty straight forward.

From Project -> Add Web Reference ..., (From Solution Explorer, Right Click on Project, From the drop down Menu, Select Add Web Reference)

Add Web Reference Dialog Comes up.

In the Text box for URL, you specify url for the Web Service.

Click Go

Then a preview of the service is shown.  You have the option of renaming the service.

Click the "Add Reference ..." Button.

Web Service is added to the solution.  You can see it under "Web References"

What Changed in Orcas

From Project -> Add Service Reference ..., (From Solution Explorer, Right Click on Project, From the drop down Menu, Select Add Service Reference)

Click Advanced Button

 

Service Reference Setting Dialog Comes up. Click "Web Reference" button here.   Web Reference dialog comes up. The rest of the steps are same as in VS2005

 

 

 

 

 

 

Posted by nagasr | 13 Comments

DataGridView in a WPF Application

 

In my previous blog, I talked about a new WindowsForms Control “ElementHost” to host WPF controls inside a WindowsForms Project.  On the contrary, “WindowsFormsHost” is used to host winforms controls inside a WPF application.  WindowsFormsHost is a very important control to get the rich capabilities of WinForms world into WPF.  WPF does not have a story yet for some of the controls like DataGridView, ToolStrips etc which have lots of uses.

In this blog, I will concentrate on getting a DataGridView getting displayed inside a WPF application.  I will make use of databinding to get data from the Customers Table of the SaveNorthWind Database.

 

The steps would be as follows

  1. Create a new WPF Application in C#
  2. Add reference to System.Windows.Forms.dll
  3. Add the “WindowsFormsHost” control from the toolbox onto the WPF window.
  4. From the Data Menu on the toolbar “Select Add New Data Source “ and connect to the Customers Table of SaveNorthWind database
    1. After successfully doing this step, you can see SaveNorthWindDataSet added to the application
  5. In the Window1.xaml.cs file, create the following function. (This basically does the plumbing work to get the data from the customers table in the form of a bindingsource object)

 

        BindingSource dataBindingPrep()

        {

            SaveNorthwindDataSet dataset = new SaveNorthwindDataSet();

            dataset.DataSetName = "SaveNorthwindDataSet";

            dataset.SchemaSerializationMode = System.Data.SchemaSerializationMode.IncludeSchema;

 

            SaveNorthwindDataSetTableAdapters.CustomersTableAdapter tableadapter = new WpfApplication1.SaveNorthwindDataSetTableAdapters.CustomersTableAdapter();

 

            BindingSource bs = new BindingSource();

            bs.DataMember = "Customers";

            bs.DataSource = dataset;

            tableadapter.ClearBeforeFill = true;

            // filling the table

            tableadapter.Fill(dataset.Customers);

 

            return bs;

        }

 

  1. Create a DataGridView control, set its datasource property
  2. set the child property of windowsformshost to the datagridview

 

        public Window1()

        {

            InitializeComponent();

           

            //adding the datagridview

            DataGridView dgv = new DataGridView();

            dgv.DataSource = dataBindingPrep();

 

            //setting the child property

            windowsFormsHost1.Child = dgv;

 

            //hooking events to DGV

            dgv.CellContentClick += new DataGridViewCellEventHandler(dgv_CellContentClick);

        }

 

when you run your app, you can see the wpf window showing the data from the customers table in a datagridview.

 

You can also try to add events to the datagridview.  In my example, I choose to show a messagebox when I click on the column1 of the DGV. I can do this by implementing the  cellcontentclick event as follows.

 

        void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)

        {

            DataGridView dgv = (DataGridView)sender;

            if (e.ColumnIndex == 0)

            {

                System.Windows.MessageBox.Show("Clicked on " + dgv.Rows[e.RowIndex].Cells[e.ColumnIndex].Value);

            }

        }

 

 

Posted by nagasr | 3 Comments

Crossbow Design Time : Using WPF Controls on WinForms Designer


 Crossbow aims to provide interoperability between windows forms and WPF.  In my previous blog http://blogs.msdn.com/nagasatish/archive/2007/07/16/crossbow-interoperability-between-windows-forms-and-wpf.aspx, i talked about a windows forms control ElementHost that is used to host the WPF control in it and display it on a windows forms app.
 Our team worked on providing a design time story for this.  The aim is to host WPF controls on a windows Forms App just like any other windows forms controls like button, datagridview etc.  In this blog, i will describe the steps involved in doing so.

They include
1) Create a Winforms Application
2) Add a User Control (WPF) item to the project.
3) Design the WPF user Control using the WPF designer. 
4) Build the App
Now there are two ways to add the WPF user control to the project. 

The first way is
a) Drop the Element Host Control form the WPF interoperability Tab on to the form
b) From the Smart tag, set the the hostedcontent to UserControl (WPF)

The other way is
a) Drop the UserControl from the toolbox on the form. When you do this, an Element Host is automatically created and its hostedcontent is set to WPF User Control


Next, I would like to hook events to the WPF User Control
We make use the HostedContentName property in achieving this.  Suppose, we have a WPF Button hosted in the element host and we want to show a message box on mouse enter event. We can do this as follows.
In the constructor of the form, we add the mouse enter event for the HostedContentName object. 
        public Form1()
        {
            InitializeComponent();
            userControl11.MouseEnter += new System.Windows.Input.MouseEventHandler(userControl11_MouseEnter);
        }


        void userControl11_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            MessageBox.Show("Mouse Entered");
        }

This is how we create wpf controls and attach events to them and host them in windows forms applications


 

Posted by nagasr | 1 Comments

Crossbow - Interoperability between Windows Forms and WPF

Windows Forms is the the technology for developing client applications on the windows platform and has been in existance for quite some time.  WPF (Windows Presentation Foundation) is the new technology for developing apps with Vista look and feel.  These two technologies are all different from each other and there needs to be some interoperability between these two technologies.  What I mean by this is, the Winforms Apps that are developed till now need to function together with WPF apps and vice versa. 

Crossbow Project is the solution for this.  Using this, a WPF control can be hosted inside a winforms project and a winforms control can be hosted within WPF App.  The assembly that supports this is WindowsFormsIntegration.dll and is shipped with .net framework 3.0.

In this blog, I will show simple examples of how to host a WPF control in a Winforms app and vice versa.

Hosting a WPF Control in a Winforms App
We use the class called ElementHost (System.Windows.Forms.Integration.ElementHost) which is a winforms control to host a WPF control say (System.Windows.Controls.Button). 
To do this, we do the following steps

1. Create a Windows Forms Application in C#
2. Add reference to WindowsFormsIntegration.dll (ElementHost Control)
3. Add reference to WindowsBase.dll, PresentationCore.dll, PresentationFramework.dll (WPF button System.Windows.Controls.Button is in presentationframework.dll, that has dependency on WindowsBase.dll, PresentationCore.dll).
4. Add the following code to Form1() Contructor after InitializeComponent()
     //Creating a new WPF Button
            System.Windows.Controls.Button wpfButton = new System.Windows.Controls.Button();
     //Setting the Content
            wpfButton.Content = "My WPF Button";
     //Create a new ElementHost
            ElementHost eh = new ElementHost();
     //Set EH's child to WPF Button
            eh.Child = wpfButton;
            eh.Location = new System.Drawing.Point(100, 100);
            //Add the EH Control to the form
            this.Controls.Add(eh);


Hosting a WindowsForms Control in WPF App
We use the class called WindowsFormsHost (System.Windows.Forms.Integration.WindowsFormsHost) which is a WPF control to host a Winforms Control say (System.Windows.Forms.Button).

To this, we do the following steps
1. Create a WPF Application in C#
2. Add reference to WindowsFormsIntegratin.dll (ElementHost Control)
3. Add reference to System.Windows.Forms.dll (System.Windows.Forms.Button control)
4. In Window1.xaml, add "Loaded" Attribute to the <Window> tag and set the "Name" attribute to the <Grid> tag shown below
<Window x:Class="WpfApplication1.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"
    Loaded="Window_Loaded">
    <Grid Name="MyGrid">
    </Grid>
</Window>

 
 Add the Loaded attribute to the Window tag will add an event "Window_Loaded" in the Window.xaml.cs file

5.  Add the following code the Window_Loaded event
            // Create a new WindowsFormsHost object  
            System.Windows.Forms.Integration.WindowsFormsHost wfh = new System.Windows.Forms.Integration.WindowsFormsHost();

            //Create a Windows Forms Button and set its content
            System.Windows.Forms.Button wfButton = new System.Windows.Forms.Button();
            wfButton.Text = "My Windows Forms Button";

           //Set the child of windowsformshost object to the windowsforms button
            wfh.Child = wfButton;
           
            //Add the windowsformshost control to the wpf window
            this.MyGrid.Children.Add(wfh);

In this way, WPF and Winforms Apps can interact with each other. 

Posted by nagasr | 2 Comments

Binding DataGridView to DLinq Object

DLinq (.net Language Integrated Query for Relational Data) is an O/R mapping infrastructure that will help manipulating relational data as objects. It also does not compramise on the ability to query.  I tried to dirty my hands playing a bit with it and use it for databinding winforms controls and posting my findings here.

I choose the well known datagridview control introduced in VS 2005. Here is how you use the DGV to get data from the Customers table of the Northwind DB in VS2005. I am using C# language.

1. Open a WinForms App in C#

2. Add a DGV to the form

3. On the Smart tag of the DGV, Click "Add Data Source"

4. Add DataSource Wizard comes up. Select "Database", Connect to the Server and select NorthWind DB, then select Customers Table.

5. Build the App. Run the App. On the running form you see contents of Customers Table in the DGV.

With DLinq, the approach is pretty similar but we use "Object" DataSource instead of selecting "Database" in the Datasource wizard.
Here are the steps

1. Create a new Winforms App in C#.

2. Add new item "Linq to SQL Classes" item to the project. Note that it creates "DataClasses.dbml" file. The codebehind file for this is "DataClasses.Designer.cs" file.

3. In the Server Explorer, create a connection to and connect to the Northwind Database.

4. Drop the Customers Table from the Server Explorer's Northwind Database onto the designer of the "dbml" file. (You can see now the designer contains the Customer Object)

5. Build the app (Very important, otherwise you will not see the Customer Obj in the next steps)

6. From the Data Menu, Select Add New Data Source

7. Select Object for choose Data Type and select Customer Object of WindowsApplication as DataSource.

8.  Drop the Customer node from the DataSource Window to the form.  CustomerBindingSource1 gets added to the form.
Your/My Expectation : Running the app will show the contents of the Customer table on the form.
Result : Running the app does not show the Customer table data on the form.

We have to bind to the DataClasses to get the data.

 

9.  Modify the form1.cs to look similar to this

    public partial class Form1 : Form
    {
        //newly added : start
        DataClasses1DataContext datacontext1;
        //newly added : end
        public Form1()
        {
            InitializeComponent();

            //newly added : start
            datacontext1 = new DataClasses1DataContext();
            customerBindingSource.DataSource = datacontext1.Customers;
            //newly added : end
        }
    }

 

10.  Now build your solution and run the solution.

You can see the form has the contents of the Customer table when Run.

Posted by nagasr | 5 Comments

WSOD in Windows Forms

Any windows forms developer might have come accross this error message while programming in Visual Studio 2005.

"One or more errors encountered while loading the designer. The errors are listed below. Some errors can be fixed by rebuilding your project, while others may require code changes."

This is called as the White Screen of Darn (WSOD) and is considered wrongly by some as an error/bug with the desinger.  It is actually an error in the user application that causes this.

Let us try to reproduce this before we learn more about this.

Open a Winforms Application in C#.
Add a button to the form.
Build the solution
Add a UserControl to the Solution.
On the UserControl add a checkbox.
Rebuild the solution
Drop the user control created from the toolbox on to the form
Delete the UserControl from the project.
Rebuild the solution.
close and open the form

You see a WSOD.

If you have followed these steps and you produced a wsod, by now you should have realized the cause of the WSOD.  It clearly indicates that it is not a bug with the designer, but the form is missing a control and so the designer fails to load the form.

How can you go about resolving the problem ?
1.  Add a new usercontrol with the same name again the project and rebuild the project. open and close the form.  the form should be shown.
Option 1 is a good one when you know what has happened that caused the wsod.  this is not the case when you are dealing with projects you got from somebody.  In that case, you have to consider option2.
2.  Comment out the lines in form1.designer.cs file which are the cause of the error.  In this case the lines which reference UserContorl1.

Posted by nagasr | 2 Comments

Problem solving using KT Matix

 

One of the goals of a Tester is to find accurate bugs and also the reason for the occurance of the bug.  By accurate bugs, what I mean is a bug that can be repro’ed.  If the bug repo’s intermittently then try to establish a pattern for the occcurance of the bug.  This boils down to the issue of root cause analysis and problem solving.  Kepner Trogoe matrix for decision making/problem solving helps to solve the issue.  We can make use of the following matrix as a template

 

 

Is conditions

(where defect appears)

Is not conditions

(where there is no defect)

Input values

 

 

Configurations

 

 

Environment

 

 

Others

 

 

 

A hypothetical example will better help to understand the problem.  Since, I am from the winforms team, I could come up with a winforms example.  Let us say I installed a 3rd party controls and add them to my toolbox in visual studio. Now when I start dragging and dropping the 3rd party controls / winforms controls from my toolbox on to the designer, VS crashes. Let us see how we can use the above matrix to come to a conclusion about the issue

 

 

Is conditions

(where defect appears)

Is not conditions

(where there is no defect)

Configuration

Installed 3rd Party Control

 

Did not Install 3rd party control

 

Input values

  • Drop a Winforms Control on the toolbox
  • Drop the 3rd party control on the toolbox
  • Drop a Winforms control on the toolbox

Environment

XP, Windows Server2003, Vista

 XP, Windows Server 2003, Vista

Others

English VS, Japanese VS

English VS, Japanese VS

 

We can easily make out from the above table that when we installed the 3rd party controls, the support for dragging and dropping controls from toolbox to designer is disturbed and we can further work on the issue.

 

 

 

 

 

 

 

Posted by nagasr | 0 Comments

Printing DataGridView contents in CSV format

  

While answering questions on the windowsforms.net regarding datagridview, i noticed that a good number of people wanted to store the contents of the datagridview in a csv (comma separated value format).

 

In this blog, i am just writing a simple solution for this.  We do this by printing each row of the Datagridview.

 

The following block of code in VB accomplishes this task. DataGridView1 has to be stored in the csv format.

 

        Dim numCols As Integer = DataGridView1.ColumnCount

        Dim numRows As Integer = DataGridView1.RowCount - 1

        Dim strDestinationFile As String = "c:\\output.txt"

 

        Using tw As TextWriter = New StreamWriter(strDestinationFile)

 

            'writing the header

            For indexCols As Integer = 0 To numCols - 1

                tw.Write(DataGridView1.Columns(indexCols).HeaderText)

                If (indexCols <> numCols - 1) Then

                    tw.Write(", ")

                End If

            Next

            tw.WriteLine()

 

            'writing the data

            For indexRows As Integer = 0 To numRows - 1

                'print all column values for a row

                For indexCols As Integer = 0 To numCols - 1

                    tw.Write(DataGridView1.Rows(indexRows).Cells(indexCols).Value)

                    If (indexCols <> numCols) Then

                        tw.Write(", ")

                    End If

                Next

                tw.WriteLine()

            Next

 

        End Using

 

Also, in the above code you can notice usage of Using block.  Not only does Using intialize the resource but also disposes it.  If I were not making use of Using block, here,  I can also accomplish this by a try catch finally block as follows.  

        Dim tw As TextWriter

        Try

            tw = New StreamWriter(strDestinationFile)

            --------

            -------

        Catch ex As Exception

 

        Finally

            tw.Close()

        End Try

 

We can see that making use of Using block makes the code look less cumbersome and this is a very good feature in .Net

 

 

Posted by nagasr | 2 Comments
More Posts Next page »
 
Page view tracker