在拖放过程中修改工具箱项

本文关键字:工具箱 修改 过程中 拖放 | 更新日期: 2023-09-27 18:17:37

我正在使用DesignSurface类,我遇到了一个令人沮丧的问题,我似乎无法弄清楚。我正在从我的工具箱中拖动项目并将它们放到我的表单上,没有问题,但我想修改这些项目的属性,因为它们被放置在DesignSurface上。因此,例如,如果我将标签拖到DesignSurface上,我可能会改变控件的BackColor的值。我知道ToolboxItem暴露了CreateComponents(),它接受属性的可选参数,但这是由低级API调用的,我不知道在哪里或如何覆盖它,所以我可以提供我自己的值。有人有什么想法吗?谢谢!

在拖放过程中修改工具箱项

这不是一个答案,我只是想展示我对你的问题的发现。我认为你应该有自己的方式来访问你自己的Toolbox,这个Toolbox实现了接口IToolboxService,这个接口有一个叫做GetSelectedToolboxItem(IDesignerHost)的方法。我认为你必须实现这个方法来指定你如何获得选中的项目,我认为你的Toolbox应该有一些控件来保存工具箱项目的列表,它可以是ListBox

第二件重要的事情是IDesignerHost,因为我知道这可能是你感兴趣的。它应该与你的DesignSurface有一些关系。它有一些你感兴趣的事件,像这样:

IDesignerHost host = {some method to get it};
host.TransactionClosed += (s,e) => {
  ICollection selected = ((ISelectionService)host.GetService(typeof(ISelectionService))).GetSelectedComponents();                
  IComponent[] comps = new IComponent[selected.Count];
  selected.CopyTo(comps, 0);
  string displayName = YourToolBox.GetSelectedToolboxItem(host).DisplayName;
  ((Control)comps[0]).Text = displayName;
};

我在一个项目中测试了一个非常复杂的IDesignerHost,它有一个方法让我创建host来测试。当在DesignSurface上拖放控件时,它仍然不受TransactionClosed中的代码的影响,我必须做一些类似Resizing, Moving...的控件,之后TransactionClosed事件被引发,它显示DisplayName OK。我没有发现任何其他事件更好,最合适的事件应该是ComponentAddedComponentAdding,但它们都不工作。

我还有一个问题,我尝试的样本项目是一个CustomFormsDesigner,它像其他正常的Windows窗体应用程序一样运行,它显示了一个工具箱,一个DesignSurface和一个属性网格。我做的一切都是在运行之后。那你的案子呢?我的意思是,当你把控件拖放到DesignSurface上时,你仍然处于Visual Studio窗口的设计阶段?还是在您自己的应用程序(作为设计器)窗口中?

我不得不说,这个问题对我来说太复杂了。多亏了你,我才知道了这种应用

虽然我觉得这有点"hacky",但我不一定知道它是错误的(如果它在技术上是"错误的",请给我一些最佳实践的反馈!),它解决了手头的问题。

我最后做的是根据我读到的一篇MSDN文章扩展ToolboxItem。

public class SurfaceToolboxItem : ToolboxItem
{
    private delegate void SetTextHandler(Control c, string text);
    public string ReportFieldName { get; set; }
    protected override IComponent[] CreateComponentsCore(IDesignerHost host)
    {
        var ctrl = (Label)host.CreateComponent(typeof(Label));
        var root = (Control) host.RootComponent;
        root.BeginInvoke(new SetTextHandler(SetText),
                         new object[] {ctrl, ReportFieldName});
        return new IComponent[]{ ctrl };
    }
    private void SetText(Control ctrl, string text)
    {
        ctrl.Text = text;
    }
}

一些明显的缺陷:

  • 这个项目只会创建一个标签控件
  • 这个项目只会根据公共字符串属性设置标签控件的文本属性。

为了克服这些问题,可以将项目扩展为更像基本的ToolboxItem(实现一个接受Type参数的actor,允许传入一个属性名称/值的字典,以便在事后设置,等等)

此外,这破坏了我从工具箱到设计界面的拖放功能,这尤其令人沮丧,因为它不太明显。问题出在我的工具箱服务实现的DeserializeToolboxItem方法中。

原来是这样的:

ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
   return (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
}

问题是,转换到ToolboxItem返回null,因为对象的类型是SurfaceToolboxItem,所以我修改了方法,看起来像这样:

ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
   var data = (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
   return data ?? (ToolboxItem)((DataObject)serializedObject).GetData(typeof(SurfaceToolboxItem));
 }

因此支持我的扩展以及默认的ToolboxItem的。