Of course C# supports regular expressions. I happen to have learned regular expressions in my dealings with FreeBSD, shell scripting, php, and other open source work. So naturally I would want to add this as a skill as I develop in C#.
What is a Regular Expression?
This is a method in code or script to describe the format or pattern of a string. For example, look at an email address:
someuser@somedomain.tld
It is important to understand that we are not trying to compare the email string against another string, we are trying to compare the string against a pattern.
To verify the email was in the correct format using String functions, it would take dozens of different functions running one after another. However, with a regular expression, a proper email address can be verified in one single function.
So instead regular expression is a language, almost like a scripting language in itself, for defining character patterns.
Most characters represent themselves. However, some characters don’t represent themselves without escaping them with a backslash because they represent something else. Here is a table of those characters.
Expression
Meaning
*
Any number of the previous character or character group.
+
One of more of the previous character or character group.
^
Beginning of line or string.
$
End of line or string.
?
Pretty much any single character.
.
Pretty much any character, zero characters, one character, or any number of characters
[ … ]
This forms a character class expression
( … )
This forms a group of items
You should look up more regular expression rules. I don’t explain them all here. This is just to give you an idea.
Example 1 – Parameter=Value
Here is a quick example of a regular expression that matches String=String. At first you might think this is easy and you can use this expression:
.*=.*
While that might work, it is very open. And it allows for zero characters before and after the equals, which should not be allowed.
This next pattern is at least correct but still very open.
.+=.+
What if the first value is limited to only alphanumeric characters?
[a-zA-z0-9]=.+
What if the second value has to be a valid windows file path or URL? And we will make sure we cover start to finish as well.
^[0-9a-zA-Z]+=[^<>|?*\”]+$
See how the more restrictions you put in place, the more complex the expression gets?
Example 2 – The email address
The pattern of an email is as follows: (Reference: wikipedia)
1 to 64 characters before the @ sign called the local-part. Can contain characters a–z, A–Z, 0-9, ! # $ % & ‘ * + – / = ? ^ _ ` { | } ~, and . if it is not at the first or end of the local-part.
Some characters after the @ sign that have a pattern as follows called the domain.
It will always have a period “.”.
One or more character before the period.
Two to four characters after the period.
So a simple patterns of an email address should be something like these:
This one just makes sure there are characters before and after the @
.+@.+
This one makes sure the are characters before and after the @ as well as a character before and after the . in the domain.
.+@.*+\..+
This one makes sure that there is only one @ symbol.
[^@]+@[^@]+\.
This are all quick an easy examples and will not work in every instance but are usually accurate enough for casual programs.
But a comprehensive example is much more complex.
I wrote one myself that is the shortest and gets the best results of any I have found:
So let me explain the first one that I wrote as it passes my unit tests below:
The start
[\w!#$%&’*+\-/=?\^_`{|}~]+
At least one valid local-part character not including a period.
(\.[\w!#$%&’*+\-/=?\^_`{|}~]+)*
Any number (including zero) of a group that starts with a single period and has at least one valid local-part character after the period.
@
The @ character
(
Start group 1
(
Start group 2
([\-\w]+\.)+
At least one group of at least one valid word character or hyphen followed by a period
[\w]{2,4}
Any two to four valid top level domain characters.
)
End group 2
|
an OR statement
(
Start group 3
([0-9]{1,3}\.){3}[0-9]{1,3}
A regular expression for an IP Address.
)
End group 3
)
End group 1
Code for both examples
Here is code for both examples. My email regular expression is enabled and the one I found on line is commented out. To see how they work differently, just comment out mine, and uncomment the one I found online.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace RegularExpressionsTest
{
class Program
{
static void Main(string[] args)
{
// Example 1 - Parameter=value
// Match any character before and after the =
// String thePattern = @"^.+=.+$";
// Match only Upper and Lowercase letters and numbers before
// the = as a parameter name and after the equal match the
// any character that is allowed in a file's full path
//
// ^[0-9a-zA-Z]+ This is any number characters upper or lower
// case or 0 thru 9 at the string's beginning.
//
// = Matches the = character exactly
//
// [^<>|?*\"]+$ This is any character except < > | ? * "
// as they are not valid in a file path or URL
String theNameEqualsValue = @"abcd=http://";
String theParameterEqualsValuePattern = "^[0-9a-zA-Z]+=[^<>|?*\"]+$";
bool isParameterEqualsValueMatch = Regex.IsMatch(theNameEqualsValue, theParameterEqualsValuePattern);
Log(isParameterEqualsValueMatch);
// Example 2 - Email address formats
String theEmailPattern = @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*"
+ "@"
+ @"((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$";
// The string pattern from here doesn't not work in all instances.
// http://www.cambiaresearch.com/c4/bf974b23-484b-41c3-b331-0bd8121d5177/Parsing-Email-Addresses-with-Regular-Expressions.aspx
//String theEmailPattern = @"^(([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))"
// + "@"
// + @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])"
// + "|"
// + @"(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$";
Console.WriteLine("Bad emails");
foreach (String email in GetBadEmails())
{
Log(Regex.IsMatch(email, theEmailPattern));
}
Console.WriteLine("Good emails");
foreach (String email in GetGoodEmails())
{
Log(Regex.IsMatch(email, theEmailPattern));
}
}
private static void Log(bool inValue)
{
if (inValue)
{
Console.WriteLine("It matches the pattern");
}
else
{
Console.WriteLine("It doesn't match the pattern");
}
}
private static List GetBadEmails()
{
List emails = new List();
emails.Add("joe"); // should fail
emails.Add("joe@home"); // should fail
emails.Add("a@b.c"); // should fail because .c is only one character but must be 2-4 characters
emails.Add("joe-bob[at]home.com"); // should fail because [at] is not valid
emails.Add("joe@his.home.place"); // should fail because place is 5 characters but must be 2-4 characters
emails.Add("joe.@bob.com"); // should fail because there is a dot at the end of the local-part
emails.Add(".joe@bob.com"); // should fail because there is a dot at the beginning of the local-part
emails.Add("john..doe@bob.com"); // should fail because there are two dots in the local-part
emails.Add("john.doe@bob..com"); // should fail because there are two dots in the domain
emails.Add("joe<>bob@bob.come"); // should fail because <> are not valid
emails.Add("joe@his.home.com."); // should fail because it can't end with a period
emails.Add("a@10.1.100.1a"); // Should fail because of the extra character
return emails;
}
private static List GetGoodEmails()
{
List emails = new List();
emails.Add("joe@home.org");
emails.Add("joe@joebob.name");
emails.Add("joe&bob@bob.com");
emails.Add("~joe@bob.com");
emails.Add("joe$@bob.com");
emails.Add("joe+bob@bob.com");
emails.Add("o'reilly@there.com");
emails.Add("joe@home.com");
emails.Add("joe.bob@home.com");
emails.Add("joe@his.home.com");
emails.Add("a@abc.org");
emails.Add("a@192.168.0.1");
emails.Add("a@10.1.100.1");
return emails;
}
}
}
Open a request using the full destination ftp path: Ftp://Ftp.Server.tld/ or Ftp://Ftp.Server.tld/Some/Path
Configure the connection request
Call GetResponse() method to actually attempt to create the directory
Verify that it worked.
See the steps inside the source as comments:
using System;
using System.IO;
using System.Net;
namespace CreateDirectoryOnFtpServer
{
class Program
{
static void Main(string[] args)
{
CreateDirectoryOnFTP("ftp://ftp.server.tld", /*user*/"User1", /*pw*/"Passwd!", "NewDirectory");
}
static void CreateDirectoryOnFTP(String inFTPServerAndPath, String inUsername, String inPassword, String inNewDirectory)
{
// Step 1 - Open a request using the full URI, ftp://ftp.server.tld/path/file.ext
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(inFTPServerAndPath + "/" + inNewDirectory);
// Step 2 - Configure the connection request
request.Credentials = new NetworkCredential(inUsername, inPassword);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
request.Method = WebRequestMethods.Ftp.MakeDirectory;
// Step 3 - Call GetResponse() method to actually attempt to create the directory
FtpWebResponse makeDirectoryResponse = (FtpWebResponse)request.GetResponse();
}
}
}
All right, now you have created a directory on the FTP server.
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
Ok, so you don’t always want a default class template for every type of class. I have to create a bunch of classes that implement Serializable and if the class template assumed this, that would be great. However, I don’t want my default class template to assume this.
So here is what I did broken down into four simple steps.
Open or create a c# project.
Create a class file.
Add the text and the variables to replaced.
Export the item as a template.
Step 1 – Open or create a c# project.
Ok, so any project will do. I used an existing project, but you can create a new one if you want. Any C# project should allow this to happen.
Step 2 – Create a class file.
In one of my C# projects in Visual Studio, I created a new class called XmlClass.cs.
Step 3 – Add the text and the variables to replaced
I put the following text into my new class:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace $rootnamespace$
{
[Serializable]
public class $safeitemrootname$
{
#region Member Variables
#endregion
#region Constructors
/*
* The default constructor
*/
public $safeitemrootname$()
{
}
#endregion
#region Properties
#endregion
#region Functions
#endregion
#region Enums
#endregion
}
}
Step 4 – Export the item as a template
In Visual Studio, chose File | Export Template. This starts a wizard that is extremely easy to follow.Note: If you have unsaved files in your project, you will be prompted to save them.
Chose Item template, select your project, and click Next.
In the next screen there was a tree view of check boxes for all my objects. I checked the box next to my XmlClass.cs.
In the next screen, provide references.Note: I added only System and System.Xml.
In the next screen, provide a Template name and a Template description.
Click finish.
You should now have the option under My Templates when you add a new item to your project.
This class will be useful and will save you and your team some typing when you are in the class creation phase of your project and you are creating all your Serializable classes.
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
Ok, so it is very common for the c# member variables to start with either an _ (underscore) or an m. So when creating a property, you can save a lot of time by changing it to assume this as well.
For example, your class may look as follows:
namespace AgentConfigurationPlugin
{
public class Class1
{
#region Member Variables
String _MemberString;
int _MemberInt;
#endregion
#region Constructors
/*
* The default constructor
*/
public Class1()
{
}
#endregion
#region Properties
public String MemberString
{
get { return _MemberString; }
set { _MemberString = value; }
}
public int Memberint
{
get { return _MemberInt; }
set { _MemberInt = value; }
}
#endregion
}
}
Note: I use the _ character even though it is hard to type (being up to the right of my pinky finger), so if you prefer, use the letter “m”, which is easy to type (being just below my pointer finger) and it also stands for “member variable”.
#region Member Variables
String mMemberString;
int mMemberInt;
#endregion
Anyway, whether it is an “m” or “_” or any other character, it is common to prefix member variables. So it would be useful if the property snippet assumed that prefix character as well.
The default snippet for creating a Property is located here:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC#\Snippets\1033\Visual C#\prop.snippet
<Code Language="csharp"><![CDATA[public $type$ $property$
{
get { return _$property$; }
set { _$property$ = value; }
}
$end$]]>
Or if you use “m” instead of “_” as I do, of course you would replace the “_” with an “m”.
<Code Language="csharp"><![CDATA[public $type$ $property$
{
get { return m$property$; }
set { m$property$ = value; }
}
$end$]]>
Now when you create a member variable and then a property that matches it exactly except for the prefix character, the works is done for you, making you a more efficient programmer.
You may want to change the propg snippet as well.
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
How to query a SQL database in C#? or How to execute a database query against a database in C#?
Having used other languages where this is much simpler, I was surprised at how “not simple” this was in C#. I expected it to be a little more complex than in some scripting language such as PHP, but it was way more complex.
It is nice to run the Query and store the results in a DataTable, so that is what my example shows.
There are a few simple steps to remember.
Create a String to hold the database connection string.
(Note: If you don’t know the proper format for a connection string use SqlConnectionBuilder.)
Create a SQL connection object.
Open the SQL connection.
Create a String to hold the query.
Create a SqlCommand object and pass the constructor the connection string and the query string.
Use the above SqlCommand object to create a SqlDataReader object.
Create a DataTable object to hold all the data returned by the query.
Use the DataTable.Load(SqlDataReader) function to put the results of the query into a DataTable.
Do something with the data in your DataTable here. For example, it is common to use a foreach loop to do something with each row.
Close the SQL connection.
Here is how I do it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace CountRows
{
class Program
{
static void Main(string[] args)
{
// Create a String to hold the database connection string.
// NOTE: Put in a real database connection string here or runtime won't work
string sdwConnectionString = @"Data Source = ServerName; user id=UserName; password=P@sswd!; Initial Catalog = DatabaseName;";
// Create a connection
SqlConnection sdwDBConnection = new SqlConnection(sdwConnectionString);
// Open the connection
sdwDBConnection.Open();
// Create a String to hold the query.
string query = "SELECT * FROM MyTable";
// Create a SqlCommand object and pass the constructor the connection string and the query string.
SqlCommand queryCommand = new SqlCommand(query, sdwDBConnection);
// Use the above SqlCommand object to create a SqlDataReader object.
SqlDataReader queryCommandReader = queryCommand.ExecuteReader();
// Create a DataTable object to hold all the data returned by the query.
DataTable dataTable = new DataTable();
// Use the DataTable.Load(SqlDataReader) function to put the results of the query into a DataTable.
dataTable.Load(queryCommandReader);
// Example 1 - Print your Column Headers
String columns = string.Empty;
foreach (DataColumn column in dataTable.Columns)
{
columns += column.ColumnName + " | ";
}
Console.WriteLine(columns);
// Example 2 - Print the first 10 row of data
int topRows = 10;
for (int i = 0; i < topRows; i++)
{
String rowText = string.Empty;
foreach (DataColumn column in dataTable.Columns)
{
rowText += dataTable.Rows[i][column.ColumnName] + " | ";
}
Console.WriteLine(rowText);
}
// Close the connection
sdwDBConnection.Close();
}
}
}
So now the results are stored in a DataTable.
You can now access each row of data using the DataTable.Rows collection.
The properties of WPF elements can be bound to properties of other WPF Elements. Lets do some simple examples of binding one element to another.
For this tutorial, I assume you are in Visual Studio 2008. I assume that you already know how to create a new Project and choose WPF Application. All examples assume you have a new WPF Application.
I am the believer that one example isn’t enough, so I am going to give you three examples:
Example 1 – Binding and Element’s property to CheckBox.IsChecked
This example will demonstrate binding a Button‘s IsEnabled property to a CheckBox‘s IsChecked property.
Step 1 – Add the elements
Add two items from the Toolbox:
CheckBox
Button
The Button is named button1 and the CheckBox is named checkBox1.
Change the text of the checkBox1 to “Enable button”. This can be done either in the Properties or in the XAML.
Step 2 – Adding Binding to the Button
In the XAML, locate the button1 element.
Add the following to the button1 element:IsEnabled="{Binding ElementName=checkBox1, Path=IsChecked}"In your project, ElementName could be any item. In this example, we only have two elements so far: button1, and checkBox1.The XAML now looks like this (only two new lines exist):
Check and uncheck the box an watch the binding do its work as it enables and disables the button.
Ok, so that was pretty cool. We have a simple example of binding one Element to another.
You can shoot yourself in the foot or Don’t be stupid!
Yes, you can shoot yourself in the foot by doing something stupid.
You could bind an element to itself. Let’s try it just so you can see it happen.
Add the same binding you added to button1 to checkBox1.
Compile and see what happens.
Example 2 – Binding and Element’s property to Slider.Value
This example uses a Slider and a TextBox.
Step 1 – Add the elements
Add two items from the Toolbox:
TextBox
Slider
The Slider is named slider1 and the TextBox is named textBox1.
Step 2 – Adding Binding to the TextBox
In the XAML, locate the textBox1 element.
Add the following to the textBox1 element:Text="{Binding ElementName=slider1, Path=Value}"ElementName can be any item. In this example, we only have two elements so far: slider1, and textBox1.The XAML now looks like this (only two new lines exist):
Example 3 – Binding and Element’s property to CheckBox.IsChecked
Ok, lets do a slight more complex example. We are going to have more than two elements. We are going to have a ListBox that contains a list of items (ListBoxItems). We are going to have a TextBox that displays the content of the selected item.
Step 1 – Add the elements
Add two items from the Toolbox:
TextBox
ListBox
Add multiple items to listBox1. This can be done either in the XAML or by clicking on the button for Items in the Properties of the listBox1.
Step 2 – Adding Binding to the TextBox
In the XAML, locate the textBox1 element.
Add the following to the textBox1 element:Text="{Binding ElementName=listBox1, Path=SelectedItem.Content}"Notice that we are using a property of a property for the Path. This is allowed. SelectedItem is a property of listBox1, and Content is a property of SelectedItem.The XAML now looks like this (only two new lines exist):
Select different items in the list and watch the textBox1 change to display the content of the selected item.
Copyright ® Rhyous.com – Linking to this post is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
This document assumes that you understand the concepts of object oriented programming and more specifically with C# programming, such as Classes or Objects, Methods, Properties, Events, etc. If not, it will be much harder to follow along.
This tutorial will cover the basics of data Binding in a WPF application. When you are done with this tutorial, you should be able to create a basic WPF-based graphical program that uses Binding. We will cover the different types of data Binding as well as what works and sometimes what doesn’t.
What is data Binding?
The idea of data Binding is to link a variable of any Type (int, string, object, etc…) to a graphical object’s Property that has the same type.
For example, lets say you have a Button object called myButton in your GUI like this: . The words “Click Me!” is a string property in the Button object: myButton.Text.
Imagine you have a string variable called strProperty in some part of your code that on its own has no way to interact with your GUI code. Lets say you want to change the myButton.Text property to match that string variable. Binding allows the button’s text string to always match a string property in some other object not really related to your GUI so if you change strProperty to equal “Enable” your button text will look like . If you then change the strProperty to “Disable” the button text will automatically change to be without out your back end code having to make any interaction with the GUI on its own.
Without Binding, you would have to write code yourself that would interact with the GUI and update the myButton.Text property when ever you update the string in code. In order to do this without Binding, you would also have to intermingle your background code with your GUI code. This can make it difficult to update or modify your GUI because GUI code is strung throughout all parts of your application. You don’t just have to update your GUI, you have to update all your code that interacts with the GUI.
So Binding allows you to have a back end code that is independent of the GUI. This is especially useful when the GUI needs to be updated or improved or when multiple GUIs exists (skins) and you can switch between them.
There are programming styles associated with developing a GUI separate from the back-end. Two of which are Model-View-Control (MVC) or Model-View-ViewModel (MVVM). This tutorial is not going to cover these, however, it is probably wise for you become familiar with these.
However, there is no reason you are limited to Binding to back end code. You can bind to code that is in the WPF GUI and very powerful applications can be written with little to no back end code.
Requirements for data Binding in WPF
In order to using data Binding, you should have the following requirements:
Your project should be a WPF Application, or your project should include a WPF Window or WPF Control.
Objects your elements bind to should implement System.ComponentModel.INotifyPropertyChanged.
The Binding source can also be “any public property, including properties of other controls, common language runtime (CLR) objects, XAML elements, ADO.NET DataSets, XML Fragments, and so forth.” (Reference: http://msdn.microsoft.com/en-us/magazine/cc163299.aspx).
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.
How to add a dynamic image and/or a dynamic button to a row using WPFToolKit DataGrid and DataGridTemplateColumns?
To start, I have a WPF project in Visual Studio 2008. I have installed the WPFToolKit and have added a reference to it in my project.
Often you want to display a DataGrid, but you don’t want to simply display it as is, you want to be able to enhance it and add functionality to it, such as adding an image to the start of each row or adding a button on each row.
Ok, so I have a table created using a DataTable that looks as follows:
IntVal
StrVal
0
normal
1
warning
2
error
I am passing this to a WFPToolKit DataGrid.
As I pass this to a Datagrid I want to add two columns:
I want to add an image that is different if it is normal, warning, or error.
I want to add a button only if it is warning or error.
So the visual would look as follows:
Image
IntVal
StrVal
Action
0
normal
1
warning
2
error
Step 1. Install prerequisites: Install Visual Studio 2008, and download and install the WPFToolkit.
You probably already have this done, and there are no steps for provided for these.
Step 2. Create a new WPF project in Visual studio 2008 and design the WPF interface
So once my project was created and the reference to WPFToolKit added, I then changed the XAML on my default Window1 class.
I needed to add a reference to the toolkit here as well.
I needed to add resources for my button.
I needed to add three separate resources for my images.
The data can come from anywhere but for this basic example, I am just statically creating a DataTable in the Constructor. I also added a property for the DataTable and the DataTable.DefaultView.
Data.cs
using System.Data;
namespace DataGridAddButtonAndImageColumns
{
public class Data
{
#region Member Variables
private DataTable mTable;
#endregion
#region Constructors
/*
* The default constructor
*/
public Data()
{
mTable = new DataTable();
mTable.Columns.Add("IntVal", typeof(int));
mTable.Columns.Add("StrVal", typeof(string));
DataRow row0 = mTable.NewRow();
row0["IntVal"] = 0;
row0["StrVal"] = "normal";
mTable.Rows.Add(row0);
DataRow row1 = mTable.NewRow();
row1["IntVal"] = 1;
row1["StrVal"] = "warning";
mTable.Rows.Add(row1);
DataRow row2 = mTable.NewRow();
row2["IntVal"] = 2;
row2["StrVal"] = "error";
mTable.Rows.Add(row2);
}
#endregion
#region Properties
public DataTable Table
{
get { return mTable; }
set { mTable = value; }
}
public DataView View
{
get { return mTable.DefaultView; }
}
#endregion
#region Functions
#endregion
#region Enums
#endregion
}
}
Step 4 – Create a ViewModel that implements INotifyPropertyChanged.
So creating a ViewModel is not exactly required but there really is benefit to the Model-View-ViewModel design pattern, so I will attempt to follow it even though this is a simple example application.
I created a new object called DataViewModel.
I implemented the INotifyPropertyChanged interface (though for this small application it isn’t used, I don’t want to leave it out cause you might need it for your application.)
I changed the constructor to take the Data object I designed in the previous step.
I expose the Table and the Table’s view as properties.
DataViewModel.cs
using System;
using System.ComponentModel;
using System.Data;
namespace DataGridAddButtonAndImageColumns
{
public class DataViewModel : INotifyPropertyChanged
{
#region Member Variables
readonly Data mData;
#endregion
#region Constructors
/*
* The default constructor
*/
public DataViewModel(Data inData)
{
mData = inData;
}
#endregion
#region Properties
public DataView View
{
get { return mData.View; }
}
public DataTable Table
{
get { return mData.Table; }
}
#endregion
#region Functions
#endregion
#region Enums
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
Step 5 – Add code to pass the DataTable to the DataGrid
So in the Window1.xaml.cs file, I create a new DataViewModel object and pass it a new Data object. I then assign the DataTable to the DataGrid’s DataContext object. My class now looks as follows.
Window1.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace DataGridAddButtonAndImageColumns
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
#region Member Variables
#endregion
#region Contructor
public Window1()
{
InitializeComponent();
DataViewModel model = new DataViewModel(new Data());
// It is ok to pass either the DataTable or the DataView
// so both lines below work, however I am only using one:
//
// mDataGrid.DataContext = model.View;
// mDataGrid.DataContext = model.Table;
mDataGrid.DataContext = model.Table;
}
#endregion
#region Functions
private void ButtonFixThis_Click(object sender, RoutedEventArgs e)
{
// Do something here
}
#endregion
#region Properties
#endregion
}
}
Now I can compile and run see my simple DataGrid.
IntVal
StrVal
0
normal
1
warning
2
error
Step 6 – Create the DataTemplateSelectors
I am going to use two DataTemplateSelector and I want them to share a base class, so first, I am going to create a base class for them.
I inherit DataTemplateSelector.
I add a function to find the parent Window1 object.
BaseDataTemplateSelector.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace DataGridAddButtonAndImageColumns
{
public class BaseDataTemplateSelector : DataTemplateSelector
{
#region Member Variables
#endregion
#region Constructors
/*
* The default constructor
*/
public BaseDataTemplateSelector()
{
}
#endregion
#region Properties
#endregion
#region Functions
protected Window1 GetWindow1(DependencyObject inContainer)
{
DependencyObject c = inContainer;
while (true)
{
DependencyObject p = VisualTreeHelper.GetParent(c);
if (c is Window1)
{
//mSectionControl = c;
return c as Window1;
}
else
{
c = p;
}
}
}
#endregion
}
}
Now I create an ActionDataTemplateSelector and a StatusImageDataTemplateSelector.
The ActionDataTemplateSelector will overload the SelectTemplate function and correctly select the Fix button resource if the status is warning or error.
ActionDataTemplateSelector.cs
using System.Data;
using System.Windows;
namespace DataGridAddButtonAndImageColumns
{
public class ActionDataTemplateSelector : BaseDataTemplateSelector
{
#region Constructors
/*
* The default constructor
*/
public ActionDataTemplateSelector()
{
}
#endregion
#region Functions
public override DataTemplate SelectTemplate(object inItem, DependencyObject inContainer)
{
DataRowView row = inItem as DataRowView;
if (row != null)
{
Window1 w = GetWindow1(inContainer);
if (row.DataView.Table.Columns.Contains("IntVal"))
{
if ((int)row["IntVal"] > 0)
{
return (DataTemplate)w.FindResource("FixThisTemplate");
}
}
return (DataTemplate)w.FindResource("NormalTemplate");
}
return null;
}
#endregion
}
}
The StatusImageDataTemplateSelector also overloads the SelectTempate function and selects the correct image for the status.
StatusImageDataTemplateSelector .cs
using System.Data;
using System.Windows;
namespace DataGridAddButtonAndImageColumns
{
public class StatusImageDataTemplateSelector : BaseDataTemplateSelector
{
#region Constructors
/*
* The default constructor
*/
public StatusImageDataTemplateSelector()
{
}
#endregion
#region Functions
public override DataTemplate SelectTemplate(object inItem, DependencyObject inContainer)
{
DataRowView row = inItem as DataRowView;
if (row != null)
{
if (row.DataView.Table.Columns.Contains("IntVal"))
{
Window1 w = GetWindow1(inContainer);
int status = (int)row["IntVal"];
if (status == 0)
{
return (DataTemplate)w.FindResource("StatusTemplateNormal");
}
if (status == 1)
{
return (DataTemplate)w.FindResource("StatusTemplateWarning");
}
if (status == 2)
{
return (DataTemplate)w.FindResource("StatusTemplateError");
}
}
}
return null;
}
#endregion
}
}
Step 7 – Create functions that add the new columns and have the constructor call each function.
Each function must:
Create a new DataGridTemplateColumn.
Assign a string for the Header.
Create a new DataTemplateSelector and assign it to the DataGridTemplateColumn’s CellTemplateSelector.
Add the new DataGridTemplateColumn to the DataGrid.
public void CreateActionButtonColumn()
{
DataGridTemplateColumn actionColumn = new DataGridTemplateColumn { CanUserReorder = false, Width = 85, CanUserSort = true };
actionColumn.Header = "Action";
actionColumn.CellTemplateSelector = new ActionDataTemplateSelector();
mDataGrid.Columns.Add(actionColumn);
}
public void CreateStatusColumnWithImages()
{
DataGridTemplateColumn statusImageColumn = new DataGridTemplateColumn { CanUserReorder = false, Width = 85, CanUserSort = false };;
statusImageColumn.Header = "Image";
statusImageColumn.CellTemplateSelector = new StatusImageDataTemplateSelector();
mDataGrid.Columns.Insert(0, statusImageColumn);
}
Don’t forget to call the functions in the constructor.
public Window1()
{
InitializeComponent();
DataViewModel model = new DataViewModel(new Data());
// It is ok to pass either the DataTable or the DataView
// so both lines below work, however I am only using one:
//
// mDataGrid.DataContext = model.View;
// mDataGrid.DataContext = model.Table;
mDataGrid.DataContext = model.Table;
CreateActionButtonColumn();
CreateStatusColumnWithImages();
}
Ok, so now you are finished. This should be working for you if you compile and run the program.
Image
IntVal
StrVal
Action
0
normal
1
warning
2
error
Options for handling the images without using a static path
The images were called statically in the above example, however, that will be problematic in actual implementation as each program is installed in a different location and the install location can usually be chosen by a user. You have two options to resolve this, and I will show you how to do both:
Embedding your images
Using image files located in a relative path
Either option work. The second option makes branding a little easier as code doesn’t have to be recompiled with new images to change the images, because the image files can simply be replaced.
Embedding your images
So you can embed your images as resources and use the embedded resources instead. To embed them, do this:
In Visual Studio under your project, create a folder called Images.
Copy your images into that folder.
In the XAML, change each of the image resource lines as shown
Using image files located in a relative path
I decided to NOT embed my images but instead solve this by using a relative path. My preference is for the images to come from actual files in an images directory that is relative to the directory from which the executable is launched:
Make sure to create the Images folder and add the images in the location where you exectuable runs. You may have to add the images folder to both the debug and release directories or otherwise resolve this, else you will get an exception when the images are not found.
Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.