Monday, December 29, 2008

Adventures while building a Silverlight Enterprise application part #2

In the previous episode we've looked at the requirements for our enterprise application and to some of the paths we've selected to solve some of the challenges we face. In this episode I wanted to give you more of an insight into our approach of tackling a kick-off into this project.
I know you people are craving for more technical stuff, but I found the following important enough to bug you with it.

Project organisation
The first thing that needs to be done to get any team to work is to get organized. This may seem obvious but this step is usually missed, skipped or otherwise ignored. We planned some moments of conversation for the team members, the first being every morning, take about 5-10 minutes to discuss what everybody is doing, so nobody gets into eachothers way and we also make sure everything we wanted to get done is being done.

The second moment of conversation is on a weekly basis. In this weekly meeting we can take decissions we need to take. To make this a smoother process, we take a few steps around this:
  1. Identify the need for a decission on some topic or question
  2. Apoint someone to write up a short proposal on the subject
  3. Make sure everyone gets and reads the proposal before the next weekly meeting
  4. Discuss the document during the next meeting
  5. Make any changes needed
  6. Approve the proposal, giving it a final status

The reason for this approach lies in the culture of our team. By writing a proposal we can keep our discussion on topic a little easier and also make sure that something is in print, that we can refer to at a later time.

Writing the book on it

Another thing that had to be done, was putting up some new coding standards. As a historical left over, coding standards were a bit out of date. Also as this is obviously our first enterprise application in Silverlight, some standard on XAML code and the coupling with the code behind and the services involved, would be a good idea. And a final subject we had to tackle was the layout of our TFS project. I won't bother you with all the details here, but if you have any specific questions, I'd be happy to answer them.

Startup

To actually get our project started we wanted to build a small sample of the application, containing some technical hotspots, but also some example code for the rest of the project. We will discuss this code after it's completion and revise it as needed, before moving production into full swing.

We identified the following topics to be a part of this first startup code:

- Security: Communicating with our security service and making sure the client can be authenticated, buiding the autorisation part of it, etc.

- Scalability: Loading multiple XAP's on the fly, testing performance degradation as the amount of code increases

- Multi language: Having the resource based multi language technology up and running

- Easy high volume data input: Build a module that allows for this

And besides this we also needed to include some standard modules, that would demonstrate how to build a module. Later on, this will evolve into a manual we can give new team members, for a running start.

Next time we will have a look at some code, that we build into this startup project. Please let me know what you think about this series and if there are any specific topics you would like to read about.

Wednesday, December 17, 2008

Highlighting databound information in Silverlight

As I was playing around with the Live Search SDK, to investigate integration in different applications, I ran into this issue. I wanted to display bound data and highlight it as well. I started digging and couldn't find a feasible solution on the web, so I came up with my own.

Let me start with a screenshot of the finished (well, sort of :-)) product:
What you are looking at is basically a textbox to input a query, a listbox to display the titles with highlights and a textblock to display some status information.
The highlighting information is supplied by Live search. It surrounds any term that needs highlighting with some special characters, one for the start and one for the end.
I figured that to get the highlighting, I would use a textblocks inlines property and add different runs for non-highlighted and highlighted text. The questions remained, when and where? My first order of business was to get the listbox to display data and I would figure it out later.
Here is the XAML for the listbox:

<ListBox x:Name="ResultsListBox" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<search:HighlightTextBlock Text="{Binding Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

As you can see I decided to use a usercontrol I would work on. The reason I used a usercontrol and not just derive from the TextBox is pretty straight forward, the TextBox class is sealed (Why, Microsoft, Why?!).
So a simple usercontrol that wraps the TextBox, is my starting point. The XAML for this is so easy that I haven't posted it here (Cause you are all an intelligent bunch, right? ;-) )

Next thing was to get the text into the textbox trough databinding. I simply added a dependency property to the user control. The code is largely generated from the snippet for it (propdp) and looks like this:

public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}

// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(HighlightTextBlock), new PropertyMetadata(string.Empty, TextPropertyChanged));


The alert reader may have spotted the extra parameter in the PropertyMetadata constructor at the end here. It points to an eventhandler that will handle the actual highlighting as our textproperty is changed.
The reason for this, in short, is that whenever databinding happens, the actual property setter is never used. Instead the SetValue method is called with the dependency property object. So the only insertion point for custom code is in an eventhandler attached to the dependency property. A better explanation can be found in this great article by Bryant Likes.


So here is the code I use to highlight my text and display it in the wrapped TextBox control:


private void SetText(string value)
{
innerTextBlock.Inlines.Clear();

string remainder = value;
while (remainder.Contains('\uE000'))
{
int highlightStart = remainder.IndexOf('\uE000');
int highlightEnd = remainder.IndexOf('\uE001');

string beforeHighlight = remainder.Substring(0, highlightStart);
Run beforeHighlightRun = new Run();
beforeHighlightRun.Text = beforeHighlight;
innerTextBlock.Inlines.Add(beforeHighlightRun);

string highlight = remainder.Substring(highlightStart + 1, highlightEnd - highlightStart - 1);
Run highlightRun = new Run();
highlightRun.Text = highlight;
highlightRun.FontWeight = FontWeights.Bold;
innerTextBlock.Inlines.Add(highlightRun);

remainder = remainder.Substring(highlightEnd + 1);
}
Run finalRun = new Run();
finalRun.Text = remainder;
innerTextBlock.Inlines.Add(finalRun);
}

private static void TextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
HighlightTextBlock textBlock = d as HighlightTextBlock;
if (textBlock != null)
{
textBlock.SetText(e.NewValue.ToString());
}
}

So whenever the Text property of my usercontrol is changed, it calls the SetText method, which in turn processes the set value into a Inlines collection, based on the provided highlighting information.

There are some obvious improvements to this code, like providing a starting and closing character property and some style to apply for highlighting, but you get the picture.

I hope this was another helpful article. Please let me know what you think about it and if you have any questions, I'm always happy to help. The comment box is waiting for you.

Tuesday, December 16, 2008

Adventures while building a Silverlight Enterprise application part #1

At the moment our team is in the start up fase of building a complete enterprise applications user interface in Silverlight. As it goes, we obviously run into some challenges and find out things. This series of articles is aimed at sharing our experiences with you, so you may avoid pitfalls, and maybe even get some inspiration from our approach.



In this first installment we would like to have a look at some of the general requirements for our application and how we aim to tackle some of those.



Requirements

The following generic requirements are part of our application:


  • Secure! This is a very important requirement for our customers, as data stored is sensitive information

  • Scalable. We will provide our application to very different users that will sometimes only use a small part of the application. They will not accept a high load for a small amount of functionality.

  • Multi language. We have to support both English and Dutch in the user interface.

  • Maintainable. It may sound obvious, but our application changes several times a year.

  • Reusable. Again, this may sound obvious, but in our case, parts of the application may be reused in portals.

  • Easy high volume data input. Some of the users need to be able to quickly enter large amounts of data.

As you can see, challenges enough! Also, some performance requirements were put down, based on ASP.NET, for loading certain parts of the application.


Right now we would like to take a look at one part of the solution we used: dynamically loading XAP files.


Dynamically loading XAP files


By applying this well documented technique to our application we expect to fill in at least part of the following requirements:



  • Secure; As our application is broken down into different parts, based on different functional roles, we only load part of the application based on access rights. So a large portion of code that a user has no access to is not loaded.

  • Scalable; As our application is broken down into different parts, it is easier to scale by adding a new part in a separate XAP file.

  • Maintainable; As we are working with a team of people, having multiple projects makes it easier to maintain and deploy.

  • Performance; This is our main reason to have multiple XAP files. We don't expect to reach our performance goals, if we would use a single XAP file. By only loading XAP files as we need them, we expect to have better performance at application start up and have a smaller footprint of the client (thus better performance).

It also raised some questions that we had to answer:



  • Security; We had to pass the security context of the user between applications. Initially we planned to also use multiple pages, thus we would have to pass this context from page to page in a secure way. We decided to have one outer application that is in charge of security context and by using an interface in the loaded modules, it can optionally pass this context to the module.

  • Reusable; As it stands, parts of our application are reused trough out different XAP files. This called for a good strategy around using Silverlight Class Libraries and User Controls. We expect this to be a continuous process, trough out building the application.

This first installment didn't contain much technical stuff, I know, but I hope it gave you some insight as to what challenges may arise when you use Silverlight 2 for building an enterprise application. I also hope that this series will show you that Silverlight 2 is actually very suitable for this purpose. Read the next post here.


If you have any questions, thoughts, suggestions, etc. please leave me a comment below. I'm always happy to read and reply to any of you.

Wednesday, December 10, 2008

Storing Silverlight DataGrid settings

Don't you find it annoying whenever you use an application with some kind of datagrid, that you can resize en reorder your columns in and the next time you use your application, the changes you made are lost? I know I hate it and I know my users are annoyed by it. This made me decide to find a way to store this information for my users and restore the settings whenever the user comes back.

As a start point I used the example code from an earlier article you can find here. I'm keen to store at least the width of each column and the order of the columns within the grid. To start this off I build a custom class to hold this information:

public class ColumnSetting
{
public int DisplayIndex { get; set; }
public int Index { get; set; }
public double Width { get; set; }

public ColumnSetting()
{
}

public ColumnSetting(int displayIndex, int index, double width)
{
DisplayIndex = displayIndex;
Index = index;
Width = width;
}
}


The next step is to have a collection of these and fill them. This begs the question, when? The moment of storing your information is key in this process. Obviously this is very dependent on your requirements. To keep it simple I used only the ColumnReorderd event on the grid. Later on I might expand this to more appropriate events.

Next is the building of code that creates a collection of the ColumnSetting class I've created, so the information is stored. Here is the first part of the method that takes care of saving the settings:

private void SaveGridLayout()
{
List<ColumnSetting> settings = new List<ColumnSetting>();
for (int index = 0; index < CarGrid.Columns.Count; index++)
{
DataGridColumn column = CarGrid.Columns[index];
ColumnSetting setting = new ColumnSetting(column.DisplayIndex, index, column.ActualWidth);
settings.Add(setting);
}


As you can see I use a generic list of ColumnSetting objects to collect the information. The columns DisplayIndex gives me the relative location in the grid, which is separate from the location in the Columns collection. The ActualWidth obviously gives me the width of the column.

Next question is, where should we store this information? Again this really depends on your requirements. Do users log on to your system and do you want to have these settings based on the user? Or do you want these settings based on the client as well? In this case I decided to go with the last, because this would allow me to play with Isolated Storage, which I haven't used before.
Some info on how this will work. Every user has his or her own Isolated Storage on the client PC, so depending on what user account is used to log on to the PC and depending on which PC there are settings. If users moves from one PC to another they will not have there settings available.

So how do we store the info then? Here is the code:


IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;

if (!appSettings.Contains("carGrid"))
{
appSettings.Add("carGrid", null);
}
appSettings["carGrid"] = settings;

As you can see, my first action is to get a shorter reference to the ApplicationSettings object. Each Silverlight 2 application has one ApplicationSettings object. Based on domain there can also be shared settings in the SiteSettings object, but in this case ApplicationSettings is the obvious choice.
As someone might very well be working with your application on this client for the very first time, we need to first create a settings entry in the appSettings collection, but only if it doesn't already exist. Next we can simply set our settings into the entry that was created earlier.

So now our settings are stored and we want to load these settings into the grid at load time. The first thing I tried was to do this in the Loaded event of the user control... and it failed. This is because you can not set the DisplayIndex as a result of a change to the DisplayIndex, which happens when you use the loaded event. As is documented by Microsoft this will result in an InvalidOperationException.
I decided that a good event would be the datagrids LayoutUpdated event. To prevent the settings being loaded every time this event is triggered, I simply keep track of this with a boolean field.

The loading of the actual settings is done like this:


private void LoadGridSettings()
{
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
if (appSettings.Contains("carGrid"))
{
List<ColumnSetting> settings = appSettings["carGrid"] as List<ColumnSetting>;
if (settings != null)
{
foreach (ColumnSetting setting in settings)
{
DataGridColumn column = CarGrid.Columns[setting.Index];
column.DisplayIndex = setting.DisplayIndex;
column.Width = new DataGridLength(setting.Width);
}
}
}
}

As you can see, if any settings are available then they are loaded in the datagrid. Note that you obviously can't set the ActualWidth property (as it is read only), so you simply set the Width property.

I hope this article was useful to you. Please leave me a comment to tell my what you think about it, ask me any questions, or suggest any topics and I will be grateful.

Wednesday, December 3, 2008

The TextBlock Inline property in Silverlight 2

I was testing with this dynamic conversion tool I've been working on when I ran into one of those moments of complete confusion about this regular expression I had to evaluate. The Regulator didn't provide me with any insight and responded differently from the actual .NET implementation (anyone any thoughts on how that is possible?).

To get a better grip on things, I decided to build a small tool for testing regular expressions myself and as I'm always looking for a good, or less then good, excuse for using Silverlight 2, I decided to use it as my UI. I figured it would be cool to have any matches in my input highlighted on the fly, so I dove into it with the following result:



So how does this relate to the TextBlock's inline property?
Well it is used twice in this application and it may not be as obvious to everyone.

Highlighting text in Silverlight without the use of any third party controls, is different from anything we're used to in either HTML, ASP.NET or Windows Forms. In HTML and ASP.NET it's quite obvious. In Windows Forms, one would simply use a selection to point out the text to change the font for that text, including the color.

In Silverlight 2, this is done trough the use of a TextBlock and it's Inline property. The Inline property is of the type InlineCollection, which is, offcourse, a collection of Inline objects. The Inline class in turn, is an abstract class that is inherited by two other classes, being Run and LineBreak. You would use Run to define a run of text and you would use LineBreak for it's obvious purpose of going to the next line.

I used this in XAML to define the tooltip you can see in the screenshot. It looks like this:

<TextBlock x:Name="matchesTextBlock">
<ToolTipService.ToolTip>
<ToolTip>
<TextBlock>
<Run>Matched text is colored red,</Run>
<LineBreak />
<Run>while unmatched text is colored black.</Run>
<LineBreak />
<Run>If the expression matches the entire</Run>
<LineBreak />
<Run>input, the background will color gray,</Run>
<LineBreak />
<Run>otherwise it will color white.</Run>
</TextBlock>
</ToolTip>
</ToolTipService.ToolTip>
</TextBlock>


As you see, once you get used to it, it isn't that hard. As you can aply a seperate style to each Inline element you can easily write some code to highlight the matches from a regular expression. The method that does it looks like this:


private void UpdateMathes()
{
string input = inputTextBox.Text;
string expression = expressionTextBox.Text;

MatchCollection matches = Regex.Matches(input, expression);

bool isMatch = matches.Count >= 1 && matches[0].Length == input.Length;

matchesTextBlock.Inlines.Clear();
int writtenChars = 0;
foreach (Match match in matches)
{
string inputToMatch = input.Substring(writtenChars, match.Index - writtenChars);
DrawUnmatchedText(inputToMatch);
writtenChars = match.Index + match.Length;
string matched = match.Value;
DrawMatchedText(matched);
}
string inputAfterMatch = input.Substring(writtenChars, input.Length - writtenChars);
DrawUnmatchedText(inputAfterMatch);

if (isMatch)
{
matchesBorder.Background = new SolidColorBrush(Colors.Gray);
}
else
{
matchesBorder.Background = new SolidColorBrush(Colors.White);
}
}

Some of you may have spotted the way that isMatched is filled and think that using Regex.IsMatch would be a more efficient way of doing this. Unfortunately Regex.IsMatch returns true if any match is found, so even a partial match would return true.

The two methods that do the actual work look like this:

private void DrawUnmatchedText(string text)
{
Run run = new Run();
run.Text = text;
run.Foreground = new SolidColorBrush(Colors.Black);
matchesTextBlock.Inlines.Add(run);
}

private void DrawMatchedText(string text)
{
Run run = new Run();
run.Text = text;
run.Foreground = new SolidColorBrush(Colors.Red);
matchesTextBlock.Inlines.Add(run);
}


So this is how you can easily format your text inside a textblock. As you may already know, I'm always keen to get your feedback on any of my articles and I'm always happy to answer any questions, so please leave your comments below.

UPDATE 31-03-2010: You can now download the original Silverlight 2 source from here. Thanks go out to alexander pointing out he was missing some source.

Monday, December 1, 2008

Silverlight 2 Datagrids RowDetailsTemplate and databinding radiobuttons

Today something I've been testing with for future use. I've been building a small prove of concept for using the datagrid to edit data, not directly in the grid, but in a details form. It looks like this:


So you can click a row and the details form is displayed below the row. You can then edit the data and it is immediately shown in the grid.

In this article I'll assume you have some knowledge on databinding and the datagrid yourself. Also having some experience with Silverlight, XAML, Blend and Visual Studio would come in handy.

So how does this work? This uses the RowDetailsTemplate property of the datagrid.

The RowDetailsTemplate in Blend
I started building this in Blend and ended up in Visual Studio anyway. Blend does provide support for editing this template, trough the menu Object &lt; Edit Other Templates &lt; Edit RowDetailsTemplate. You can build up a template here, but it's visual designer is not really user friendly.

Then comes the databinding. The datagrid is easy. Just go to the Project panel and under data add a CLR object, in this case a collection and drag it onto the grid. You can then turn off AutoGenerateColumns and define your own columns trough the Columns collection.

For the RowDetailsTemplate this doesn't work that polished. You can go to a control, let's say a textbox, and databind to it's properties, in this case the text property. A nice Create Data Binding dialog comes up and I taught "ok, just bind to the collection and we're done". But clicking on the collections datasource created earlier only shows the count property. So I figured I'd add a datasource for the detailed class and bind to the property there. The dialog allows you to do this, but as soon as you close it errors start popping up telling you that a TextBox can't be converted into a DependencyObject (don't ask me why not) and the datasource you just created can't be resolved.

Checking the XAML shows that Blend did exactly what I asked, binding to the datasource I created, with the path I clicked on. But this is not what I want. I just want to bind to the actual datacontext, so at this point I fired up Visual Studio to edit this mess myself (as Blend doesn't support any IntelliSense, I prefer Visual Studio as a XAML editor). I simply changed the binding to {Binding propertyname} and it works like a charm.

So next up, I wanted to databind two RadioButtons to one field.

Databinding radiobuttons
In the process of building a small form to show how this could work in future applications, I wanted to bind a field (in this case a persons Gender field) to two radiobuttons (obviously Male and Female). I googled around a bit and found out that this is not as straight forward as one might think. Some people actually wrote a considerable amount of code to get it to work.
I pondered on this a bit and decided that I would come up with an approach using ValueConverters. You can build custom ValueConverters simply by creating a class that implements the IValueConverter interface. From there on it's actually not that hard.

I came up with using two value converters, one for the male and one for the female. Here is what the Female one looks like:

public class GenderFemaleConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool result = false;
Gender actualValue = (Gender)value;
result = actualValue == Gender.Female;
return result;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool actualValue = (bool)value;
if (actualValue)
{
return Gender.Female;
}
else
{
return null;
}
}

#endregion
}


For the male version of this, all I changed where the Gender.Female references to Gender.Male. Then all you have to do is use these in your databinding. To do so, first you'll need to create resources for them at some point. In this case I put the resources in the UserControl. The XAML looks like this:

&lt;UserControl.Resources>
&lt;DataClasses:PersoonCollection x:Key="PersonCollectionDS" d:IsDataSource="True"/>
&lt;local:GenderFemaleConverter x:Key="GenderFemaleConverter" />
&lt;local:GenderMaleConverter x:Key="GenderMaleConverter" />


Next step is to include these resources as converters in your databinding:


&lt;RadioButton Grid.Row="1" Grid.Column="3" Content="Male" IsChecked="{Binding Gender, Converter={StaticResource GenderMaleConverter}}">&lt;/RadioButton>
&lt;RadioButton Grid.Row="1" Grid.Column="4" Content="Female" IsChecked="{Binding Gender, Converter={StaticResource GenderFemaleConverter}}">&lt;/RadioButton>


As you can see, I bind both RadioButtons to the same field, but I use the two different converters.
One could argue that this is not the most efficient way of doing things and I would agree. A better approach may be to build some sort of RadioGroup control that has a single converter and databinding happens to a property of that. For this prove of concept however, it does what is has to do.

I hope you enjoyed this article and found it helpful. Please let me know if there are any questions and also suggestions for subjects are still very welcome, although I might not be able to go into every single thing you throw at me.