如何在c#中动态地将普通类转换为可绑定类

时间:2013-06-30 11:50:05

标签: c# .net wpf data-binding

正如您在WPF应用程序中所知,如果要将特定类的某些属性绑定到控件的属性,则必须实现该类的INotifyPropertyChanged接口。

现在考虑我们有许多没有实现INotifyPropertyChanged的普通类。这个例子非常简单:

    public class User : ModelBase
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
//  ...
}

例如我想将UserName绑定到TextBox,所以我应该编写另一个新的User类来实现INotifyPropertyChanged,如下所示:

public class User : INotifyPropertyChanged
{
    public string Password { get {return _Password}}
                             set {_Password=value;OnPropertyChanged("Password");}

   // ... Id and UserName properties are implemented like Password too.

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
       if (PropertyChanged != null)
       {
           PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
       }
    }
//  ...
}

现在我的问题是,你知道是否有任何机制或技巧来简化它?

考虑到我们有超过100个模型,可能会被更改。

我在想一种方法,比如使用泛型类来做(编辑):

public class BindableClass<NormalClass> { ...adding ability to bind instructions using reflection... }

NormalClass NormalInstance = new NormalClass(); 
      // simple class, does not implemented INotifyPropertyChanged 
      // so it doesn't support binding.

BindableClass<NormalClass> BindableInstance = new BindableClass<NormalClass>(); 
      // Implemented INotifyPropertyChanged 
      // so it supports binding.

当然,我不确定这是不是好方法!澄清我的问题只是一个想法。

请不要告诉我没有办法或不可能!有数百种型号!!!

感谢。

3 个答案:

答案 0 :(得分:1)

如果您无法修改源类,则可以调查Unity新版本中的“行为注入”功能。 “行为注入”功能允许您将类连接到BEHAVE,就好像它从INotifyPropertyChanged继承而不修改类本身。

昨天here有一个非常相似的主题,它提供了让你开始的链接。 Unity团队甚至为您提供了在POCO类上创建INotifyPropertyChanged的注入器的源代码。我已经使用它并且可以证明它有效。有一个性能损失,但如果您不想修改源类但仍然喜欢WPF绑定的功能,那么这是游戏的一部分。

答案 1 :(得分:0)

将InNotifyPropertyChanged实现移动到基类,让视图模型继承该基类。

  

基类

public class BaseNotify
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
       if (PropertyChanged != null)
       {
           PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
       }
    }
}
  

您的View Model类

public class User : BaseNotify
{

    private string userName;
    public string UserName 
    { 

        get { return userName; }
        set
        {
            userName = value;
            OnPropertyChanged("UserName");
        }
    }
}

答案 2 :(得分:0)

我目前没有正在使用的IDE,但我认为这可行(我不确定泛型)

帮助

public class Bindable<T>: RealProxy, INotifyPropertyChanged
{
    private T obj; 

    private Bindable(T obj)
        : base(typeof(T))
    {
        this.obj = obj;
    }

    // not sure if this compiles, 
    // make it a property in that case and the ctor public 
    public static T BuildProxy(T obj) 
    {
       return (T) new Bindable<T>(obj).GetTransparentProxy();
    }

    public override IMessage Invoke(IMessage msg)
    {
        var meth = msg.Properties["__MethodName"].ToString();
        var bf = BindingFlags.Public | BindingFlags.Instance ;
        if (meth.StartsWith("set_"))
        {
            meth = meth.Substring(4);
            bf |= BindingFlags.SetProperty;
        }
        if (meth.StartsWith("get_"))
        {
           bf |= BindingFlags.GetProperty;
           // get the value...
            meth = meth.Substring(4);
        }
        var args = new object[0];
        if (msg.Properties["__Args"] != null)
        {
            args = (object[]) msg.Properties["__Args"];
        }
        var res = typeof (T).InvokeMember(meth, 
            bf
            , null, obj, args);
        if ((bf && BindingFlags.SetProperty) ==  BindingFlags.SetProperty)
        {
            OnPropertyChanged(meth);   
        }
        return new ReturnMessage(res, null, 0, null, null);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
       if (PropertyChanged != null)
       {
           PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
       }
    } 
}

<强>用法

User yourplainUser = GetFromBackEnd();
// if I've the generics wrong, sorry...
var bindUser = Bindable<User>.BuildProxy(yourplainUser);  
// var bindUser = new Bindable<User>(yourplainUser).Proxy;

您现在可以在WPF表单中使用bindUser对象,至少它支持INotifyPropertyChanged,并且对您的应用程序(和/或消费者)更加透明。