Silverlight Data and Controls Series
- Silverlight 4 Hands On Lab
- Silverlight 4 using MVVM Pattern, RIA Services and Custom Domain Service
- Silverlight Custom Authentication Domain Service with RIA Services
- Binding ComboBox and AutoComplete Controls in Silverlight DataGrid
- RIA Service Domain Service Method Life-Cycle
- Silverlight Localization and Resource Files
- Deep Dive into Custom RIA Service and Transactions
- Bind a Silverlight DataGrid Column to the User Control DataContext
- Project RIA Methods
- Silverlight RIA Data Validation Best Practices
Background
I recently had to come up with a solution for a problem that was bothering me for some time. The issue I was how to access the user control’s DataContext from a Silverlight column. I was able to find the exact solution I was looking for by using the code in Dan Wahlin’s genius blog posting (http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx).
The specific issue was I needed to enable and disable specific columns in a grid based on the form’s state; and not the state of object bound to the.
Take a look at the code exerts from a xaml control below:
- You will see there is a DataGrid bound to a list of Employees that is on my ViewModel.
- I then added autocomplete column to the grid. I bounded the Text property to the Employee’ s EmployeeCode property.
- I then tried to bind to the ViewModel’s IsEmployeesIsEnabled property to the IsEnabled property of autocomplete column.
That solution will never work. You will never get a compile or runtime error however because EmployeesIsEnabled is not a property of the Employee object in the row, the binding will never work.
<!--User control-->
<UserControl x:Class="OCS.Controls.Canvass.DetailsInfo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:local="clr-namespace:OCS.Converters" xmlns:vm="clr-namespace:OCS.ViewModels" d:DesignHeight="450" d:DesignWidth="1024" x:Name="DetailsInfoControl">
<!--Data Grid-->
<sdk:DataGrid AutoGenerateColumns="False" Height="240" Name="dgDetailsEmployees" Width="Auto" BorderThickness="0"
ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee, Mode=TwoWay}">
<!--Data Grid Column-->
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<sdk:AutoCompleteBox Name="acbEmployeeCode"
MinimumPrefixLength="0" MinimumPopulateDelay="0" IsTextCompletionEnabled="True" ItemsSource="{Binding EmployeeCodes, Source={StaticResource oLookups}}" ValueMemberBinding="{Binding Abbreviation}" Text="{Binding EmployeeCode, Mode=TwoWay, ValidatesOnDataErrors=True,NotifyOnValidationError=True}" IsEnabled="{Binding EmployeesIsEnabled}">
Solution
To solve this use the code in this blog (http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx). Here is the code so you do not have to go over there but thank you very much Nick!!!
using System.Windows.Controls;
using System.Windows.Data;
namespace JobPlan.Controls
{
public class DataContextProxy : FrameworkElement
{
public DataContextProxy()
{
this.Loaded += new RoutedEventHandler(DataContextProxy_Loaded);
}
void DataContextProxy_Loaded(object sender, RoutedEventArgs e)
{
Binding binding = new Binding();
if (!String.IsNullOrEmpty(BindingPropertyName))
{
binding.Path = new PropertyPath(BindingPropertyName);
}
binding.Source = this.DataContext;
binding.Mode = BindingMode;
this.SetBinding(DataContextProxy.DataSourceProperty, binding);
}
public Object DataSource
{
get { return (Object)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(Object), typeof(DataContextProxy), null);
public string BindingPropertyName { get; set; }
public BindingMode BindingMode { get; set; }
}
}
Solution
Then make the following modifications:
- I added a new resource to the xaml that creates the DataContextProxy.
- Then I modified the IsEnabled property of the autocomplete column to be bound to EmployeesIsEnabled property of the ViewModel set to the user control’s DataContext.
Voila – it works – the column will be enabled and disabled based on the form state and not the grid row.
<UserControl x:Class="OCS.Controls.Canvass.DetailsInfo" xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml xmlns:d=http://schemas.microsoft.com/expression/blend/2008 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" xmlns:sdk=http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk xmlns:local="clr-namespace:OCS.Converters" xmlns:vm="clr-namespace:OCS.ViewModels" d:DesignHeight="450" d:DesignWidth="1024">
<UserControl.Resources><vm:DataContextProxy x:Key="dataContextProxy" /></UserControl.Resources>
<!--Data Grid-->
<sdk:DataGrid AutoGenerateColumns="False" Height="240" Name="dgDetailsEmployees" Width="Auto" BorderThickness="0"
ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee, Mode=TwoWay}">
<!--Data Grid Column-->
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<sdk:AutoCompleteBox Name="acbEmployeeCode"
MinimumPrefixLength="0" MinimumPopulateDelay="0" IsTextCompletionEnabled="True" ItemsSource="{Binding EmployeeCodes, Source={StaticResource oLookups}}" ValueMemberBinding="{Binding Abbreviation}" Text="{Binding EmployeeCode, Mode=TwoWay, ValidatesOnDataErrors=True,NotifyOnValidationError=True}" IsEnabled="{Binding Source={StaticResource dataContextProxy},Path=DataSource.EmployeesIsEnabled}">
No comments:
Post a Comment