65.9K
CodeProject 正在变化。 阅读更多。
Home

设计嵌套控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (35投票s)

2009 年 7 月 2 日

CPOL

6分钟阅读

viewsIcon

137048

downloadIcon

4196

一篇关于在设计时允许嵌套控件接受子控件的文章。

NestedControlDesigner Screen Shot

引言

本文演示了如何允许一个作为另一个Control子控件的Control在设计时接受控件的拖放。 这不是一篇很长的文章,代码也不多,这可能不是“官方”的最佳方法。 然而,它确实有效,并且在我能够测试的范围内是稳定的。

背景

自从 VS2003 以来,我大部分时间都在业余时间使用 C#。在那期间,我曾偶尔尝试设计各种类型的控件,这些控件又包含其他控件。 当这些控件被放置在FormPanel或其他控件上时, nunca 就不可能将TextBox等控件拖放到内部控件上,并将其添加为该控件的子控件。 我曾寻找解决这个问题的方法,但从未找到。 最近,我在 C# 论坛上试图帮助别人时,与其说是运气,不如说是判断,我找到了一个我认为能解决问题的方法。 在我帮助完其他 CPian 后,我匆忙制作了一个非常快速的演示并写了这篇文章。 我写这篇文章部分是因为我终于找到了解决问题的方法,但主要是因为我没有在任何地方看到过这种技术的示例,我认为将其传播出去很重要。

Using the Code

本文配套的演示应用程序由应用程序主窗体和两个控件库组成。 其中第一个NestedControlDesignerLibrary包含TestControlTestControlDesigner的代码。

TestControl由一个UserControl组成,该UserControl又包含两个Panel控件。 一个停靠在顶部,包含一个Label用作整个控件的标题;第二个是我想让它能够接受子控件的控件,它占满了剩余的空间。

这是TestControl的代码。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace NestedControlDesignerLibrary
{
    /// <summary>
    /// A test Control to demonstrate allowing nested Controls
    /// to accept child controls at design time.
    /// </summary>
    [
    Designer(typeof(NestedControlDesignerLibrary.Designers.TestControlDesigner))
    ]
    public partial class TestControl : UserControl
    {
        public TestControl()
        {
            InitializeComponent();
        }

        #region TestControl PROPERTIES ..........................
        /// <summary>
        /// Surface the Caption to allow the user to 
        /// change it
        /// </summary>
        [
        Category("Appearance"),
        DefaultValue(typeof(String), "Test Control")
        ]
        public string Caption
        {
            get
            {
                return this.lblCaption.Text;
            }

            set
            {
                this.lblCaption.Text = value;
            }
        }

        [
        Category("Appearance"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
        ]
        /// <summary>
        /// This property is essential to allowing the designer to work and
        /// the DesignerSerializationVisibility Attribute (above) is essential
        /// in allowing serialization to take place at design time.
        /// </summary>
        public Panel WorkingArea
        {
            get
            {
                return this.pnlWorkingArea;
            }
        }
        #endregion
    }
}

它很简单,但它实际上并没有做什么;作为一个控件,它几乎是无用的。 它的唯一目的是演示这种方法。

有两个重要部分

  • 类声明正上方的Designer属性,它告诉 VS Forms Designer 该控件有自己的设计器以及在哪里找到它。
  • WorkingArea属性及其相关属性。 此属性在 Designer 中用于设置控件托管功能。 该属性确保当控件在设计时拖放到WorkingArea上时,它会被序列化。 作为序列化过程的一部分,Designer 确保拖放的控件会被自动添加到WorkingAreaControls集合中,而不是主控件的Controls集合中。

设计器代码

namespace NestedControlDesignerLibrary.Designers
{
    public class TestControlDesigner : ParentControlDesigner
    {
        public override void Initialize(System.ComponentModel.IComponent component)
        {
            base.Initialize(component);

            if (this.Control is TestControl)
            {
                this.EnableDesignMode((
                   (TestControl)this.Control).WorkingArea, "WorkingArea");
            }
        }
    }
}

就是这样。 我发现一个困扰我多年的问题可以用这么少的代码解决,真是令人沮丧。 我绝不是 Designer 方面的专家,但即使是我也能理解这一点。 TestControlDesigner继承自ParentControlDesigner,根据 MSDN 的说法,它是

用于扩展支持嵌套控件的Control的设计模式行为的基设计器类。

由于我们这里处理的是嵌套控件,这似乎是合适的。 当设计器初始化时,它会调用其基类的Initialize以确保默认行为的发生,然后它会调用EnableDesignMode,并将要接收设计时拖放控件的控件指定为WorkingArea。 主控件需要被转换为正确的控件类型,因为设计器通常将此信息存储为对Control的引用,并使用名称作为字符串向用户公开控件。 可以为每个需要设计时功能的子控件重复调用EnableDesignMode。 有关更多信息,请参阅 MSDN 上的 EnableDesignMode

上面呈现的代码和控件存在一个显著的缺点。WorkArea在属性窗口中的行为类似于SplitContainerpanel1panel2。 这意味着,如果展开它,您将可以看到它的所有公共属性。 这包括Dock属性,对于这个控件来说,这没有意义,因为WorkArea的停靠是设计的基础,允许用户更改它简直是愚蠢的。

在座的各位经验丰富者将知道如何克服这个问题,但对于新的编码者,我在ImprovedNestedControlDesignLibrary中创建了一个ImprovedTestControl。与普通控件唯一的区别是,在改进版本中,WorkArea是一个继承自Panel的 User Control,而不是一个简单的Panel。我选择这样做是因为它使我能够为其提供自己的 Designer,这是我选择用来隐藏不需要的属性的技术。还有很多其他方法可以做到这一点,并且可以在 CodeProject 上找到一些优秀的关于如何做到这一点的文章。在主页的搜索框中输入“hiding inherited properties”然后按 Enter。我找到的第一篇文章是 Hiding Inherited Properties from the PropertyGrid,作者是Shaun Wilde,专门处理这个问题;其他文章或多或少也涉及到这个问题。请随意浏览并找到您喜欢的方法来用于您自己的代码。

总之,这是新的WorkArea的代码。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ImprovedNestedControlDesignerLibrary
{
    [
    Designer(typeof(ImprovedNestedControlDesignerLibrary.Designers.WorkingAreaDesigner))
    ]
    public partial class WorkingAreaControl : Panel
    {
        public WorkingAreaControl()
        {
            InitializeComponent();
            base.Dock = DockStyle.Fill;
        }
    }
}

如前所述,此控件继承自Panel

由于我隐藏了Dock属性的方式,在设计ImprovedTestControl时它将不可用,因此有必要在构造函数中对其进行适当的设置。就这样了,除了 Designer。

这是代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms.Design;

namespace ImprovedNestedControlDesignerLibrary.Designers
{
    public class WorkingAreaDesigner : ScrollableControlDesigner
    {
        protected override void PreFilterProperties(
                  System.Collections.IDictionary properties)
        {
            properties.Remove("Dock");

            base.PreFilterProperties(properties);
        }
    }
}

这个 Designer 所做的就是覆盖PreFilterProperties方法。该方法只有一个参数,即其控件所有公共属性的Dictionary。因此,只需要删除任何您不想显示在属性窗口中的属性,然后将现在更精简的Dictionary传递给基方法。搞定!

演示应用程序是一个简单的窗体,带有一个TestControl和一个ImprovedTestControl

添加TestControl后,我在上面放了一个ComboBox并为其添加了一些项。 对于ImprovedTestControl,我添加了一个 Label 和一个ListBox,同样也添加了一些项。

关注点

在撰写本文的过程中,我学到了许多有趣的东西。 尤其是,我学到了帮助他人也能帮助自己。 我也得到了证实,微软关于 Designer 主题的文档是多么糟糕。

以下是一些关于 Windows Forms 设计过程的文章链接。

我希望这篇文章能让你们中的一些人觉得有用,因为我找到这个问题的解决方案时高兴极了。

历史

  • 版本 1: 2009 年 7 月 1 日。
设计嵌套控件 - CodeProject - 代码之家
© . All rights reserved.