wpf - 使用 WPF 和 MVVM 创建动态行和列

标签 wpf xaml mvvm data-binding mvvm-light

注意:我正在使用 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()方法。

enter image description here

最佳答案

我以前也遇到过这个问题。

如果你看看这里做了什么:

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/

相关文章:

c# - 无法在运行时找到静态资源,即使设计师可以看到它

.net - 如何调试堆栈跟踪中没有您的代码的 WPF 异常?

c# - 翻转/镜像图像

c# - 如何: Create a DockPanel in C# (WPF)

wpf - 如何使用 MVVM 创建定时弹出窗口?

.net - WPF WBA (XBAP) 与 Silverlight

c# - ListView 无法使用 Xamarin Forms 正确呈现

WPF:列表框单击并拖动选择其他项目

c# - 单击按钮MVVM时获取commandparameter值

c# - TabItem/TabControl 中的 DataGridTextColumn 标题绑定(bind)问题