通用ViewModel类

时间:2018-10-18 19:18:51

标签: c# mvvm reflection reference

我的目标是制作一个ViewModel类,该类可以接受任何类型的对象,并将其分解为可在View中显示和修改的字段的ObservableCollection。我想象中的字段被封装在FieldViewModel中,该字段公开了“字符串FieldName”属性,“对象Value”属性以及Type。

class ViewModel : ViewModelBase
{
    public ViewModel(ref object instance)
    {
        foreach (var field in instance.GetType().GetFields())
        {
            Fields.Add(new FieldViewModel(instance, field));
        }
    }

    private ObservableCollection<FieldViewModel> _fields = new ObservableCollection<FieldViewModel>();
    public ObservableCollection<FieldViewModel> Fields
    {
        get { return _fields; }
        set
        {
            _fields = value;
            OnPropertyChanged();
        }
    }
}

上面的伪代码是尝试传达我的意图的尝试。当然,需要更多代码来确定字段是否为只读。如果无法做到这一点,您将如何建议您这样做?请告知。

注意:我的特定应用程序只关心字段,而不是属性。我没有创建要在UI中显示的数据模型。

2 个答案:

答案 0 :(得分:0)

ExpandoObjects的ObservableCollection可能能够做到这一点。它基本上是一个带有一些语法糖的字典-每个键的每次更改都具有INotifyPropertyChanged通知。其余的将不得不通过反射在原始模型的场上发散。

理想情况下,您不应该回避“强类型化”。此类的存在主要是为了允许与弱类型的Webserve等进行互操作。 Strong Typisaiton是您最大的朋友之一。只需查看此Webcomic的PHP和JavaScript示例,了解原因:http://www.sandraandwoo.com/2015/12/24/0747-melodys-guide-to-programming-languages/

答案 1 :(得分:0)

我已经找到了适合自己目的的方法。可以将其扩展为与属性一起使用,并且仍然需要对用户文本输入进行适当的错误处理。

public class ViewModel : ViewModelBase
{
    public ViewModel(object instance)
    {
        foreach (var field in instance.GetType().GetFields())
        {
            Fields.Add(new FieldViewModel(instance, field));
        }
    }

    private ObservableCollection<FieldViewModel> _fields = new ObservableCollection<FieldViewModel>();
    public ObservableCollection<FieldViewModel> Fields
    {
        get { return _fields; }
        set
        {
            _fields = value;
            OnPropertyChanged();
        }
    }
}

FieldViewModel如下...

public class FieldViewModel : ViewModelBase
{
    private object _instance;
    private FieldInfo _fieldInfo;
    private Type _fieldType;

    public FieldViewModel(object instance, FieldInfo fieldInfo)
    {
        _instance = instance;
        _fieldInfo = fieldInfo;

        _fieldType = fieldInfo.FieldType;

        IsReadOnly = fieldInfo.IsInitOnly || fieldInfo.IsLiteral;
        FieldName = fieldInfo.Name;
        TypeName = fieldInfo.FieldType.Name;
    }


    private bool _IsReadOnly;
    public bool IsReadOnly
    {
        get { return _IsReadOnly; }
        private set
        {
            _IsReadOnly = value;
            OnPropertyChanged();
        }
    }


    private string _fieldName;
    public string FieldName
    {
        get { return _fieldName; }
        set
        {
            _fieldName = value;
            OnPropertyChanged();
        }
    }

    private string _typeName;
    public string TypeName
    {
        get { return _typeName; }
        set
        {
            _typeName = value;
            OnPropertyChanged();
        }
    }

    public object Value
    {
        get
        {
            return _fieldInfo.GetValue(_instance);
        }
        set
        {
            _fieldInfo.SetValue(_instance, Convert.ChangeType(value, _fieldType));
            OnPropertyChanged();
        }
    }
}

用法看起来像...

Person personInstance = new Person
{
    Name = "automagically",
    Age = 25,
    Height = 185.2,
    IsMarried = false
};

ViewModel = new ViewModel(personInstance);

还有视图...

<ItemsControl ItemsSource="{Binding ViewModel.Fields}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>

                <TextBlock Text="{Binding FieldName}" />
                <TextBox Grid.Column="1" Text="{Binding Value}" IsReadOnly="{Binding IsReadOnly}"/>
                <TextBlock Grid.Column="2" Text="{Binding TypeName}" />
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

看起来像这样...

I can't embed images yet...