C# 在拖放过程中修改ToolboxItem
我正在使用DesignSurface类,我遇到了一个令人沮丧的问题,我似乎无法解决。我正在从工具箱中拖动项目,并将其毫无问题地放到表单中,但我希望在将这些项目放到DesignSurface中时修改其属性。因此,例如,如果我将标签拖到设计图面上,我可能会更改控件背景色的值。我知道ToolboxItem公开CreateComponents(),它接受一个可选的属性参数,但这是由较低级别的API调用的,我不知道在哪里或如何重写它,以便提供自己的值。有人有什么想法吗?谢谢 这不是答案,我只是想展示一下我对你的问题的发现。我认为您应该有自己的方式访问自己的C# 在拖放过程中修改ToolboxItem,c#,winforms,C#,Winforms,我正在使用DesignSurface类,我遇到了一个令人沮丧的问题,我似乎无法解决。我正在从工具箱中拖动项目,并将其毫无问题地放到表单中,但我希望在将这些项目放到DesignSurface中时修改其属性。因此,例如,如果我将标签拖到设计图面上,我可能会更改控件背景色的值。我知道ToolboxItem公开CreateComponents(),它接受一个可选的属性参数,但这是由较低级别的API调用的,我不知道在哪里或如何重写它,以便提供自己的值。有人有什么想法吗?谢谢 这不是答案,我只是想展示一下我
工具箱
,这个工具箱
实现接口IToolboxService
,这个接口有一个名为GetSelectedToolboxItem(IDesignerHost)
的方法。我认为您必须实现该方法来指定如何获取所选项,我认为您的工具箱
应该有一些控制来保存工具箱项列表,它可以是列表框
第二件重要的事情是IDesignerHost
,据我所知,这可能是您感兴趣的内容。它应该与您的设计图面
有一些关系。它有一些让你感兴趣的事件,比如:
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;
};
ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
return (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
}
ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
var data = (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
return data ?? (ToolboxItem)((DataObject)serializedObject).GetData(typeof(SurfaceToolboxItem));
}
我在一个项目中进行了测试,该项目实现了一个非常复杂的IDesignerHost
,它提供了一种方法来创建要测试的主机。当在设计图面上拖放控件时,它仍然不受TransactionClosed
中的代码的影响,我必须执行类似调整控件大小、移动…
的操作,然后引发TransactionClosed
事件,并显示DisplayName
OK。我没有发现其他更好的事件,最合适的事件应该是ComponentAdded
或ComponentAdding
,但它们都不起作用
我还有一个问题,我尝试的示例项目是一个CustomFormsDesigner
,它与其他普通Windows窗体应用程序一样运行,它显示一个工具箱、一个设计图面和一个属性网格。我在运行它之后做所有事情。但是你的案子呢?我的意思是,当您将控件拖放到DesignSurface上时,您仍然处于Visual Studio窗口的设计阶段?还是在您自己的应用程序(作为设计师)窗口中
我不得不说,这个问题对我来说太复杂了。多亏了你,我知道这类应用程序。虽然我觉得这有点“黑客”,但我不一定知道它是错的(如果它在技术上是“错误的”,请给我一些关于最佳实践的反馈!),它解决了手头的问题
我最终做的是根据我读到的一篇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(实现接受类型参数的ctor,允许传入属性名称/值的字典,以便在事实发生后进行设置,等等)
此外,这还破坏了我从工具箱到设计界面的拖放功能,这尤其令人沮丧,因为原因不太明显。问题出在工具箱服务实现的反序列化ToolboxItem方法中
原来是这样的:
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;
};
ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
return (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
}
ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
var data = (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
return data ?? (ToolboxItem)((DataObject)serializedObject).GetData(typeof(SurfaceToolboxItem));
}
问题是对ToolboxItem的转换返回null,因为对象的类型为SurfaceToolboxItem,所以我修改了方法,使其如下所示:
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;
};
ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
return (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
}
ToolboxItem IToolboxService.DeserializeToolboxItem(object serializedObject)
{
var data = (ToolboxItem) ((DataObject) serializedObject).GetData(typeof (ToolboxItem));
return data ?? (ToolboxItem)((DataObject)serializedObject).GetData(typeof(SurfaceToolboxItem));
}
因此支持我的扩展和默认ToolboxItem 您的意思是说设计图面上有一些项目
,您希望在设计时(通过“属性”窗口)修改这些项目的某些属性吗?您的DesignSurface
看起来像什么?它有点像面板吗?您的问题对我来说不够清楚。DesignSurface是一个用户控件。我实际上想做的是,当用户将一个控件放到UserControl上时,我想在它实际显示在表面之前修改该控件的一些属性。当然,如果我想在控件的值出现在DesignSurface上之后修改它们,我只需要配置一个PropertyGrid。这对我来说有点奇怪?为什么一定要这样做?我正在开发的应用程序基本上是试图复制Visual Studio中报表设计器中的一小部分功能,以便向用户公开。我试图解决的问题是,工具箱实际上包含报表字段名称,当用户在DesignSurface上拖放字段时,我需要一种方法来保持x标签实际上是数据集中的名称字段。我还没有想出更好的方法来处理这个问题,坦白说,这是我第一次被要求主持运行时设计器,所以这很有挑战性。嘿,金。有几件事。我应该更清楚:我实际上使用的是System.ComponentModel.Design.DesignSurface,因此与设计师合作的许多细节都从我身上抽象了出来。我的RootComponent是UserControl类型,您的代码确实可以工作,每次向其添加控件时它都会触发,但我更具体的问题是:我工具箱中的所有“工具”都是Label类型,它们的DisplayName设置为报表数据集中的字段名。我需要那个DisplayName值,这样我就知道要将控件绑定到哪个字段。但我愿意接受suggestion@abszero我发现我的解决方案有问题,它只有在我拖动控件时才起作用