c# - 动态设置 DataGridColumn 的单元格内容

标签 c# wpf mvvm wpfdatagrid

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

UIField 包含许多 BindingProperty 属性,允许我指定 Control 将绑定(bind)到的属性。

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 中,我模板化了 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>

然后我在 View 模型中创建这些字段并将它们添加到集合中。然后,UI 使用 ItemsControl

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

这对于普通字段非常有用,但我遇到了 DataGrid 的问题。

最好,我希望能够绑定(bind)已扩展的 UIField 集合,为 DataGrid 的列提供 Header 属性,然后绑定(bind)模板这些到 DataGridColumn,但您不能在 DataTemplate 中有 DataGridColumn

目前,我已经创建了一个界面,为我提供标题、DataGridColumn 类型和每个单元格中应包含的内容:

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

    Type ColumnType { get; }

    UIField CellContents { get; }
}

然后我使用以下方法将它们绑定(bind)到 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 时,我只获取分配给所选 TypeDataTemplate

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

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

最佳答案

看来你想重新发明轮子。 WPF DataGrid 已经很好地处理基本的数据类型,如字符串、 bool 、整数。但是您会有自己的理由。

我认为如果您想采用动态方法,您需要设置 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 事件来覆盖列的创建。也许您想派生您的 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;
    }
}

关于c# - 动态设置 DataGridColumn 的单元格内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40608915/

相关文章:

c# - 使用数据绑定(bind)访问 DataTemplate 中的 WPF 组件

c# - 如何防止多次连续按下 'Enter'键

c# - 为操作绑定(bind) Listview SelectedItems

c# - 如何在JSON数组中按键获取元素

c# - 根据特定条件更改按钮格式

c# - INotifyCollectionChanged : Added item does not appear at given index '0'

c# - WPF - MVVM 架构(Visual Studio 解决方案和项目)

c# - XamlCompiler 错误 WMC1006 : Cannot resolve Assembly or Windows Metadata file 'XXX.YYY.ZZZ'

C# LINQ 按基类分组

wpf - 如何防止在第一次单击时选择整个DataGrid行?