分享在winform下實現左右佈局多視窗界面

来源:http://www.cnblogs.com/zuowj/archive/2016/01/04/5092038.html
-Advertisement-
Play Games

在web頁面上我們可以通過frameset,iframe嵌套框架很容易實現各種導航+內容的佈局界面,而在winform、WPF中實現其實也很容易,我這裡就分享一個:在winform下實現左右佈局多視窗界面。我這裡說的多視窗是指一個父視窗包含多個子視窗,在winform中實現這種效果很簡單,即將某個窗...


在web頁面上我們可以通過frameset,iframe嵌套框架很容易實現各種導航+內容的佈局界面,而在winform、WPF中實現其實也很容易,我這裡就分享一個:在winform下實現左右佈局多視窗界面。

我這裡說的多視窗是指一個父視窗包含多個子視窗,在winform中實現這種效果很簡單,即將某個視窗的IsMdiContainer設為true,然後將其它子視窗的MdiParent設為其父視窗對象即可,這樣就完成了一個多視窗界面,效果如下:

點擊NEW新打開一個視窗,其效果如下:

請看我上圖紅色標註的地方,Windows菜單項下麵顯示的是當前所有已打開的子視窗,點擊某個菜單,即可快速切換到其它視窗,若關閉某個子視窗,與之相對應的菜單項也會自動被移除,實現這個功能也很簡單,只需要將菜單的MdiWindowListItem屬性設為需要顯示活動視窗列表的菜單項即可,如:this.menuStrip1.MdiWindowListItem = this.windowsToolStripMenuItem;

上述示例完整的實現代碼如下:

    public partial class FormMdi : Form
    {
        private int formCount = 0;

        public FormMdi()
        {
            InitializeComponent();
            this.menuStrip1.MdiWindowListItem = this.windowsToolStripMenuItem;
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowChildForm<FormChild>();
        }


        private void ShowChildForm<TForm>() where TForm : Form, new()
        {
            TForm childForm = new TForm();
            childForm.Name = "frm" + Guid.NewGuid().ToString("N");
            childForm.Text = string.Format("Child Form -{0}", ++formCount);
            childForm.MdiParent = this;
            childForm.WindowState = FormWindowState.Maximized;
            childForm.Show();
        }
    }

相信實現上面這部份功能一般用過winform的人都會操作,我這裡就當是複習順便給新手一個參考,同時也為下麵要實現的左右佈局功能做一個鋪墊吧。

 要實現左右佈局,並且能夠支持可動態調整左右占比的功能,非SplitContainer控制項莫屬了,如果不瞭解該控制項用法請自行在網上查找相關資料,我這裡就不作說明,如果要顯示WINDOWS已打開的子視窗情況,同樣也需要用到MenuStrip控制項,

最終設計的主視窗(FormMain)效果如下:

我這裡因為只是演示,所以菜單控制項上我只添加了兩個菜單項,分別為:WINDOWS,用於顯示WINDOWS已打開的子視窗列表,NEW,用於打開一個子視窗;SplitContainer控制項全部採用預設的,沒有放置任何控制項在其中,如果用在正式系統中,一般左邊Panel1中會放置一個樹形菜單,右邊Panel2中保持空即可,因為這個是用來作為子視窗的容器。

 控制項層次結構如下圖示:

界面設計好了,下麵就實現最重要的兩個功能。

第一個功能:在右邊Panel2中顯示子視窗,實現代碼如下:

public FormMain()
       {
            this.IsMdiContainer = true;
        }

private void ShowChildForm<TForm>() where TForm : Form, new()
        {
            TForm childForm = new TForm();
            childForm.Name = "frm" + Guid.NewGuid().ToString("N");
            childForm.Text = string.Format("Child Form -{0}", ++formCount);
            childForm.MdiParent = this;
            childForm.Parent = splitContainer1.Panel2;
            childForm.WindowState = FormWindowState.Maximized;
            childForm.Show();

        }

簡要說明:

1.在視窗構造函數中動態的將IsMdiContainer設為true,當然也可以設計視圖中設置;

2.編寫一個顯示寫子視窗的方法,方法中需註意的地方:childForm.MdiParent = this;childForm.Parent = splitContainer1.Panel2,意思是:將當前視窗作為子視窗的父視窗,同時將Panel2指定為子視窗的父對象,這樣就能實現子視窗在Panel2中打開了。

第二個功能:在WINDOWS菜單項下顯示已打開的子視窗列表,這裡實現就沒有像文章一開始介紹的那樣簡單,使用那個方法是無效的,需要我們來自行實現,稍微有點複雜,但如果明白其實現原理,也就簡單明白了。

實現思路:當childForm載入到Panel2時,會觸發Panel2.ControlAdded事件,當childForm被關閉時,會觸發Panel2.ControlRemoved事件,我們可以統一訂閱這兩個事件,當childForm載入時,那麼就在WINDOWS菜單項下增加一個菜單項,反之則移除該菜單項,實現代碼如下:

this.splitContainer1.Panel2.ControlAdded += Panel2_ControlChanged;
this.splitContainer1.Panel2.ControlRemoved += Panel2_ControlChanged;


void Panel2_ControlChanged(object sender, ControlEventArgs e)
        {
            var frm = e.Control as Form;
            string menuName = "menu_" + frm.Name;
            bool exists = this.splitContainer1.Panel2.Controls.Contains(frm);
            if (exists)
            {
                var menuItem = GetMenuItem(menuName);
                if (menuItem != null)
                {
                    menuItem.Checked = true;
                    frm.BringToFront();
                    frm.Focus();
                }
                else
                {
                    windowsToolStripMenuItem.DropDownItems.Add(new ToolStripMenuItem() { Text = frm.Text, Name = menuName, Tag = frm, Checked = true });
                }
            }
            else
            {
                var menuItem = GetMenuItem(menuName);
                if (menuItem != null)
                {
                    windowsToolStripMenuItem.DropDownItems.Remove(menuItem);
                    menuItem.Dispose();
                }
            }
        }

        private ToolStripMenuItem GetMenuItem(string menuName)
        {
            var menuItems = windowsToolStripMenuItem.DropDownItems.Cast<ToolStripMenuItem>();
            menuItems.ToList().ForEach(m => m.Checked = false);
            return menuItems.Where(m => m.Name == menuName).SingleOrDefault();
        }

同時為了實現點擊WINDOWS菜單項的子菜單能夠快速切換子視窗,需要訂閱WINDOWS菜單項的DropDownItemClicked事件,當然也可以為新增的子菜單項訂閱Click事件,實現代碼如下:

windowsToolStripMenuItem.DropDownItemClicked += windowsToolStripMenuItem_DropDownItemClicked;

void windowsToolStripMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            var menuItem = GetMenuItem(e.ClickedItem.Name);
            menuItem.Checked = true;
            var childForm = menuItem.Tag as Form;
            childForm.BringToFront();
            childForm.Focus();

        }


private void CheckWindowsMenuItem(string menuName)
        {
            var menuItem = GetMenuItem(menuName);
            if (menuItem != null)
            {
                menuItem.Checked = true;
            }
        }

這樣就基本實現了在WINDOWS菜單項下顯示已打開的子視窗列表,並點擊指定的菜單項能夠切換當前活動的子視窗,但仍有一個不足的地方,那就是,當直接點擊子視窗來切換當前活動視窗時(說白了就是當點擊某個子視窗標題欄,該視窗就顯示在其它所有的子視窗最前面),WINDOWS菜單項下的子菜單勾選項沒有同步更新,一開始想到的是用Activated事件來進行處理,結果經測試發現有效,該Activated事件在點擊子視窗標題欄時並不會被觸發,所以只能換種方法,經過多次測試,發現當視窗從後面切換到前面時(稱為Z順序改變),子視窗就會發生重繪,從而觸發Paint方法,我們可以訂閱該事件,併進行處理,實現代碼如下:

private string currentChildFormName = null;   //記錄當前活動子視窗名稱

childForm.Paint += (s, e) => { 
                var frm=s as Form;
                if (!frm.Name.Equals(currentChildFormName) && this.splitContainer1.Panel2.Controls[0].Equals(frm)) //當容器中第一個控制項就是當前的視窗,則表明該視窗處於所有視窗之上
                {
                    CheckWindowsMenuItem("menu_" + frm.Name);
                    currentChildFormName = frm.Name;
                }
            };

最後貼出完整的實現代碼:

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

namespace WindowsFormsApplication1
{
    public partial class FormMain : Form
    {
        private int formCount = 0;
        private string currentChildFormName = null;
        public FormMain()
        {
            InitializeComponent();
            this.IsMdiContainer = true;
            this.splitContainer1.Panel2.ControlAdded += Panel2_ControlChanged;
            this.splitContainer1.Panel2.ControlRemoved += Panel2_ControlChanged;
            windowsToolStripMenuItem.DropDownItemClicked += windowsToolStripMenuItem_DropDownItemClicked;
        }

        void windowsToolStripMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            var menuItem = GetMenuItem(e.ClickedItem.Name);
            menuItem.Checked = true;
            var childForm = menuItem.Tag as Form;
            childForm.BringToFront();
            childForm.Focus();

        }


        private void FormMain_Load(object sender, EventArgs e)
        {
            ShowChildForm<FormChild>();
        }

        private void ShowChildForm<TForm>() where TForm : Form, new()
        {
            TForm childForm = new TForm();
            childForm.Name = "frm" + Guid.NewGuid().ToString("N");
            childForm.Text = string.Format("Child Form -{0}", ++formCount);
            childForm.MdiParent = this;
            childForm.Parent = splitContainer1.Panel2;
            childForm.WindowState = FormWindowState.Maximized;
            childForm.Paint += (s, e) => { 
                var frm=s as Form;
                if (!frm.Name.Equals(currentChildFormName) && this.splitContainer1.Panel2.Controls[0].Equals(frm)) //當容器中第一個控制項就是當前的視窗,則表明該視窗處於所有視窗之上
                {
                    CheckWindowsMenuItem("menu_" + frm.Name);
                    currentChildFormName = frm.Name;
                }
            };
            
            childForm.Show();

        }


        private void CheckWindowsMenuItem(string menuName)
        {
            var menuItem = GetMenuItem(menuName);
            if (menuItem != null)
            {
                menuItem.Checked = true;
            }
        }

        void Panel2_ControlChanged(object sender, ControlEventArgs e)
        {
            var frm = e.Control as Form;
            string menuName = "menu_" + frm.Name;
            bool exists = this.splitContainer1.Panel2.Controls.Contains(frm);
            if (exists)
            {
                var menuItem = GetMenuItem(menuName);
                if (menuItem != null)
                {
                    menuItem.Checked = true;
                    frm.BringToFront();
                    frm.Focus();
                }
                else
                {
                    windowsToolStripMenuItem.DropDownItems.Add(new ToolStripMenuItem() { Text = frm.Text, Name = menuName, Tag = frm, Checked = true });
                }
            }
            else
            {
                var menuItem = GetMenuItem(menuName);
                if (menuItem != null)
                {
                    windowsToolStripMenuItem.DropDownItems.Remove(menuItem);
                    menuItem.Dispose();
                }
            }
        }

        private ToolStripMenuItem GetMenuItem(string menuName)
        {
            var menuItems = windowsToolStripMenuItem.DropDownItems.Cast<ToolStripMenuItem>();
            menuItems.ToList().ForEach(m => m.Checked = false);
            return menuItems.Where(m => m.Name == menuName).SingleOrDefault();
        }


        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowChildForm<FormChild>();
        }


    }
}

以下是系統自動生成的代碼:

namespace WindowsFormsApplication1
{
    partial class FormMain
    {
        /// <summary>
        /// 必需的設計器變數。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的資源。
        /// </summary>
        /// <param name="disposing">如果應釋放托管資源,為 true;否則為 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗體設計器生成的代碼

        /// <summary>
        /// 設計器支持所需的方法 - 不要
        /// 使用代碼編輯器修改此方法的內容。
        /// </summary>
        private void InitializeComponent()
        {
            this.menuStrip1 = new System.Windows.Forms.MenuStrip();
            this.windowsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.menuStrip1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
            this.splitContainer1.SuspendLayout();
            this.SuspendLayout();
            // 
            // menuStrip1
            // 
            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.windowsToolStripMenuItem,
            this.newToolStripMenuItem});
            this.menuStrip1.Location = new System.Drawing.Point(0, 0);
            this.menuStrip1.MdiWindowListItem = this.windowsToolStripMenuItem;
            this.menuStrip1.Name = "menuStrip1";
            this.menuStrip1.Size = new System.Drawing.Size(1069, 25);
            this.menuStrip1.TabIndex = 1;
            this.menuStrip1.Text = "menuStrip1";
            // 
            // windowsToolStripMenuItem
            // 
            this.windowsToolStripMenuItem.Name = "windowsToolStripMenuItem";
            this.windowsToolStripMenuItem.Size = new System.Drawing.Size(73, 21);
            this.windowsToolStripMenuItem.Text = "Windows";
            this.windowsToolStripMenuItem.Click += new System.EventHandler(this.windowsToolStripMenuItem_Click);
            // 
            // newToolStripMenuItem
            // 
            this.newToolStripMenuItem.Name = "newToolStripMenuItem";
            this.newToolStripMenuItem.Size = new System.Drawing.Size(46, 21);
            this.newToolStripMenuItem.Text = "New";
            this.newToolStripMenuItem.Click += new System.EventHandler(this.newToolStripMenuItem_Click);
            // 
            // splitContainer1
            // 
            this.splitContainer1.BackColor = System.Drawing.SystemColors.ActiveCaption;
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point(0, 25);
            this.splitContainer1.Name = "splitContainer1";
            // 
            // splitContainer1.Panel2
            // 
            this.splitContainer1.Panel2.BackColor = System.Drawing.SystemColors.ScrollBar;
            this.splitContainer1.Size = new System.Drawing.Size(1069, 526);
            this.splitContainer1.SplitterDistance = 356;
            this.splitContainer1.TabIndex = 2;
            // 
            // FormMain
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(1069, 551);
            this.Controls.Add(this.splitContainer1);
            this.Controls.Add(this.menuStrip1);
            this.MainMenuStrip = this.menuStrip1;
            this.Name = "FormMain";
            this.Text = "FormMain";
            this.Load += new System.EventHandler(this.FormMain_Load);
            this.menuStrip1.ResumeLayout(false);
            this.menuStrip1.PerformLayout();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
            this.splitContainer1.ResumeLayout(false);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.MenuStrip menuStrip1;
        private System.Windows.Forms.ToolStripMenuItem windowsToolStripMenuItem;
        private System.Windows.Forms.SplitContainer splitContainer1;
        private System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem;
    }
}

以下是效果演示截圖:

如果大家有什麼更好的實現方法可以在下方評論,不足之處也歡迎指出,謝謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、http協議(這一塊兒有時間的話會做記錄)2、常用的兩種RPC方式基於http協議:HttpClient和JDK自己的Http操作類基於TCP或UDP協議:mina2和netty(這一部分以後有時間做記錄)3、HttpClient工具類的編寫(只列出了兩個最常用的方法get和post)使用場合:...
  • 前言: 新的一年開始了,今年的主要目標就是javascript,最近在看javascript高級程式設計(第三版),單純的看書也是個很枯燥的事,所以就把看完的東西再寫一遍吧,一方面加深印象,一方面理解不正確的也好讓園子裡面的大神們給提提意見。 這本書前兩章主要是javascript簡介以及在...
  • 1.ctrl+shift+p ; 輸入ssvvp ; 回車2.輸入markdown-preview進行安裝3.打開任意.md文件 ; 按ctrl-shift-m 進行預覽
  • 1、代碼實現給出的屬性文件:http.properties(位於類路徑下)1 #每個路由的最大連接數2 httpclient.max.conn.per.route = 20 3 #最大總連接數4 httpclient.max.conn.total = 4005 #連接超時時間(ms)6 http.....
  • setpoint.html:行政區域工具 用Winform 讀取百度地圖的經緯度:using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Dra....
  • 增加節點時,我們是這樣寫的:xop.Document.Element("messages").Add( new XElement("message", new XAttribute("event", ...
  • 經過十多天的艱苦奮戰,MyKTV點歌系統終於成型,從剛開始接到項目的茫然,到完成項目時的喜悅,整個過程的艱辛和付出只有自己知道.雖然這個項目還有許多需要完善的地方,譬如添加歌詞信息,實現窗體的美化等,這些在後續時間里我再一一進行一個完善吧!首先呢,我先將整個項目所能實現的功能做一個簡單的介紹,K.....
  • 配置代碼: 異常信息: [Error] An unhandled exception was thrown by the application.System.Security.Cryptography.CryptographicException: An error occurred while ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...