Saturday, 24 March 2012

Styling user controls in WPF with EventSetter


It is possible to style a WPF control not only with a shared style, often put into a ResourceDictionary, but also attach some behavior to the applied style, using EventSetter element in XAML. This makes it possible to quickly apply shared behavior of a WPF control in the ResourceDictionary. The ResourceDictionary with this custom Style can then be inserted into the App.xaml (application wide) file, such that all controls of that given type will have this custom style and behavior. If a style is created without a x:Key element, it is called an implicit style. This means that the user control in WPF will "inherit" this style automatically, if the control does not override the style. This again means that the Setter and EventSetter of the Style will be applied to the user control in WPF. It is also possible to set a lot of other things in a style, such as triggers, but this blog article will focus on custom code in the EventSetter element inside a WPF style.

Example - setting an EventSetter on a RadTimePicker control
The RadTimePicker is a stylish user control to set the time of the day. This user control is not part of standard WPF but created by Telerik. I worked with this control today and there was an error which actually Telerik admitted was present in their forums. If you type the text "0130" for instance, this is interpreted to "13:00", which means it is hard to type correct hours between 00:00 and 09:59. There was obviously an error when the text contains a leading zero. The suggestion from Telerik was to handle the ParseDateTimeValue event handler in the control. The problem for me was that already multiple usages of the RadTimePicker was present in the solution I was working on. I wanted to "patch" all the RadTimePicker controls with handling the ParseDateTimeHandler. The quickest way, it turned out, was to use the already added implicit style of the RadTimePicker. The style was in a resource file which was added in the App.xaml file and I just added an EventSetter element to the style element.

I will now present this technique next. First off, lets create a resource dictionary with an implicit style for the RadTimePicker element.


<ResourceDictionary x:Class="EventSetterWPF.ControlStyles"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tc="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input">


<Style TargetType="{x:Type tc:RadTimePicker}">
<EventSetter Event="ParseDateTimeValue" Handler="RadTimePicker_ParseDateTimeValue" />
</Style>

</ResourceDictionary>


Make note that I use x:Class here, this is a resource dictionary with a code behind. To achieve this, add a file with the same name as the resource dictionary, but with the .cs extension. E.g. the resource dictionary is called ControlStyles.xaml, then add a class called ControlStyles.xaml.cs

Add this to the class:



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

namespace EventSetterWPF
{


public partial class ControlStyles : ResourceDictionary
{

public ControlStyles()
{
InitializeComponent();
}

public void RadTimePicker_ParseDateTimeValue(object sender, ParseDateTimeEventArgs e)
{
if (e.IsParsingSuccessful)
{
string originalString = e.TextToParse.Replace(":", "");
if (!string.IsNullOrEmpty(originalString) && originalString.Length == 4 && originalString.StartsWith("0"))
{
int enteredHour = -1;
int enteredMinutes = -1;
if (int.TryParse(originalString.Substring(0, 2), out enteredHour) &&
(int.TryParse(originalString.Substring(2, 2), out enteredMinutes)) &&
e.Result.HasValue)
{
DateTime originalValue = e.Result.Value;
DateTime? newValue = new DateTime(originalValue.Year, originalValue.Month, originalValue.Day,
enteredHour, enteredMinutes, 0);
e.Result = newValue;
}
}
}
}

}

}




There are two important things to consider her first. The class is made partial, which is necessary for a code behind class. The code behind is referenced by the x:Class attribute in the XAML code of the resource dictionary (ControlStyles.xaml).
Further, the resource dictionary must inherit from ResourceDictionary as its base class.

Then the EventHandler is set up in the Style of the RadTimePicker with the EventSetter:



<Style TargetType="{x:Type tc:RadTimePicker}">
<EventSetter Event="ParseDateTimeValue" Handler="RadTimePicker_ParseDateTimeValue" />
</Style>



The EventSetter element has got an attribute called Handler which points to the event handler (Callback for event) that will handle an event in the styled WPF user control. The code behind will then parse the datetime as shown in the event RadTimePicker_ParseDateTimeValue.

This is a very powerful technique in WPF. Using an implicit style for a WPF user control not only is limited to style setters, triggers or even using a control template. It is also possible to use EventSetter to have common behavior and specify the common behavior in a code behind.

The final step is just to add in the resource dictionary in the App.xaml file to make it available for all RadTimePicker controls in the application.



<Application x:Class="EventSetterWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="TestRadTimePicker.xaml">
<Application.Resources>
<ResourceDictionary Source="ControlStyles.xaml" />
</Application.Resources>
</Application>




Understanding how we can "style" WPF user controls using EventSetter and a codebehind file opens up a whole new world of possibilites for creating advanced WPF user controls.

Monday, 5 March 2012

Speeding up Entity Framework queries with CompiledQuery

If you use Entity Framework, you may have experienced tha EF is quite slow when it comes to more complex queries against the database, because the EF query must be first compiled into SQL before it is executed to populate the EF data objects of the result set. It is possible to speed up these EFqueries, by using Compiled Queries in Entity Framework (EF). I will explain how this is done by following the MSDN page with examples of how to achieve this next, MSDN page with Compiled Queries examples

First off, not every case will benefit from a compiled query. If the query is simple or not easy parametrized, using a compiled query is not adviced. However, more often than not, you will want to compile the query first and store off that query in a static readonly variable (at least this is what is done in the examples), so that we can reuse the compiled query variable by invoking it. Let's investigate an example of how to perform such compiled query in Entity Framework.

The following code is a complete example of how to perform compiled queries:



class Program
{

static readonly Func<AdventureWorksEntities, MyParams, IQueryable<SalesOrderHeader>> s_compiledQuery =

CompiledQuery.Compile<AdventureWorksEntities, MyParams, IQueryable<SalesOrderHeader>>(
(ctx, myparams) => from sale in ctx.SalesOrderHeader
where sale.ShipDate > myparams.startDate && sale.ShipDate < myparams.endDate
&& sale.TotalDue < myparams.totalDue
select sale);

static void Main(string[] args)
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<SalesOrderHeader> sales = s_compiledQuery.Invoke(context, new MyParams
{
startDate = new DateTime(2003, 3, 3),
endDate = new DateTime(2003, 3, 8),
totalDue = 700.00M
});
foreach (SalesOrderHeader sale in sales)
{
Console.WriteLine("ID: {0} Order date: {1} Total due: {2}",
sale.SalesOrderID,
sale.OrderDate,
sale.TotalDue);
}
Console.WriteLine(string.Format("Number of rows found: {0}", sales.Count()));
}

Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}



At the top of this example code, we create a static readonly variable of type Func with the following parametres, first off AdventureWorksEntities (which is a type parameter like the rest). This is the type of the ObjectContext used in the AdventureWorks database, which I guess many of you already know about. It is possible to install the AdventureWorks on your local Sql Server 2008 (Express) R2 database, if you want to try it out. Check out this url for more information about AdventureWorks:
Download AdventureWorks sample database .

Further on, we pass in a MyParams type. It is a struct in this case (it could be a class), which we use in this example for demonstrating purposes. The overload to CompiledQuery.Compile method takes up to 16 type parameters, if you use a parameter object instead, you can provide multiple parametres. The MyParams type looks like this:




struct MyParams
{

public DateTime startDate { get; set; }

public DateTime endDate { get; set; }

public decimal totalDue { get; set; }
}




Let's have a look at the declaration of the compiled query again:


static readonly Func<AdventureWorksEntities, MyParams, IQueryable<SalesOrderHeader>> s_compiledQuery =

CompiledQuery.Compile<AdventureWorksEntities, MyParams, IQueryable<SalesOrderHeader>>(
(ctx, myparams) => from sale in ctx.SalesOrderHeader
where sale.ShipDate > myparams.startDate && sale.ShipDate < myparams.endDate
&& sale.TotalDue < myparams.totalDue
select sale);


Make a note that we use the Func type to store the compiled query. The LAST parameter is the return type of the delegate that we use, which is the right hand side of the declaration above. The return type is IQueryable of type SalesOrderHeader. This must of course match the entity framework query that is used in the right hand side. If you use an ordering expression in the EF query (orderby), the return type will be IOrderedQueryable instead. Further on, we use a lamba expression and use two variables on the left of the goes to operator => , which for some will be a bit uncommon, as most are used to x => x.MyValue and so on. It is possible to specify more than two variables also here, if you use 'index', that has a special meaning (index within the result set).. Anyways, the injection of the parameters are defined indirectly on the left hand side and then used in the EF query on the right side of the expression.

The data is fetched from the database by invoking the compiled query with the Invoke method of the Func which is assigned at the top. This will actually fetch the data. The result set is stored into a variable of type IQueryable<SalesOrderHeader> , if you want to use ordering in the EF query, use IOrderedQueryable instead, it is also possible to used grouping in the EF query of a compiled query, use IGroupedQueryable then (example not included..)



IQueryable<SalesOrderHeader> sales = s_compiledQuery.Invoke(context, new MyParams
{
startDate = new DateTime(2003, 3, 3),
endDate = new DateTime(2003, 3, 8),
totalDue = 700.00M
});



The compiled query is invoked and MyParams is specified inline here by using a constructor initializer (this is a struct which is used, however it is possible to use a class instead, if desired), the context is also passed. The invoke method of the Func will use the compiled query and invoke the query to the database and fetch the dataset using the compiled query.

If you use EF to build reports or other more complex queries, the use of a compiled query is highly recommended. It could be possible to use directly SQL, but that will mean that you will loose the support of refactoring (directly querying the database skips the overhead of translating an Entity Framework query into a sql expression).

This is therefore a highly recommended approach to performing faster Entity Framework queries against your database. Check out the MSDN page for more resources, I have not done any performance testing on this feature, but I feel it looks very promising.

Sunday, 5 February 2012

A more deterministic approach to TempData in MVC

I just read the article of Greg Shackles on custom ITempDataProvider. I will follow up his blog post with my own slight adjustments and experiences of his implementation of a persistent customized TempDataProvider, using MongoDb.

The original article is here:
MongoDb based TempData provider

The default implementation of ITempDataProvider in MVC is the class SessionStateTempDataProvider. This temp data provider relies on SessionState.
As long as the end-user applies InProc as the SessionState in web.config,
TempData should work as expected. However, as soon as a move towards SqlServer
in SessionState of web.config is selected, things will start to crash..

The following is a sample implementation of how to implement a more deterministic and stable implementation, with the aid of the MongoDb object database.

The custom ITempDataProvider looks like this:


public class CustomTempDataProvider : ITempDataProvider
{
private string _collectionName;
private string _databaseName;

public CustomTempDataProvider(string databaseName, string collectionName)
{
_collectionName = collectionName;
_databaseName = databaseName;
}

#region ITempDataProvider Members

public IDictionary LoadTempData(ControllerContext controllerContext)
{
var tempDataDictionary = new Dictionary();

using (Mongo mongo = new Mongo("mongodb://127.0.0.1:27017"))
{
mongo.Connect();
IMongoCollection collection = mongo.GetDatabase(_databaseName).
GetCollection(_collectionName);
IEnumerable tempData = collection.Find(item => item.SessionIdentifier == controllerContext.HttpContext.Request.UserHostAddress).Documents;



foreach (var tempDataItem in tempData)
{
tempDataDictionary.Add(tempDataItem.Key, tempDataItem.Value);
collection.Remove(tempDataItem);
}
}

return tempDataDictionary;
}

public void SaveTempData(ControllerContext controllerContext, IDictionary values)
{
string hostName = controllerContext.HttpContext.Request.UserHostName;
using (Mongo mongo = new Mongo())
{
mongo.Connect();

IMongoCollection collection = mongo.GetDatabase(_databaseName).
GetCollection(_collectionName);

IEnumerable oldItems = collection.Find(item =>
(item.SessionIdentifier == hostName)).Documents;

foreach (var tempDataItem in oldItems)
{
collection.Remove(tempDataItem);
}

if (values != null && values.Count > 0)
{
collection.Insert(values.Select(tempDataValue => new MongoTempData
{
SessionIdentifier = controllerContext.HttpContext.Request.UserHostAddress,
Key = tempDataValue.Key,
Value = tempDataValue.Value
}));
}
}

}

#endregion
}


Beware of the code above that the SessionIdentifier is set to the UserHostAddress of the request. Rather, you might want to inject the ASP.NET SessionId or something similar here instead to avoid multiple users accessing potentially the same data. This will both crash for the next User B since User A got to the data first (TempData removes its data after it is read). In addition User A gets to read User B TempData, which could of course include sensitive data. With that word of caution, I will leave to the read to select a good identifier for the TempData.

Next up, we create a base class for setting the TempDataProvider:


public class CustomTempDataControllerBase : Controller
{
public CustomTempDataControllerBase()
{
TempDataProvider = new CustomTempDataProvider
("test", "MongoTempData");
}
}


Our controllers do then only need to inherit from this base class (note that this base class inherits from the MVC class Controller):


public class HomeController : CustomTempDataControllerBase
{

public ActionResult Index()
{
TempData["CurrentCity"] = "Trondheim";
TempData["CurrentTemperature"] = "-7 Centigrade Windchill";
return Content("TempData updated! Read the TempData here");
}

public ActionResult ReadTempData()
{
return View();
}

}


The view ReadTempData.cshtml looks like this (Razor code):
Contents of TempData:






@foreach (var tempDataItem in TempData)
{
@tempDataItem.Key @:: @tempDataItem.Value
}


That is basically all that is too it. To start up the MongoDb on your computer, download the binaries to e.g. c:\mongodb . The binaries are available from the site:
http://www.mongodb.org/

Start the database like this:
mongod --dbpath c:\mongodb\data

Next up, get the necessary binaries from the original source of this article:
http://www.gregshackles.com/2010/07/asp-net-mvc-do-you-know-where-your-tempdata-is/comment-page-1/#comment-2491

In addition I recommend the tool MongoVUE, a GUI for viewing the data inside your collections of this object oriented database (MongoDb is not a RDBMS).

Screenshot of our sample in MongoVUE:



The source code for the custom ITempDataProvider in this article is here:
Mongo Db based custom Temp Data Provider for MVC