Friday, February 1, 2013

Silverlight DataBinding Error

Silverlight DataBinding Error

I've a GridView which has RowDetail. I want to each time user clicks on the rows get some detail from database, I use Telerik GridView. In normal way it's not possible or at least I don't know how, because RowDetail context binded directly to the grid DataContext, what I want is more than what GridRow contains it. What I found is maybe I can set RowDetailTemplate DataContext to UserControl by naming the UserControl so I can reference RowDetail to other model. My code is something like this

    <UserControl     x:Name="mainPageView"     x:Class="Project.Client.TestView"     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"      xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"      mc:Ignorable="d"     d:DesignHeight="300" d:DesignWidth="400">      <UserControl.Resources>         <DataTemplate x:Key="ContactRowDetailTemplate" >             <Grid Background="Transparent"                 DataContext="{Binding DataContext.ContactStatModel,                  ElementName=mainPageView,Mode=OneTime}">                 <Grid.RowDefinitions>                     <RowDefinition Height="28" />                 </Grid.RowDefinitions>                 <Grid.ColumnDefinitions>                     <ColumnDefinition Width="Auto" />                     <ColumnDefinition Width="Auto" />                     <ColumnDefinition Width="Auto" />                 </Grid.ColumnDefinitions>                 <TextBlock Text="Sent SMS Count" Grid.Column="0" Grid.Row="0" />                 <TextBlock Text=":" Grid.Column="1" Grid.Row="0" />                 <TextBlock Text="{Binding SMSCount}" Grid.Column="2" Grid.Row="0" />              </Grid>         </DataTemplate>     </UserControl.Resources>      <telerik:RadGridView           x:Name="gridView"         AutoGenerateColumns="False" Height="Auto" Grid.Row="3"         ItemsSource="{Binding VOutboxList, Mode=TwoWay}"         SelectedItem="{Binding VOutboxModel, Mode=TwoWay}"         RowDetailsTemplate="{StaticResource ContactRowDetailTemplate}"         LoadingRowDetails="gridView_LoadingRowDetails">         <telerik:RadGridView.Columns>             <telerik:GridViewDataColumn UniqueName="FirstName"  Header="First Name" Width="150" />             <telerik:GridViewDataColumn UniqueName="LastName" Header="Last Name" Width="150" />         </telerik:RadGridView.Columns>     </telerik:RadGridView>  </UserControl> 

But this time I get this exception

{Error: System.Exception: BindingExpression_CannotFindElementName} 

Any advice will be helpful. Best Regards.

Answers & Comments...

Answer: 1

The reason for this is that WPF's and Silverlights DataGrid columns live outside the logical tree and thus make it impossible to use a binding source specified using ElementName which is common when referencing ViewModel properties such as commands from within DataGrid Template Columns. For more information about this problem see: http://blogs.msdn.com/b/jaimer/archive/2008/11/22/forwarding-the-datagrid-s-datacontext-to-its-columns.aspx

The class below act's as glue between the column and the world around it. It was written for Silverlight's built-in DataGrid but should be easy enough to adapt it for the Telerik Grid. It can be used like this:

<DataTemplate x:Key="ContactRowDetailTemplate" >   <Grid Background="Transparent"     DataContext="{Binding ParentDataGrid.DataContext.ContactStatModel,      ElementName=shim,Mode=OneTime}">         <Shims:DataGridShim x:Name="shim"/>     <Grid.RowDefinitions>       <RowDefinition Height="28" />     </Grid.RowDefinitions>     <Grid.ColumnDefinitions>       <ColumnDefinition Width="Auto" />       <ColumnDefinition Width="Auto" />       <ColumnDefinition Width="Auto" />     </Grid.ColumnDefinitions>     <TextBlock Text="Sent SMS Count" Grid.Column="0" Grid.Row="0" />     <TextBlock Text=":" Grid.Column="1" Grid.Row="0" />     <TextBlock Text="{Binding SMSCount}" Grid.Column="2" Grid.Row="0" />   </Grid> </DataTemplate>  public class DataGridShim : FrameworkElement {   /// <summary>   /// Initializes a new instance of the <see cref="DataGridShim"/> class.   /// prepares the ParentDataGrid property for consumption by sibling elements in the DataTemplate   /// </summary>   public DataGridShim()   {     Loaded += (s, re) =>     {       ParentDataGrid = GetContainingDataGrid(this);     };   }    /// <summary>   /// Gets or sets the parent data grid.   /// </summary>   /// <value>   /// The parent data grid.   /// </value>   public DataGrid ParentDataGrid { get; protected set; }    /// <summary>   /// Walks the Visual Tree until the DataGrid parent is found and returns it   /// </summary>   /// <param name="value">The value.</param>   /// <returns>The containing datagrid</returns>   private static DataGrid GetContainingDataGrid(DependencyObject value)   {     if (value != null)     {       DependencyObject parent = VisualTreeHelper.GetParent(value);       if (parent != null)       {         var grid = parent as DataGrid;         if (grid != null)           return grid;          return GetContainingDataGrid(parent);       }        return null;     }      return null;   } } 
by : Oliver Weichholdhttp://stackoverflow.com/users/88513

Answer: 2

I've even simplified the accepted solution. It uses the trick that from DataTemplates, you can reference static resources. And in static resources, you can use ElementName in binding.

  1. Create a new control:

    public class ElementProxy : DependencyObject {     public DependencyObject Element     {         get { return (DependencyObject)GetValue(ElementProperty); }         set { SetValue(ElementProperty, value); }     }  public static readonly DependencyProperty ElementProperty =     DependencyProperty.Register("Element", typeof(DependencyObject), typeof(ElementProxy), new PropertyMetadata(null)); } 
  2. Put it into static resources of the DataGrid or its parent control and reference it through StaticResource:

    <UserControl.Resources>     <helpers:ElementProxy Element={Binding ElementName=mainPageView} x:Key="Proxy" /> </UserControl.Resources> 

(in column template:)

    <DataTemplate>         <Grid DataContext={Binding Element.DataContext,Source={StaticResource Proxy}} />     </DataTemplate> 
by : giushttp://stackoverflow.com/users/19712




No comments:

Post a Comment

Send us your comment related to the topic mentioned on the blog