Ошибка привязки данных Silverlight - PullRequest
0 голосов
/ 25 июля 2011

У меня есть GridView, который имеет RowDetail. Я хочу, чтобы каждый раз, когда пользователь нажимал на строки, получал некоторую информацию из базы данных, я использую Telerik GridView. Обычно это невозможно, или, по крайней мере, я не знаю как, потому что контекст RowDetail связан напрямую с сеткой DataContext, и я хочу больше, чем то, что содержит GridRow. Я обнаружил, что, возможно, я могу установить для RowDetailTemplate DataContext значение UserControl, указав имя UserControl, чтобы я мог ссылаться на RowDetail для другой модели. Мой код что-то вроде этого

    <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>

Но на этот раз я получаю это исключение

{Error: System.Exception: BindingExpression_CannotFindElementName}

Любой совет будет полезен. С наилучшими пожеланиями.

Ответы [ 2 ]

3 голосов
/ 23 августа 2011

Я даже упростил принятое решение.Он использует трюк, заключающийся в том, что из DataTemplates вы можете ссылаться на статические ресурсы.А в статических ресурсах вы можете использовать ElementName в связывании.

  1. Создать новый элемент управления:

    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. Поместить его в статические ресурсыDataGrid или его родительский элемент управления и ссылка на него через StaticResource:

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

(в шаблоне столбца:)

    <DataTemplate>
        <Grid DataContext={Binding Element.DataContext,Source={StaticResource Proxy}} />
    </DataTemplate>
3 голосов
/ 25 июля 2011

Причина этого заключается в том, что столбцы WPF и Silverlights DataGrid находятся за пределами логического дерева и, таким образом, делают невозможным использование источника привязки, указанного в ElementName, который является общим при обращении к свойствам ViewModel, таким как команды из столбцов шаблона DataGrid. Для получения дополнительной информации об этой проблеме см .: http://blogs.msdn.com/b/jaimer/archive/2008/11/22/forwarding-the-datagrid-s-datacontext-to-its-columns.aspx

Класс ниже действует как клей между колонной и окружающим миром. Он был написан для встроенной в Silverlight DataGrid, но его должно быть достаточно легко адаптировать для Telerik Grid. Это можно использовать так:

<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;
  }
}
...