2016-11-15 4 views
3

Я создаю динамическое приложение пользовательского интерфейса в WPF MVVM, которое связывает области пользовательского интерфейса с коллекцией моего настраиваемого класса UIField.Динамически установить содержимое ячейки DataGridColumn

UIField содержит ряд объектов BindingProperty, которые позволяют мне указать свойство, которое свяжет Control.

public abstract class UIField : BaseObject 
{ 
    BindingProperty property, isReadonly = new BindingProperty(false, false); 
    object dataContext; 

    public BindingProperty Property 
    { 
     get { return property; } 
     set { SetProperty(ref property, value,() => Property); } 
    } 
    public BindingProperty IsReadonly 
    { 
     get { return isReadonly; } 
     set { SetProperty(ref isReadonly, value,() => IsReadonly); } 
    } 
    public object DataContext 
    { 
     get { return dataContext; } 
     set { SetProperty(ref dataContext, value,() => DataContext); } 
    } 
} 

В ResourceDictionary I шаблон производных классов UIField

<DataTemplate DataType="{x:Type fields:TextBoxField}"> 
    <TextBox metro:TextBoxHelper.Watermark="{e:IndirectBinding Watermark}" 
      metro:TextBoxHelper.UseFloatingWatermark="{e:IndirectBinding UseFloatingWatermark}" 
      IsReadOnly="{e:IndirectBinding IsReadonly}" 
      Text="{e:IndirectBinding Property}"     
       /> 
</DataTemplate> 

А потом я создаю эти поля в модели представления и добавить их в коллекцию. UI затем связывается с коллекцией с помощью ItemsControl

new TextBoxField() 
{ 
    DataContext = this, 
    Property = new BindingProperty(() => ((SalesOrder)Order).SecondSalesReference), 
    Watermark = new BindingProperty("Customer Ref. Number", false), 
    UseFloatingWatermark = new BindingProperty(true, false) 
} 

Это работает отлично подходит для нормальных полей, но я бегу в проблему с DataGrid.

Предпочтительно, я хотел бы быть в состоянии связать коллекцию UIField с, которые были продлены, чтобы обеспечить свойство заголовка для столбцов DataGrid, а затем шаблон их к DataGridColumn с, но вы не можете иметь DataGridColumn в a DataTemplate.

В настоящее время я создал интерфейс, который дает мне заголовок, DataGridColumn типа и содержимое, которое должно быть в каждой клетке:

public interface IDataGridField 
{ 
    string Header { get; set; } 

    Type ColumnType { get; } 

    UIField CellContents { get; } 
} 

Я тогда связываю этот друг DataGrid сек столбцов с помощью:

public class DataGridHelper 
{ 
    public static readonly DependencyProperty BindableColumnsProperty = DependencyProperty.RegisterAttached("BindableColumns", typeof(ThreadSafeCollection<IDataGridField>), typeof(DataGridHelper),new UIPropertyMetadata(null, BindableColumnsPropertyChanged)); 
    static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) 
    { 
     var dataGrid = source as DataGrid; 
     var columns = e.NewValue as ThreadSafeCollection<IDataGridField>; 
     dataGrid.Columns.Clear(); 
     if (columns == null) 
      return; 

     foreach (var column in columns) 
     { 
      var newColumn = Utility.CreateInstance(column.ColumnType) as DataGridColumn; 
      newColumn.Header = column.Header; 
      dataGrid.Columns.Add(newColumn); 
     } 
    } 
    public static void SetBindableColumns(DependencyObject element, ThreadSafeCollection<IDataGridField> value) 
    { 
     element.SetValue(BindableColumnsProperty, value); 
    } 
    public static ThreadSafeCollection<IDataGridField> GetBindableColumns(DependencyObject element) 
    { 
     return (ThreadSafeCollection<IDataGridField>)element.GetValue(BindableColumnsProperty); 
    } 
} 

Это даст мне колонки с заголовками, но не содержит содержания в DataGridCell.

Как настроить содержимое каждой ячейки в DataGridColumn?

EDIT 1 (Progress):

я понял, что DataGridColumn генерироваться должен быть DataGridTemplateColumn так это открывает несколько дополнительных свойств.

Я изменил IDataGridField, изменив свойство UIField на Type.

Затем при создании нового DataGridColumn я просто получить DataTemplate назначенные выбранные Type

foreach (var column in columns) 
{ 
    var newColumn = new DataGridTemplateColumn(); 
    var key = new DataTemplateKey(column.CellContentType); 
    var template = Application.Current.FindResource(key) as DataTemplate; 
    newColumn.CellTemplate = template; 
    newColumn.Header = column.Header; 
    dataGrid.Columns.Add(newColumn); 
} 

Я думаю, что единственная проблема сейчас применяют привязки.

ответ

0

Кажется, вы хотите изобрести колесо. WPF DataGrid обрабатывает базовые DataTypes, такие как string, bool, int уже достаточно хорошо. Но у вас будут причины.

Я думаю, что если вы хотите использовать динамический подход, вы хотите установить CellTemplate или/и CellEditingTemplate вместо создания «правильного» типа столбца.Это также будет работать для более сложных типов данных.

Это просто XAML, но он должен показывать направление вы могли бы пойти:

<DataGrid ItemsSource="{Binding}"> 
    <DataGrid.Columns> 
     <DataGridTemplateColumn> 
      <DataGridTemplateColumn.CellTemplate> 
       <DataTemplate> 
         <TextBox metro:TextBoxHelper.Watermark="{e:IndirectBinding Watermark}" 
      metro:TextBoxHelper.UseFloatingWatermark="{e:IndirectBinding UseFloatingWatermark}" 
      IsReadOnly="{e:IndirectBinding IsReadonly}" 
      Text="{e:IndirectBinding Property}"     
       /> 
       </DataTemplate> 
      </DataGridTemplateColumn.CellTemplate> 
     </DataGridTemplateColumn> 
    </DataGrid.Columns> 
</DataGrid> 

Вы также можете использовать AutoGeneratingColumn событие DataGrid, чтобы отменить создание столбцов. Может быть, вы хотите получить свой CustomDataGrid?

private void DataGrid_AutoGeneratingColumn(object sender, System.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e) 
{ 
    //check for the type 
    if (e.PropertyType == typeof(TextField)) 
    { 
     DataGridTemplateColumn newDataGridTemplateColumn = new DataGridTemplateColumn(); 
     e.Column = newDataGridTemplateColumn; 

     //do some datatemplate voodoo, maybe you want to load it from resources or similar 
     StringReader stringReader = new StringReader(
@"<DataTemplate 
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""> 
    <" + typeof(ComboBox).Name + @" Text=""{Binding " + "Text" + @"}""/> 
</DataTemplate>"); 
     XmlReader xmlReader = XmlReader.Create(stringReader); 
     DataTemplate template = XamlReader.Load(xmlReader) as DataTemplate; 

     newDataGridTemplateColumn.CellTemplate = template; 
    } 
} 
+0

Проблема в том, что это даст мне тот же DataTemplate для одного и того же типа. Мне нужно указать разные свойства (IsReadonly и т. Д.). Я думаю, что мой лучший выбор может быть переопределением DataGridTemplateColumn и GenerateElement() – Blinx

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