注意:我正在使用 MVVM Light Toolkit 和 MahApps.Metro。
我已经检查了答案,但似乎它们中的任何一个都与我的问题无关。
我有一个应该动态创建列和标题的网格。列数和值无法查看,行数无法查看。
列、行和行中的数据代表一个数据库表。所有数据都存在于 ViewModel 中。
我有一个 ObservableCollection<ServerRow> ServerRows;
在我的 View 模型中。
Server Row 对象是一个看起来像这样的模型:
public class ServerRow : ObservableObject
{
private ObservableCollection<ServerColumn> _columns;
public ObservableCollection<ServerColumn> Columns
{
get { return _columns; }
set { Set(() => Columns, ref _columns, value); }
}
}
这是
ServerColumn
类(class) : public class ServerColumn : ObservableObject
{
private string _name;
private string _type;
private string _value;
public string Name
{
get { return _name; }
set { Set(() => Name, ref _name, value); }
}
public string Type
{
get { return _type; }
set { Set(() => Type, ref _type, value); }
}
public string Value
{
get { return _value; }
set { Set(() => Value, ref _value, value); }
}
}
想法是将 DataGrid 绑定(bind)到
ObservableCollection<ServerRow> ServerRows;
,然后根据 ServerRow
生成列具有 ServerColumns
的对象其中有 Name
(应该是列的标题),Type
作为列数据的数据类型,Value
作为应该在每一行/列中表示的值。我的 XAML 非常简单(因为它不完整,当然 - 不工作)
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding ServerRows}"/>
如何正确编写 XAML 以实现我想要做的事情?
这是结果,这是有道理的,因为 Grid 试图在单个 Column 中显示对象集合并调用它的
ToString()
方法。最佳答案
我以前也遇到过这个问题。
如果你看看这里做了什么:
https://github.com/taori/WMPR/blob/0a81bc6a6a4c6fc36edc4cbc99f0cfa8a2b8871c/src/WMPR/WMPR.Client/ViewModels/Sections/ReportEvaluationViewModel.cs#L503
您将可迭代集合提供为 ObservableCollection<object>
当底层结构实际上是 DynamicGridCell 类型时,它使用 DynamicGridCellDescriptor 可以在
动态网格单元:
public class DynamicGridCell : DynamicObject, ICustomTypeDescriptor, IDictionary<string, object>
{
private readonly Dictionary<string, object> _values = new Dictionary<string, object>();
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return new AttributeCollection();
}
string ICustomTypeDescriptor.GetClassName()
{
return nameof(DynamicGridCell);
}
string ICustomTypeDescriptor.GetComponentName()
{
return null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return null;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return null;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return null;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return null;
}
private PropertyDescriptor[] CreatePropertyDescriptors()
{
var result = new List<PropertyDescriptor>();
foreach (var pair in _values)
{
result.Add(new DynamicGridCellDescriptor(pair.Key));
}
return result.ToArray();
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
var result = new PropertyDescriptorCollection(CreatePropertyDescriptors());
return result;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
var result = new PropertyDescriptorCollection(CreatePropertyDescriptors());
return result;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public IEnumerator GetEnumerator()
{
return _values.GetEnumerator();
}
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
return _values.GetEnumerator();
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
{
_values.Add(item.Key, item.Value);
}
void ICollection<KeyValuePair<string, object>>.Clear()
{
_values.Clear();
}
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
return _values.Contains(item);
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
if (_values.ContainsKey(item.Key))
{
_values.Remove(item.Key);
return true;
}
return false;
}
public int Count => _values.Count;
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
public bool ContainsKey(string key)
{
return _values.ContainsKey(key);
}
public void Add(string key, object value)
{
_values.Add(key, value);
}
bool IDictionary<string, object>.Remove(string key)
{
return _values.Remove(key);
}
public bool TryGetValue(string key, out object value)
{
return _values.TryGetValue(key, out value);
}
public object this[string key]
{
get { return _values[key]; }
set
{
if (_values.ContainsKey(key))
{
_values[key] = value;
}
else
{
_values.Add(key, value);
}
}
}
public ICollection<string> Keys => _values.Keys;
public ICollection<object> Values => _values.Values;
}
动态网格单元描述符
public class DynamicGridCellDescriptor : PropertyDescriptor
{
public DynamicGridCellDescriptor(string name) : base(name, null)
{
}
public override bool CanResetValue(object component)
{
return true;
}
public override object GetValue(object component)
{
return ((DynamicGridCell) component)[Name];
}
public override void ResetValue(object component)
{
((DynamicGridCell) component)[Name] = null;
}
public override void SetValue(object component, object value)
{
((DynamicGridCell) component)[Name] = value;
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override Type ComponentType => typeof(DynamicGridCell);
public override bool IsReadOnly => false;
public override Type PropertyType => typeof(object);
}
只需确保您绑定(bind)到的属性的类型为
ObservableCollection<object>
无论如何-否则对我来说自动网格列生成不起作用。
关于wpf - 使用 WPF 和 MVVM 创建动态行和列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43541742/