我正在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
我认为现在唯一的问题是应用绑定。
答案 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;
}
}