2010-02-03 3 views
8

Я пытаюсь выяснить, как связать заголовок столбца и основные данные WPF DataGrid с источником данных с использованием шаблона MVVM. В результате я ищу будет выглядеть следующим образом:WPF Datagrid привязка собственных заголовков столбцов

alt text http://brian.vallelunga.com/files/datagrid-custom-headers.PNG

Я успешно стиле заголовки здесь, но я не уверен, как связать значения в заголовках. В частности, свойство IsChecked флажка, выбранный индекс в поле со списком и значение текстового поля.

Ранее я использовал простой DataTable для заполнения основных данных сетки, но мне потребуется что-то более сложное для хранения как данных сетки, так и значений для каждого столбца. Или, может быть, я могу хранить их как отдельные объекты целиком.

Итак, есть ли у кого-нибудь представление о том, как я могу снять эту привязку? Одно из ограничений заключается в том, что столбцы должны быть автоматически сгенерированы, так как я понятия не имею, какими они будут до выполнения. Приложение просто загружает форму данных в электронную таблицу Excel, и может присутствовать любое количество столбцов.

Спасибо, Brian

ответ

7

Вот что я в конечном итоге делает, чтобы использовать этот шаблон MVVM:

У меня есть два набора данных для связывания с моей точки зрения модели: один для фактических данных сетки и один для заголовков столбцов ,В настоящее время они подвергаются как два свойства:

// INotifyPropertyChanged support not shown for brevity 
public DataTable GridData { get; set; } 
public BindingList<ImportColumnInfo> ColumnData { get; set; } 

Хитрости работать с двумя разными наборами данных в сетке. Я подклассифицировал DataGrid и дал сетке дополнительный источник данных, называемый ColumnSource, как свойство зависимости. Это то, что связано с ColumnData в моей модели представления. Затем я устанавливаю заголовок каждого автоматически сгенерированного столбца на соответствующие индексированные данные в источнике данных ColumnSource. Код выглядит следующим образом:

public class ImporterDataGrid : DataGrid 
{ 
    protected override void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e) 
    { 
     base.OnAutoGeneratingColumn(e); 

     int columnIndex = this.Columns.Count; 
     var column = new ImporterDataGridColumn(); 
     column.Header = ColumnSource[columnIndex]; 
     column.Binding = new Binding(e.PropertyName) { Mode = BindingMode.OneWay }; 
     e.Column = column; 
    } 

    public IList ColumnSource 
    { 
     get { return (IList)GetValue(ColumnSourceProperty); } 
     set { SetValue(ColumnSourceProperty, value); } 
    } 

    public static readonly DependencyProperty ColumnSourceProperty = DependencyProperty.Register("ColumnSource", typeof(IList), typeof(ImporterDataGrid), new FrameworkPropertyMetadata(null)); 

} 

теперь я могу выполнить связывание в шаблонном заголовке моих колонок, нормальные данные, которые будут все связывается с данными в свойстве ColumnData моей модели представления.

UPDATE: меня попросили показать XAML для моей сетки. Это действительно просто, но вот это:

<Controls:ImporterDataGrid 
    AutoGenerateColumns="True" x:Name="previewDataGrid" 
    VerticalScrollBarVisibility="Visible" 
    HorizontalScrollBarVisibility="Visible" 
    IsReadOnly="True" 
    SelectionMode="Extended" 
    HeadersVisibility="Column" 
    ItemsSource="{Binding PreviewData}" 
    ColumnSource="{Binding PreviewColumnData}" 
    Style="{StaticResource ImporterDataGridStyle}" 
    Background="White" CanUserReorderColumns="False" CanUserResizeRows="False" 
    CanUserSortColumns="False" AlternatingRowBackground="#FFFAFAFA" AllowDrop="True" /> 

А вот ImporterColumnHeaderStyle:

<Style x:Key="ImporterDataGridColumnHeaderStyle" TargetType="{x:Type toolkit:DataGridColumnHeader}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type toolkit:DataGridColumnHeader}"> 
       <Grid> 
        <toolkit:DataGridHeaderBorder Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" IsClickable="{TemplateBinding CanUserSort}" IsHovered="False" IsPressed="False" SortDirection="{TemplateBinding SortDirection}"> 
         <Grid> 
          <CheckBox Height="16" Margin="6,6,16,0" Name="importCheckBox" IsChecked="{Binding Path=Import}" VerticalAlignment="Top">Import Column</CheckBox> 
          <StackPanel IsEnabled="{Binding Path=Import}"> 
           <ComboBox Height="24" Margin="6,29,6,0" Name="columnTypeComboBox" VerticalAlignment="Top" SelectedValue="{Binding ColumnType}" ItemsSource="{Binding Source={local:EnumList {x:Type Models:ImportColumnType}}}"> 
           </ComboBox> 
           <TextBox Height="23" Margin="6,6,6,33" Name="customHeadingTextBox" VerticalAlignment="Bottom" Text="{Binding Path=CustomColumnName}" IsEnabled="{Binding ColumnType, Converter={StaticResource ColumnTypeToBooleanConverter}}" /> 
          </StackPanel> 
          <TextBlock Height="20" Margin="6,0,6,7" Name="originalHeadingTextBlock" Text="{Binding Path=OriginalColumnName}" VerticalAlignment="Bottom" Foreground="Gray" /> 
         </Grid> 
        </toolkit:DataGridHeaderBorder> 

        <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left"> 
         <Thumb.Style> 
          <Style TargetType="{x:Type Thumb}"> 
           <Setter Property="Width" Value="8"/> 
           <Setter Property="Background" Value="Transparent"/> 
           <Setter Property="Cursor" Value="SizeWE"/> 
           <Setter Property="Template"> 
            <Setter.Value> 
             <ControlTemplate TargetType="{x:Type Thumb}"> 
              <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/> 
             </ControlTemplate> 
            </Setter.Value> 
           </Setter> 
          </Style> 
         </Thumb.Style> 
        </Thumb> 
        <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right"> 
         <Thumb.Style> 
          <Style TargetType="{x:Type Thumb}"> 
           <Setter Property="Width" Value="8"/> 
           <Setter Property="Background" Value="Transparent"/> 
           <Setter Property="Cursor" Value="SizeWE"/> 
           <Setter Property="Template"> 
            <Setter.Value> 
             <ControlTemplate TargetType="{x:Type Thumb}"> 
              <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/> 
             </ControlTemplate> 
            </Setter.Value> 
           </Setter> 
          </Style> 
         </Thumb.Style> 
        </Thumb> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 
+1

Было бы интересно увидеть все решение. Например, что такое ImporterDataGridColumn? –

+0

Вы можете игнорировать ImporterDataGridColumn и просто использовать DataGridTextColumn. Это всего лишь стандартный столбец, который я подклассифицировал, думая, что мне потребуется дополнительная функциональность, но не сделал этого. Весь код сетки находится выше. Модель представления предоставляет свойства GridData и ColumnData, перечисленные выше, с данными, заполненными где угодно. Затем XAML перехватывает эти два элемента: <Элементы управления: ImporterDataGrid AutoGenerateColumns = "True" ... ItemsSource = "{Binding GridData}", ColumnSource = "{Binding ColumnData}" /> –

+0

Не могли бы вы (кстати, пожалуйста) добавить просмотреть xaml для этого? –

0

Я определенно WPF/MVVM/нуб привязки данных, но упорно работать над этим материалом в последнее время. Я не знаю, к чему вы подключились, но сначала вам нужно установить DataContext для вашего представления. Поскольку вы используете MVVM, я предполагаю, что у вас есть ViewModel, так что это должен быть DataContext для вашего представления.

т.е. если у вас есть View создать/есть свой ViewModel, это может выглядеть примерно так:

MyViewModel vm = new MyViewModel(); 
this.DataContext = vm; 

Вы можете легко привязка вашего CheckBox, ComboBox и TextBox свойствам в вашей ViewModel. Я нашел самый простой способ сделать ваш ViewModel наследованием от базового класса viewmodel, например, one that Josh Smith wrote. Это даст вам метод внутреннего вызова, если вы хотите, чтобы ViewModel уведомил GUI о любых изменениях значений.

Если у вас есть такие свойства, как ImportColumn, LastName и LastNameText (все # свойства C, не поля, которые называют OnPropertyChanged соответственно), то ваш XAML будет выглядеть примерно так:

<CheckBox IsChecked="{Binding ImportColumn}" /> 
<ComboBox SelectedItem="{Binding LastName}" /> 
<TextBox Text="{Binding LastName Text, Mode=TwoWay}" /> 

Я надеюсь, что это помогает вы. Если нет, прокомментируйте, и я постараюсь сделать все возможное, чтобы попробовать другие вещи.

+0

Я использую Caliburn и есть мой ViewModel уже создан и работает. Проблема заключается в том, как я должен использовать привязку данных для одновременного связывания заголовков и данных сетки. Здесь я боюсь. –

0

Мы делаем что-то подобное в нашем приложении.

Что я сделал, это мой собственный тип столбца (DataGridSearchableBooleanColumn), затем я заменяю шаблон DataGridColumnHeader, я помещаю там двух презентаторов материалов. первый я привязываю к контенту (то же, что и шаблон по умолчанию), второй привязывается к столбцу. Я использую шаблон данных для столбца (у меня есть несколько из них для разных типов поиска (text, combo, boolean). Затем я добавляю дополнительные свойства в столбец, чтобы я мог привязываться к ним. Посмотрите, имеет ли этот код смысл.

<!--Style for the datagrid column headers, contains a text box for searching--> 
    <Style 
     x:Key="columnHeaderStyle" 
     TargetType="dg:DataGridColumnHeader"> 
     <Setter 
     Property="Foreground" 
     Value="#FF000000" /> 
     <Setter 
     Property="HorizontalContentAlignment" 
     Value="Left" /> 
     <Setter 
     Property="VerticalContentAlignment" 
     Value="Center" /> 
     <Setter 
     Property="IsTabStop" 
     Value="False" /> 
     <Setter 
     Property="Padding" 
     Value="1,2,1,2" /> 
     <Setter 
     Property="Template"> 
     <Setter.Value> 
      <ControlTemplate 
       TargetType="dg:DataGridColumnHeader"> 
       <Grid 
        x:Name="Root"> 
        <dg:DataGridHeaderBorder 
        Background="{TemplateBinding Background}" 
        BorderBrush="{TemplateBinding BorderBrush}" 
        BorderThickness="{TemplateBinding BorderThickness}" 
        Padding="{TemplateBinding Padding}" 
        IsClickable="{TemplateBinding CanUserSort}" 
        IsHovered="{TemplateBinding IsMouseOver}" 
        IsPressed="{TemplateBinding IsPressed}" 
        SeparatorBrush="{TemplateBinding SeparatorBrush}" 
        SeparatorVisibility="{TemplateBinding SeparatorVisibility}" 
        SortDirection="{TemplateBinding SortDirection}"> 

        <Grid 
         HorizontalAlignment="Stretch" 
         Margin="{TemplateBinding Padding}" 
         VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> 
         <Grid.Resources> 
          <DataTemplate 
           DataType="{x:Type local:DataGridSearchableBooleanColumn}"> 
           <CheckBox 
           Margin="0,5,0,0" 
           IsThreeState="True" 
           IsChecked="{Binding Path=IsChecked}" /> 
          </DataTemplate> 
         </Grid.Resources> 
         <Grid.ColumnDefinitions> 
          <ColumnDefinition /> 
          <ColumnDefinition 
           Width="Auto" /> 
         </Grid.ColumnDefinitions> 
         <Grid.RowDefinitions> 
          <RowDefinition 
           Height="19" /> 
          <RowDefinition 
           Height="Auto" /> 
         </Grid.RowDefinitions> 

         <ContentPresenter 
          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Path=Content}" /> 

         <Path 
          x:Name="SortIcon" 
          Fill="#FF444444" 
          Stretch="Uniform" 
          HorizontalAlignment="Left" 
          Margin="4,0,0,0" 
          VerticalAlignment="Center" 
          Width="8" 
          Opacity="0" 
          RenderTransformOrigin=".5,.5" 
          Grid.Column="1" 
          Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "> 
          <Path.RenderTransform> 
           <ScaleTransform 
           ScaleX=".9" 
           ScaleY=".9" /> 
          </Path.RenderTransform> 
         </Path> 
         <ContentPresenter 
          x:Name="columnHeaderContentPresenter" 
          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Column}" 
          Grid.Row="1" 
          Grid.ColumnSpan="2" 
          Margin="0,0,0,0" /> 
        </Grid> 
        </dg:DataGridHeaderBorder> 

        <Thumb 
        x:Name="PART_LeftHeaderGripper" 
        HorizontalAlignment="Left"> 
        <Thumb.Style> 
         <Style 
          TargetType="{x:Type Thumb}"> 
          <Setter 
           Property="Width" 
           Value="8" /> 
          <Setter 
           Property="Background" 
           Value="Transparent" /> 
          <Setter 
           Property="Cursor" 
           Value="SizeWE" /> 
          <Setter 
           Property="Template"> 
           <Setter.Value> 
           <ControlTemplate 
            TargetType="{x:Type Thumb}"> 
            <Border 
             Background="{TemplateBinding Background}" 
             Padding="{TemplateBinding Padding}" /> 
           </ControlTemplate> 
           </Setter.Value> 
          </Setter> 
         </Style> 
        </Thumb.Style> 
        </Thumb> 
        <Thumb 
        x:Name="PART_RightHeaderGripper" 
        HorizontalAlignment="Right"> 
        <Thumb.Style> 
         <Style 
          TargetType="{x:Type Thumb}"> 
          <Setter 
           Property="Width" 
           Value="8" /> 
          <Setter 
           Property="Background" 
           Value="Transparent" /> 
          <Setter 
           Property="Cursor" 
           Value="SizeWE" /> 
          <Setter 
           Property="Template"> 
           <Setter.Value> 
           <ControlTemplate 
            TargetType="{x:Type Thumb}"> 
            <Border 
             Background="{TemplateBinding Background}" 
             Padding="{TemplateBinding Padding}" /> 
           </ControlTemplate> 
           </Setter.Value> 
          </Setter> 
         </Style> 
        </Thumb.Style> 
        </Thumb> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
     </Setter> 
    </Style> 
+0

Это интересно, но я не уверен, что он решает мою проблему. Я также создал новый настраиваемый столбец и переопределяю OnAutoGeneratingColumn и заменяю существующий столбец своей пользовательской версией. Ваша система работает, но я думаю, вам нужно пройти через столбцы, чтобы получить данные, тогда как я пытаюсь привязать к внешнему источнику данных, используя шаблон MVVM. Если бы я мог получить индекс текущего столбца, я думаю, что у меня будет решение, использующее второй источник данных. –

+0

Это то место, где сетка падает, но не полностью работает в шаблоне mvvm, потому что коллекция столбцов только для чтения (вы можете добавить удаление, но не установить). Я подклассифицирую сетку, придав ей дополнительное свойство, являющееся интерфейсом, затем привяжите это к моей модели представления. то пусть он управляет столбцами в коде. Другое решение заключается в подклассе сетки, которая дает ему еще одну коллекцию колонок, с которой вы можете привязываться, и следить за изменениями в этом, чтобы добавлять/удалять столбцы. (тогда ваша модель может напрямую связываться) –

 Смежные вопросы

  • Нет связанных вопросов^_^