动态设置DataGridColumn的单元格内容

时间:2016-11-15 11:37:43

标签: c# wpf mvvm wpfdatagrid

我正在WPF MVVM中创建一个动态UI应用程序,它将UI区域绑定到我的自定义UIField类的集合。

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的列提供Header属性,然后将这些属性模板化为DataGridColumn但是你不能在DataGridColumn中拥有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创建的每个单元格的内容?

编辑1(进度?):

我意识到生成的DataGridColumn必须是DataGridTemplateColumn,因此这会打开更多属性。

我修改了IDataGridField,将UIField属性更改为Type

然后,在生成新的DataGridColumn时,我只是将[{1}}分配给选定的DataTemplate

Type

我认为现在唯一的问题是应用绑定。

1 个答案:

答案 0 :(得分:0)

似乎你想重新发明轮子。 WPF DataGrid已经很好地处理了像string,bool,int这样的基本DataType。但是你有理由说明原因。

我认为,如果您想采用动态方法,则需要设置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>

您还可以使用datagrid的AutoGeneratingColumn事件来覆盖列的创建。也许你想派生你的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;
    }
}