One of the great features Silverlight offers is the ability to customize controls by using control templates. If you don’t like how a particular control looks you can modify the template and in many cases be ready to use the new control without writing a single line of C# or VB code. I’m working on a client application that uses the AutoCompleteBox found in the Silverlight Toolkit and needed a way to change it from a regular TextBox to more of an editable ComboBox. Fortunately the Silverlight Toolkit samples (for Silverlight 2 and 3) already do something like this as you can see here (once on the page click on AutoCompleteBox to the left and then on the Styling tab at the top of the page to see the sample).
The sample modifies the standard AutoCompleteBox to look more like an editable ComboBox by defining a custom control template with a ToggleButton in it (Tim Heuer provides a nice walk through of this type of customization here if you’re interested). Here’s a simplified version of the Silverlight Toolkit’s sample template that I’m using:
<ControlTemplate TargetType="input:AutoCompleteBox">
<Grid Margin="{TemplateBinding Padding}">
<TextBox IsTabStop="True" x:Name="Text" Style="{TemplateBinding TextBoxStyle}" Margin="0" />
<ToggleButton x:Name="ToggleButton"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{StaticResource ComboToggleButton}"
Margin="0"
HorizontalContentAlignment="Center"
Background="{TemplateBinding Background}"
BorderThickness="0"
Height="16" Width="16"
Click="DropDownToggle_Click">
<ToggleButton.Content>
<Path x:Name="BtnArrow" Height="4" Width="8" Stretch="Uniform" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "
Margin="0,0,6,0" HorizontalAlignment="Right">
<Path.Fill>
<SolidColorBrush x:Name="BtnArrowColor" Color="#FF333333"/>
</Path.Fill>
</Path>
</ToggleButton.Content>
</ToggleButton>
<Popup x:Name="Popup">
<Border x:Name="PopupBorder" HorizontalAlignment="Stretch" Opacity="1.0" BorderThickness="0">
<Border.RenderTransform>
<TranslateTransform X="2" Y="2" />
</Border.RenderTransform>
<Border.Background>
<SolidColorBrush Color="#11000000" />
</Border.Background>
<ListBox x:Name="Selector" ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemTemplate="{TemplateBinding ItemTemplate}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
Looking at the template you’ll see that it defines a ToggleButton with a Click event and associated event handler named DropDownToggle_Click. Here’s what the ToggleButton’s Click event handler looks like:
private void DropDownToggle_Click(object sender, RoutedEventArgs e)
{
FrameworkElement fe = sender as FrameworkElement;
AutoCompleteBox acb = null;
while (fe != null && acb == null)
{
fe = VisualTreeHelper.GetParent(fe) as FrameworkElement;
acb = fe as AutoCompleteBox;
}
if (acb != null)
{
if (String.IsNullOrEmpty(acb.SearchText))
{
acb.Text = String.Empty;
}
acb.IsDropDownOpen = !acb.IsDropDownOpen;
}
}
You can see that the code uses the VisualTreeHelper class to access the parent of the ToggleButton which is the AutoCompleteBox. Once the AutoCompleteBox parent is found the code handles showing or hiding the Popup control that’s part of the control template by setting the IsDropDownOpen property to true or false.
This works fine if the control template is placed in the same scope as the event handler code such as the page or user control resources section. However, if you try to move the template code to another resources section that doesn’t have access to the event handler code (DropDownToggle_Click) you’ll run into problems . What if you want to put the control template in a merged resource dictionary (similar to an external CSS stylesheet to give a web analogy) and can’t hard-code the click event into the control template since you don’t know where the event handler will be defined at that point? Although you can certainly write a custom control that derives from AutoCompleteBox in this case (which would be recommended if you’ll re-use the control across multiple pages or user controls), another solution is to hook-up the ToggleButton’s Click event when the AutoCompleteBox control first loads as shown next:
void SilverlightApplication_Loaded(object sender, RoutedEventArgs e)
{
HookAutoCompleteBoxEvents();
}
void HookAutoCompleteBoxEvents()
{
AutoCompleteBox[] boxes = { this.JobIDAutoCompleteBox, this.EmployeeAutoCompleteBox };
foreach (var box in boxes)
{
Grid grid = VisualTreeHelper.GetChild(box, 0) as Grid;
ToggleButton tb = grid.Children[1] as ToggleButton;
if (tb != null) tb.Click += DropDownToggle_Click;
}
}
When the Silverlight application Loaded event is called it calls the HookAutoCompleteBoxEvents() method. Within HookAutoCompleteBoxEvents() an array of AutoCompleteBox controls is iterated through to locate the ToggleButton for each control and attach a Click event handler to it. Doing this avoids hard-coding the event handler in the control template so that it can be defined just about anywhere you’d like without running into code scoping issues.
Note: Keep in mind that if you plan on using the customized AutoCompleteBox control in several places it may be worth the time to create a custom control that derives from AutoCompleteBox to avoid having to put the ToggleButton event handler code and the HookAutoCompleteBoxEvents code into each page or user control.

For more information about onsite, online and video training, mentoring and consulting solutions for .NET, SharePoint or Silverlight please visit http://www.thewahlingroup.com/.

Posted by dwahlin |
Tue, 30 Jun 2009 17:17:51 GMT |
Comments (0)
If you build ASP.NET applications then you’re used to working with controls a lot. Nearly everything you do requires accessing control IDs. If you need to retrieve data entered by an end user you need to reference the controls that contain the data. That’s just the way we do it in ASP.NET and if you’ve been writing ASP.NET applications very long its how you’re conditioned to think.
Silverlight changes the game quite a bit due to the way you can bind data to controls. With Silverlight applications I don’t always name my controls since ultimately I care about accessing the data rather than the control that contains the data. Sure, if I need to perform an animation or change a control’s style I’ll need to access the control directly by its name, but when it comes to accessing data there’s an easier way. Silverlight provides two-way bindings that allow a data object to be bound to a control in a unique way. If the user changes the data the source object is automatically updated without additional code on your part. An example of a two-way binding defined in XAML is shown next:
<TextBox Text="{Binding MondayQuantity, Mode=TwoWay}"
Style="{StaticResource TimeSheetTextBoxStyle}" />
This example binds the DataContext (the main object bound to the overall form) object’s MondayQuantity property to a TextBox control. The Mode is set to TwoWay rather than the default OneWay binding which means that any changes to the TextBox are automatically moved back to the MondayQuantity property in the source object. As a result, you don’t need to access the TextBox to get the value entered since the source object that was originally bound to the control contains up-to-date data. Having done a lot of ASP.NET programming this took some time to get used to, but it’s nice to leverage once you know about it.
I’m working on an application that uses a Silverlight DataGrid control with nested ComboBox and TextBox controls in each row. As a user changes quantity or hour values the totals need to be updated.
Let’s take a look at different solutions that can be used to update the totals values.
A Control-Oriented Approach
One solution to updating the totals is to iterate through the target row and locate each TextBox to get the values. That’s the control-oriented approach we’d normally use in ASP.NET applications. To update TextBlock controls that track totals at the end of each row (red area in the image shown above) as a user changes values, you could use the following code which iterates through each column in a given row and locates TextBox controls:
//Get the DataGrid's selected item
TimeSheetViewRow dataContext = (TimeSheetViewRow)TimeSheetDataGrid.SelectedItem;
decimal totalQty = 0;
decimal totalHours = 0;
//Loop through all the columns for the selected row
for (int i = 2; i < TimeSheetDataGrid.Columns.Last().DisplayIndex; i++)
{
StackPanel sp = (StackPanel)TimeSheetDataGrid.Columns[i].GetCellContent(dataContext);
foreach (UIElement elem in sp.Children)
{
//Find the TextBox controls
if (elem is TextBox)
{
TextBox tb = (TextBox)elem;
if (!String.IsNullOrEmpty(tb.Text))
{
decimal val = decimal.Parse(tb.Text);
if (tb.Tag.ToString() == "Quantity") totalQty += val;
if (tb.Tag.ToString() == "Hours") totalHours += val;
}
}
}
}
//Find totals TextBlocks and update them
StackPanel totalsSP = (StackPanel)TimeSheetDataGrid.Columns[TimeSheetDataGrid.Columns.Last()
.DisplayIndex].GetCellContent(dataContext);
((TextBlock)totalsSP.FindName("TotalQuantityTextBlock")).Text = totalQty.ToString();
((TextBlock)totalsSP.FindName("TotalHoursTextBlock")).Text = totalHours.ToString();
While this technique works fine, it’s definitely not the easiest way to total each day’s hours and quantity and would have to be re-written if the TextBox controls’ container changes from a StackPanel to something else. Let’s look at a more flexible data-oriented approach.
A Data-Oriented Approach
Since the TextBoxes in each row all have TwoWay bindings defined, the source object that was originally bound (of type TimeSheetViewRow) is automatically updated as TextBox values change. As a result, I can simply grab the selected item (which represents the bound object) from the DataGrid and then total up the property values. Once the totals are calculated the appropriate quantity and hours total properties can be updated on the source object which automatically updates the grid TextBlock controls bound to those properties. It’s important to note that the TimeSheetViewRow class implements INotifyPropertyChanged so that it can notify Silverlight as property values change so that the data can be re-bound to the controls. The control-oriented approach shown earlier can be simplified to the following:
TimeSheetViewModel vm = (TimeSheetViewModel)LayoutRoot.DataContext;
vm.UpdateRowTotals((TimeSheetViewRow)TimeSheetDataGrid.SelectedItem);
The code that performs the actual calculations is shown next. It’s located in a ViewModel class named TimeSheetViewModel that exposes an UpdateRowTotals method. The ViewModel class contains all of the properties that are bound to the form (called the View) that the end user sees. I used reflection to simplify the calculation process since each property has a set naming convention (MondayHours, MondayQuantity, TuesdayHours, TuesdayQuantity, etc.) but I certainly could’ve written code to add all of the Monday – Sunday quantity and hours properties together more explicitly.
public void UpdateRowTotals(TimeSheetViewRow row)
{
decimal qty = 0M;
decimal hours = 0M;
Type t = typeof(TimeSheetViewRow);
foreach (PropertyInfo prop in t.GetProperties())
{
object val = prop.GetValue(row, null);
decimal decimalVal = (val==null)
?0.00M:decimal.Parse(val.ToString());
if (prop.Name.EndsWith("Hours")) hours += decimalVal;
if (prop.Name.EndsWith("Quantity")) qty += decimalVal;
}
row.HoursTotal = hours;
row.QuantityTotal = qty;
}
As the HoursTotal and QuantityTotal properties change the corresponding TextBlock controls that they’re bound to will be updated automatically.
You can see that with Silverlight you can focus on working with data as opposed to working with controls by using the built-in support for TwoWay binding. Controls are there to present the data to the end user and allow them to change values. By focusing less on controls we can reduce the amount of code that has to be written in many cases. It takes a little getting used to especially if you’re used to the ASP.NET control-centric approach, but once the concept clicks it really changes how you think about writing data-oriented code.

Posted by dwahlin |
Sat, 20 Jun 2009 22:43:29 GMT |
Comments (10)
We’re currently working on a client application that captures time sheet information and needed the ability to completely customize the look and feel of DataGrid headers. Fortunately, that’s fairly straightforward to do in Silverlight 3 (or Silverlight 2 for that matter). Nearly all of the columns being used are DataGridTemplateColumn types (although that’s not required to customize headers) and changing the header is accomplished by using the HeaderStyle property as shown next:
<data:DataGridTemplateColumn Header="Tue" HeaderStyle="{StaticResource TimeSheetDayHeaderStyle}">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding TuesdayQuantity}" Style="{StaticResource TimeSheetTextBoxStyle}"/>
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1" />
<TextBox Text="{Binding TuesdayHours}" Margin="2,0,0,0" Style="{StaticResource TimeSheetTextBoxStyle}"/>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
The tricky part of creating a custom header for the first time is knowing what the style’s TargetType should be. You’ll need to reference the System.Windows.Controls.Primitives namespace as shown next as it contains the DataGridColumnHeader type:
xmlns:dataprimitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
Once that’s available you can define the styles. We needed stacked headers that displayed each day of the week as well as hours and quantity under the day and ended up going with the following styles (note that Silverlight 3’s new BasedOn feature is being used). The TimeSheetDayHeaderStyle style defines the stacked headers using a Grid control.
<Style x:Key="DataGridBaseHeaderStyle" TargetType="dataprimitives:DataGridColumnHeader">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style x:Key="TimeSheetTotalsHeaderStyle" TargetType="dataprimitives:DataGridColumnHeader"
BasedOn="{StaticResource TimeSheetDayHeaderStyle}">
<Setter Property="Foreground" Value="#FFFF0000"/>
</Style>
<Style x:Key="TimeSheetDayHeaderStyle" TargetType="dataprimitives:DataGridColumnHeader"
BasedOn="{StaticResource DataGridBaseHeaderStyle}">
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="SeparatorBrush" Value="#FFC9CACA"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="Root">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundRectangle"
Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#7FFFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#CCFFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#F2FFFFFF"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundRectangle"
Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[0].Color" To="#D8FFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#C6FFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#8CFFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#3FFFFFFF"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SortStates">
<VisualState x:Name="Unsorted"/>
<VisualState x:Name="SortAscending" />
<VisualState x:Name="SortDescending" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="BackgroundRectangle" Fill="#FF1F3B53" Stretch="Fill" Grid.ColumnSpan="2"/>
<Rectangle x:Name="BackgroundGradient" Stretch="Fill" Grid.ColumnSpan="2">
<Rectangle.Fill>
<LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">
<GradientStop Color="#FCFFFFFF" Offset="0.015"/>
<GradientStop Color="#F7FFFFFF" Offset="0.375"/>
<GradientStop Color="#E5FFFFFF" Offset="0.6"/>
<GradientStop Color="#D1FFFFFF" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="1" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="1" />
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<!-- Row 0 -->
<ContentPresenter Content="{TemplateBinding Content}"
VerticalAlignment="Center" HorizontalAlignment="Center"
Grid.ColumnSpan="3" />
<!-- Row 1 -->
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Height="1"
Visibility="Visible" Grid.Row="1" Grid.ColumnSpan="3" />
<!-- Row 2 -->
<ContentPresenter Content="Qty" Grid.Row="2" VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1"
Visibility="Visible" Grid.Row="2" Grid.Column="1" />
<ContentPresenter Content="Hours" Grid.Row="2" Grid.Column="2"
VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
<Rectangle x:Name="VerticalSeparator" Fill="#FFC9CACA"
VerticalAlignment="Stretch" Width="1" Visibility="Visible"
Grid.Row="1" Grid.Column="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The end result is a grid with headers that are just like the client wanted:

For more information about onsite, online and video training, mentoring and consulting solutions for .NET, SharePoint or Silverlight please visit http://www.thewahlingroup.com/.

Posted by dwahlin |
Thu, 11 Jun 2009 18:01:39 GMT |
Comments (6)
99% of the projects my company works on for clients are deployed internally to an enterprise environment managed by a company. We rarely have to worry about deploying to a shared hosting environment which is nice. One of the client projects we finished up recently based on ASP.NET MVC, PLINQO and jQuery was deployed to a shared hosting provider for the client and nothing worked initially. Everything worked locally of course even if we hit the hosting provider’s database from the staging server code base. It’s never fun to have development or staging working with the same code failing in production.
After researching the error more we thought it related to data access permissions since we were getting null reference errors as collections were converted to lists (data wasn’t being returned from the database). Finding the root cause of errors in this situation is like finding a needle in a haystack since you can’t attach a debugger, can’t run SQL Profiler or do anything aside from looking at log files, make code changes, FTP the files to the server and see what happens.
After thinking through it more and running several isolated tests we realized it wasn’t a data access error that we were getting (even though that’s what logs led us to believe). With a little research I came across the good old <trust> element that can be used in web.config. It’s not something that I personally have had to use for over 5 years so I completely forgot about it. By placing the following in web.config you can simulate a medium trust environment:
<system.web>
<trust level="Medium"/>
</system.web>
Changing the trust level made the staging environment fail with the same error shown on the shared host (which was awesome….rarely am I excited to get an error). We found out that a PLINQO assembly we were using didn’t allow partially trusted calls so we located the source for that assembly, recompiled it to allow partially trusted callers and everything was fine.
Bottom Line/Lesson Learned: If you’re deploying to a medium trust hosting environment you’d be wise to set the trust level to Medium up front to save time and energy down the road.

Posted by dwahlin |
Tue, 09 Jun 2009 20:11:25 GMT |
Comments (5)
I always love visiting California and get a chance to visit again on behalf of INETA to speak at 3 .NET user groups next week. If you’re in the Southern California area and are interested in Silverlight 3 or ASP.NET MVC swing on by. Here’s the schedule and information about the talks.
ASP.NET MVC vs. Web Forms
Microsoft's release of the ASP.NET MVC framework adds another option that developers can choose when building web applications. In this talk Dan Wahlin will compare ASP.NET MVC to ASP.NET Web Forms, explain the pros and cons of each and share lessons learned while building a client application based on ASP.NET MVC. Demonstrations of using the ASP.NET MVC framework will be provided and benefits it provides such as automatic property mapping, simplified AJAX integration as well as testing will be discussed. If you're interested in learning more about ASP.NET MVC and some of the benefits it brings then this event is for you.
Download Slides and Samples
Getting Started with Silverlight 3
Interested in learning more about Silverlight but don't know where to start? In this session Dan Wahlin will demonstrate how to create Silverlight 3 applications from scratch using a learn by example approach. Topics covered include XAML, controls, styles and templates as well as data binding. Different techniques for accessing data from within a Silverlight application will also be discussed as well as several Silverlight 3 features such new controls, element to element binding, out of the browser mode, and more.
Download Slides and Samples
Getting Started with Silverlight 3

Interested in learning more about Silverlight but don't know where to start? In this session Dan Wahlin will demonstrate how to create Silverlight 3 applications from scratch using a learn by example approach. Topics covered include XAML, controls, styles and templates as well as data binding. Different techniques for accessing data from within a Silverlight application will also be discussed as well as several Silverlight 3 features such new controls, element to element binding, out of the browser mode, and more.
Download Slides and Samples
For more information about onsite, online and video training, mentoring and consulting solutions for .NET, SharePoint or Silverlight please visit http://www.thewahlingroup.com/.

Posted by dwahlin |
Fri, 29 May 2009 18:20:24 GMT |
Comments (4)
Looking for more?
Browse all of the entries in the Dan Wahlin's WebLog archives