星期五, 5月 05, 2023

[C#] TitlePanel

該 GitHub 範例 - Enable Child Control Designer 筆記,該自訂控件效果如下,只弄了從工具箱至 UserControl 的效果,作者 repo 內還有一張從 Form 拖曳進 UserControl 的示意圖喔


自訂控件內控件階層顯示如下圖,UserControl 內有兩個 Panel (TitlePanel 和 ContentPanel),TitlePanel 內再放一個 Label (TitleLabel) 控件,最後透過 Dock 設定讓控件大小可以隨著 UserControl 變化,各子控件 Dock 設定
  • TitlePanel:Dock.Top
  • ContentPanel:Dock.Fill
  • TitleLabel:Dock.Fill
[C#] TitlePanel-2

核心功能為使用者在設計階段只能把控件拖曳進 ContentPanel 內、TitlePanel 是無法放控件

該範例整體設計觀念在於 UserControl 無法拖曳控件進入,但是在 UserControlDesigner 內針對子控件去啟用 [設計階段可以拖曳控件進入],以下就用 Code 方式來記錄,部分使用 Code 來達到的效果,會改寫順道驗證看看理解是否正確,筆記重點拆分為
  • ContentPanel 和 ContentPanelDesigner
  • UCPanel 和 UCPanalDesigner

ContentPanel 和 ContentPanelDesigner

原寫法是使用標準 Panel 並透過 Designer 去移除變更位置、大小相關屬性,讓使用者在設計階段無法進行變更,改寫建立一個 UserControl (ContentPanel 和 ContentPanelDesigner) 並進行相關設定

ContentPanelDesigner 內只進行 SelectionRules 設定,移除 AllSizeable,讓使用者無法用拖曳方式變更 ContentPanel 大小,SelectionRules 請參考該筆記 - [C#] SelectionRules,至於 PostFilterAttributes 和 PostFilterProperties 內的 Code 轉換進 UCContentPanel,以下針對內容作紀錄
  • DockingAttribute:移除智能標籤上的 Docking 功能,詳見 [C#] DockingAttribute
  • BrowsableAttribute:讓使用者看不見位置、大小相關屬性

using System.Windows.Forms.Design;

namespace UCPanelSample
{
    public class UCContentPanelDesigner : ParentControlDesigner
    {
        public override SelectionRules SelectionRules
        {
            get
            {
                SelectionRules selectionRules = base.SelectionRules;
                selectionRules &= ~SelectionRules.AllSizeable;
                return selectionRules;
            }
        }

        #region 該設定已經移往 UCContentPanel 去

        //protected override void PostFilterAttributes(IDictionary attributes)
        //{
        //    base.PostFilterAttributes(attributes);

        //    attributes[typeof(DockingAttribute)] = new DockingAttribute(DockingBehavior.Never);
        //}

        //protected override void PostFilterProperties(IDictionary properties)
        //{
        //    base.PostFilterProperties(properties);

        //    var propertiesToRemove = new string[] {
        //        "Dock", "Anchor",
        //        "Size", "Location", "Width", "Height",
        //        "MinimumSize", "MaximumSize",
        //        "AutoSize", "AutoSizeMode",
        //        "Visible", "Enabled",
        //    };

        //    foreach (var item in propertiesToRemove)
        //    {
        //        if (properties.Contains(item))
        //            properties[item] = TypeDescriptor.CreateProperty(this.Component.GetType(),
        //                (PropertyDescriptor)properties[item],
        //                new BrowsableAttribute(false));
        //    }
        //} 

        #endregion
    }
}

PostFilterAttributes 和 PostFilterProperties 內的 Code 轉換成 UCContentPanel 使用者自訂控件,UCContentPanel 繼承 Panel,有些 Property 可以透過 override 來進行改寫,沒有辦法 override 則是透過 new 來達到 BrowseAttribute(false) 設定,new 使用請參考 [C#] new 關鍵字
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace UCPanelSample
{
    [Docking(DockingBehavior.Never)]
    [Designer(typeof(UCContentPanelDesigner))]
    public class UCContentPanel : Panel
    {
        [Browsable(false)]
        public override DockStyle Dock { get => base.Dock; set => base.Dock = value; }

        [Browsable(false)]
        public override AnchorStyles Anchor { get => base.Anchor; set => base.Anchor = value; }

        [Browsable(false)]
        public override Size MinimumSize { get => base.MinimumSize; set => base.MinimumSize = value; }

        [Browsable(false)]
        public override Size MaximumSize { get => base.MaximumSize; set => base.MaximumSize = value; }

        [Browsable(false)]
        public override bool AutoSize { get => base.AutoSize; set => base.AutoSize = value; }

        [Browsable(false)]
        public override AutoSizeMode AutoSizeMode { get => base.AutoSizeMode; set => base.AutoSizeMode = value; }

        [Browsable(false)]
        public new Size Size { get => base.Size; set => base.Size = value; }

        [Browsable(false)]
        public new Point Location { get => base.Location; set => base.Location = value; }

        [Browsable(false)]
        public new int Width { get => base.Width; set => base.Width = value; }

        [Browsable(false)]
        public new int Height { get => base.Height; set => base.Height = value; }

        [Browsable(false)]
        public new bool Visible { get => base.Visible; set => base.Visible = value; }

        [Browsable(false)]
        public new bool Enabled { get => base.Enabled; set => base.Enabled = value; }
    }
}

以上即改成 [標準 Panel 搭配 Designer] 為 UserControl (ContentPanel 和 ContentPanelDesigner)

UCPanel 和 UCPanalDesigner

UCPanelDesigner 為自訂控件控件本身,相關設定是禁止控件拖曳進來,重點設定
  • EnableDesignMode:雖然控件本身無法接受子控件,但是可以透過 EnableDesignMode 設定,讓子控件可以接受子控件
  • CanParent:該控件不允許有子控件
  • OnDragOver:禁止拖曳控件
  • CreateToolCore:禁止從工具箱拖曳控件
原來從工具箱拖曳控件和從 Form 上拖曳控件,是兩個不同處理方式
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace UCPanelSample
{
    public class UCPanelDesigner : ParentControlDesigner
    {
        public override void Initialize(IComponent component)
        {
            base.Initialize(component);

            var contentsPanel = ((UCPanel)Control).ContentsPanel;
            EnableDesignMode(contentsPanel, nameof(UCPanel));
        }

        public override bool CanParent(Control control)
        {
            return false;
        }

        protected override void OnDragOver(DragEventArgs de)
        {
            de.Effect = DragDropEffects.None;
        }

        protected override IComponent[] CreateToolCore(ToolboxItem tool, int x, int y, int width, int height, bool hasLocation, bool hasSize)
        {
            return null;
        }
    }
}
UCPanel 重點
  • ContentsPanel Property 設計:讓 UCPanelDesigner 可以在建構式內抓到它,並開啟 EnableDesignMode,也因此要避免使用者去變化它的位置和大小
  • TitleFont Property:自行加上去的 Property,搭配啟用 TitleLabel 和 TitlePanel AutoSize,使其在設計階段可以根據需求調整字型大小
  • TypeDescriptor.AddAttributes:原寫法是透過 Code 把 UCContentPanelDesigner 綁定上 UCContentPanel,這部分已經改寫成 UserControl
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace UCPanelSample
{
    [Designer(typeof(UCPanelDesigner))]
    public partial class UCPanel : UserControl
    {
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public UCContentPanel ContentsPanel
        {
            get { return ContentPanel; }
        }

        public string TitleText
        {
            get { return Titlelabel.Text; }
            set { Titlelabel.Text = value; }
        }

        // 自行增加的屬性
        public Font TitleFont
        {
            get { return Titlelabel.Font; }
            set { Titlelabel.Font = value; }
        }

        public UCPanel()
        {
            InitializeComponent();

            // 變更 TitleFont 時,TitlePanel 高度才會一併變化
            Titlelabel.AutoSize = true;
            TitlePanel.AutoSize = true;

            // 直接加進去,就不用另外成立一個 UserControl-ContentPanel
            // TypeDescriptor.AddAttributes(ContentsPanel, new DesignerAttribute(typeof(UCContentPanelDesigner)));
        }
    }
}

變化字型大小效果

[C#] TitlePanel-3

沒有留言:

張貼留言