【问题标题】:DataType to store and use generic types with specific restrictions?DataType 存储和使用具有特定限制的泛型类型?
【发布时间】:2014-08-15 14:38:18
【问题描述】:

我觉得这应该很容易,但今天早上我的大脑出现了问题。

我有一个自定义 UserControl 绑定到自定义 ItemsSource。我想将其设置为使用匹配的Control 根据用户定义的绘制项目的方式绘制每个项目。

例如,我希望能够从使用控件的类中执行此操作

var data = new List<object>();
data.Add(new MyClassA());
data.Add(new MyClassB());
data.Add(new MyClassB());

myCustomControl.ItemsSource = myObjectArray;

myCustomControl.ResourceLibrary = {
    { MyClassA, ctlClassA },
    { MyClassB, ctlClassB }
}

在我的自定义控件中,类似的东西

foreach(var item in ItemsSource)
{
    var key = item.GetType();

    if (ResourceLibrary.ContainsKey(key))
    {
        var ctlType = ResourceLibrary[key];
        if (ctlType != null)
        {
            var ctl = new ctlType();
            // Do something with control
        }
    }
}

我可以为ResourceLibrary 使用哪种数据类型来允许我这样做?或者有没有更好的方法来完成这项任务?

我还希望能够将传入的类型限制为仅允许类型where T : Control, new()

我正在使用 WinForms、C# 和 .Net 3.5

【问题讨论】:

  • 你的一个问题是var ctl = new ctlType();,C# 不做虚拟构造函数。
  • 使用 MVVM 和 Caliburn,将其替换/包装为 MyClassAViewModelMyClassAView,整个事情就变得自动化了。
  • @HenkHolterman 我希望我可以为此使用 WPF,但它是 WinForms 和 .Net 3.5
  • 嘿..你在winforms中实现ItemsControl?这很酷.. 代码是专有的吗?否则,如果你能分享它,那就太棒了。
  • @HighCore 我没有尝试任何非常高级的东西......我只想要一个可绑定的 TabControl。似乎向 WinForms 索取有点过分了... =/

标签: c# winforms .net-3.5


【解决方案1】:

在最基本的层面上,我认为这样的事情可能会有所帮助:

public class DataTemplateManager
{
    private readonly Dictionary<Type, Type> dataTemplates = new Dictionary<Type, Type>();

    public void Add<TModel, TView>() where TView : Control, new()
    {
        dataTemplates.Add(typeof (TModel), typeof (TView));
    }

    public void Add(Type modelType, Type viewType)
    {
        if (!typeof (Control).IsAssignableFrom(viewType))
            throw new InvalidOperationException("viewType must derive from System.Windows.Forms.Control");

        dataTemplates.Add(modelType, viewType);
    }

    public Control Resolve(object model)
    {
        if (model == null)
            return null;

        var type = model.GetType();

        if (!dataTemplates.ContainsKey(type))
            return null;

        var viewType = dataTemplates[type];

        var control = Activator.CreateInstance(viewType);

        return control as Control;
    }
}

然后只需将这种类型的属性添加到您的控件中:

public class MyControl: Control
{
    public DataTemplateManager DataTemplateManager {get; private set;}

    public MyControl()
    {
        this.DataTemplateManager = new DataTemplateManager();
    }
}

然后你可以这样使用:

myControl1.DataTemplateManager.Add<MyModel1, MyView1>();
myControl1.DataTemplateManager.Add<MyModel2, MyView2>();
myControl1.DataTemplateManager.Add<MyModel3, MyView3>();

并像这样使用Resolve() 方法:

foreach(var item in ItemsSource)
{
    var ctl = this.DataTemplateManager.Resolve(item);

    //.. do something with ctl     
}

另外,我可以想到添加一些非常有用的功能,例如 IBinder&lt;TModel,TView&gt; 的概念,它可以处理 do something with ctl.. 部分:

public interface IBinder<TModel,TView>
{
     void Bind(TModel model, TView view);
}

public class MyBinder: IBinder<MyModel1,MyView1>
{
    public void Bind(MyModel1 model, MyView1 view)
    {
         view.SomeProperty = model.SomeOtherProperty;
         //.. And so on.
    }
}

然后:

myControl.DataTemplateManager.AddBinder<TModel1,TView1,MyBinder>();

并在Resolve() 方法中使用它。

请注意,这不处理继承或泛型。如果您需要支持这些场景,想法会变得有点复杂,但它仍然是可行的。

顺便说一句,我试图让它尽可能抽象。否则人们可能会认为我在编写 winforms 代码...

【讨论】:

  • 哇,这个做得好!如果可以的话,我将不得不给你一些额外的赏金代表。 :)
猜你喜欢
  • 1970-01-01
  • 2011-04-22
  • 2013-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多