0

我正在构建一个 Wpf 应用程序,该应用程序当前具有一个工具箱,该工具箱的外观和功能类似于 Visual Studio 工具箱。我有一系列在工具箱中表示的类和不同类型的对象,每个对象都可以包含一些但不是全部的工具箱项。

作为启动和运行我的应用程序的权宜之计,我将工具箱功能硬编码到 UserControls 中。现在我必须改进一个没有硬编码的更好的设计。

我有一个简单的 ToolBoxItem 类,它具有类型、标签和图标的属性。需要进入工具箱的每个类都被添加到 ToolBoxItems 的集合中并且正确显示。

但我正在努力为两个问题想出一个优雅的解决方案:

  • 如何在正在编辑的对象中创建由 ToolBoxItem 类型表示的类的实例。
  • 如何隐藏与正在编辑的项目不兼容的工具箱项目。我现在使用硬编码属性手动执行此操作,每个编辑的类必须通过接口(即 CanSupportClassX、CanSupportBaseClassY)实现。

我的感觉是委托和泛型可能是一个解决方案,但我过去只使用过这些功能并且不确定如何继续。

4

2 回答 2

1

我认为最好的方法是 Visual Studio 本身采用的方法:每个对象通过其属性的类型来描述它可以包含的内容。

所以对于 Visual Studio,我可以创建一个新的对象来保存这样的小部件:

[ContentAttribute("Children")]
public class MyObject
{
  ...
  public ICollection<Widget> Children { get ... set ... }
}

Visual Studio 类型系统完成其余的工作。

当然,这里有一些限制,可能需要一些扩展甚至另一种技术。例如,如果您的对象可以接受两种不同类型的内容,除了 之外,它们不共享一个公共基类object,您将必须声明您的属性,因为ICollection<object>在这种情况下,您的工具箱将不知道它可以真正接受什么,除非您有一个额外的机制。

许多附加机制可用于这些特殊情况,例如:

  • 允许多个“内容”属性,例如ICollection<Widget> WidgetChildren { get ... set ...}; ICollection<Doodad> DoodadChildren { get ... set ... }
  • 创建一个可以应用于该类的属性,以赋予它可以包含的对象类型,例如[ContentTypeAllowed(typeof(Widget))] [ContentTypeAllowed(typeof(Doodad))]您的实际内容属性在哪里IEnumerable<object>
  • 创建一个只有类名列表的属性,例如[ContentTypesAllowed("Widget,Doodad")]
  • 创建一个方法,如果在目标对象中定义,则评估潜在的内容类并返回true它是否可以是子对象,false如果不是,类似这样bool CanAcceptChildType(Type childType) { return type.IsSubclassOf(Widget) && !type==typeof(BadWidget); }
  • 创建一个列出非法儿童的属性,例如[ContentTypesDisallowed("BadWidget")]
  • 将属性或方法添加到您的项目类以表示此数据,例如[TargetMustRespondTo(EditCommands.Cut)] public class CutTool { ... }
  • 将数据添加到您的项目类以表示此数据,例如new ToolBoxItem { Name="Widget", AllowedContainers=new[] { typeof(MyObject), typeof(OtherObject) }
  • 以上的组合

所有这些都是可行的技术。考虑您自己的情况,看看哪些最适合实施。

要真正实现隐藏工具箱项,只需IMultiValueConverter在工具箱项的DataTemplate. 将两个绑定传递给转换器:您的工具箱项和您的目标。然后在IMultiValueConverter.

例如,在您只关心 ContentAttribute 的集合类型的最简单情况下,此代码将起作用:

public object Convert(object[] values, ...)
{
  var toolItem = values[0] as ToolItem;
  var container = values[1];

  var contentPropertyAttribute =
    container.GetType().GetCustomAttributes()
    .OfType<ContentPropertyAttribute>()
    .FirstOrDefault();
  if(contentPropertyAttribute!=null)
  {
    var contentProperty = container.GetType().GetProperty(contentPropertyAttribute.Name);
    if(contentProperty!=null &&
       contentProperty.Type.IsGeneric &&
       contentProperty.Type.GetGenericArguments()[0].IsAssignableFrom(toolItem.Type))
      return Visibility.Visible;
  }
  return Visibility.Collapsed;
}

在实际情况下,事情可能会稍微复杂一些,例如并非所有的 Content 属性都是 ICollection,因此您必须进行额外的检查并可能实现更多算法。添加一些缓存也是一个好主意,这样您就不会经常使用反射。

于 2009-11-09T17:10:55.537 回答
1

您可以在AvalonDock中找到您尝试实现的一个很好的示例,它是完全开源的。如果您想了解如何将 AvalonDock 与 MVVM 一起使用(即将工具放入 windows 等),请查看SoapBox Core Framework

于 2009-11-10T03:38:29.513 回答