目錄
- 簡介
- 實作導航面板
- 實作方法
- 使用方法
- 實作標題欄
- 表單拖拽及最大化
- 自定義表單按鈕
- 標題顯示
- 按鈕設定
- 實作狀態欄
- 整體使用
- 參考文章
簡介
每次新專案都要想著界面怎么設計好,但想來想去上位機界面就那幾種,按照導航方式可分為:選單工具列導航、漢堡包導航、側邊導航等,我用的最多的是側邊導航,導航選單一般只有一級(最多二級),三級導航選單基本很少用到,
本文實作一個簡單的側邊導航Winform程式框架,以后開發專案可以直接用,話不多說上圖:

整個程式界面分為上、中、下三個區域,分別是:
- 標題區:顯示軟體的名稱、LOG、版本等資訊,實作表單標題欄的基本功能,
- 導航區:顯示整個軟體的主要內容,即導航選單和顯示面板,
- 狀態區:顯示軟體的狀態資訊,如用戶、報警、日志等,此狀態欄不是必須的,重要的狀態資訊也可以在標題欄顯示,
整個界面的實作可以分為三個部分:實作表單標題、實作導航面板、調整界面布局,主要的技術難點(或者說作業任務)集中在前面兩個部分,最終核心還是在于導航面板的實作,
下面將按重要程度依次介紹各個部分的實作程序,而且所有內容都盡量不使用VS的UI設計器,通過代碼來控制所有控制元件的屬性,
實作導航面板
為了代碼復用,導航面板封裝為一個用戶控制元件,主要功能就是點擊導航選單后顯示指定的界面,使用時直接傳入選單項名稱和對應的表單實體即可,
實作方法
新建一個用戶控制元件命名為LeftNavigation,在用戶控制元件上添加SplitContainer控制元件,不需要設定任何屬性,后臺代碼如下:
public partial class LeftNavigation : UserControl
{
/// <summary>
/// 生成選單按鈕的方法,可以自己設定按鈕的風格、屬性,主要按鈕的父類是Control即可
/// </summary>
public Func<Control> CreateBtnFunc=()=> { return null; };
/// <summary>
/// 選單按鈕的點擊事件,在處理完導航內容的顯示任務后觸發
/// </summary>
public event EventHandler BtnClick;
/// <summary>
/// 獲取或設定選單按鈕的背景顏色
/// </summary>
public Color BtnBackColor{ get; set; } = Color.FromArgb(30, 56, 83);
/// <summary>
/// 獲取或設定選單按鈕的選中顏色
/// </summary>
public Color BtnSelectkColor { get; set; } = Color.FromArgb(67, 92, 200);
/// <summary>
/// 獲取選單按鈕的大小,這個屬性基本上很少變動,所以只支持在建構式的引數中設定該值
/// </summary>
public Size BtnSize { get;}
/// <summary>
/// 獲取選單按鈕之間、選單按鈕與面板邊緣之間的間距,該值也作為拆分器的間隔設定值,
/// 這個屬性基本上很少變動,所以只支持在建構式的引數中設定該值,
/// </summary>
public int BtnInterval { get;}
/// <summary>
/// 建構式
/// </summary>
/// <param name="btnWidth">選單按鈕的寬度</param>
/// <param name="btnHeight">選單按鈕的高度</param>
/// <param name="btnInterval">間距值</param>
public LeftNavigation(int btnWidth=198, int btnHeight=66, int btnInterval=5)
{
InitializeComponent();
this.Dock = DockStyle.Fill;
//用戶控制元件太小時,splitContainer尺寸調整會失效
this.Size = new Size(1000, 1000);
BtnSize = new Size(btnWidth, btnHeight);
BtnInterval = btnInterval;
splitContainer1.Dock = DockStyle.Fill;
splitContainer1.SplitterWidth = BtnInterval;
splitContainer1.SplitterDistance = BtnSize.Width + BtnInterval * 2;
splitContainer1.IsSplitterFixed = true;
splitContainer1.FixedPanel = FixedPanel.Panel1;
splitContainer1.Panel1.AutoScroll = true;
splitContainer1.Panel2.AutoScroll = true;
splitContainer1.Panel1.BackColor = Color.White;
}
/// <summary>
/// 選單按鈕點擊事件處理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_Click(object sender, EventArgs e)
{
Control btnCtr = sender as Control;
foreach (var item in splitContainer1.Panel1.Controls)
{
Control btnItem = item as Control;
Form form = btnItem.Tag as Form;
if (btnItem == btnCtr)
{
btnItem.BackColor = BtnSelectkColor;
splitContainer1.Panel2.Controls.Clear();
if (form != null)
{
splitContainer1.Panel2.Controls.Add(form);
form.Visible = true;
}
}
else
{
btnItem.BackColor = BtnBackColor;
if (form != null) form.Visible = false;
}
}
if (BtnClick != null) BtnClick(sender, e);
}
/// <summary>
/// 生成選單按鈕
/// </summary>
/// <param name="text">顯示的文本</param>
/// <param name="tag">對應的表單實體</param>
/// <param name="point">按鈕位置</param>
/// <returns></returns>
private Control CreateBtnCtr(string text,Form tag,Point point)
{
Control btnCtr= CreateBtnFunc();
if (btnCtr == null)
{
Button btn = new Button();
btn.BackColor = BtnBackColor;
btn.Font = new Font("Tahoma", 20, FontStyle.Bold);
btn.ForeColor = Color.White;
btn.FlatStyle = FlatStyle.Flat;
btnCtr = btn;
}
btnCtr.Click += button_Click;
btnCtr.Text = text;
btnCtr.Tag = tag;
btnCtr.Size = BtnSize;
btnCtr.Location = new Point(point.X, point.Y);
return btnCtr;
}
/// <summary>
/// 設定導航資訊
/// </summary>
/// <param name="btnDic"></param>
public void SetNavigation(Dictionary<string,Form> btnDic)
{
splitContainer1.Panel1.Controls.Clear();
splitContainer1.Panel2.Controls.Clear();
Point point = new Point(BtnInterval, BtnInterval);
foreach (var item in btnDic)
{
if (item.Value != null)
{
item.Value.TopLevel = false;
item.Value.FormBorderStyle = FormBorderStyle.None;
item.Value.Dock = DockStyle.Fill;
item.Value.AutoScroll = true;
item.Value.Show();
}
if (!string.IsNullOrEmpty(item.Key))
{
Control btnCtr = CreateBtnCtr(item.Key, item.Value, point);
point.Y = point.Y + BtnSize.Height + BtnInterval;
splitContainer1.Panel1.Controls.Add(btnCtr);
}
}
if (splitContainer1.Panel1.Controls.Count > 0)
{
button_Click(splitContainer1.Panel1.Controls[0], new EventArgs());
}
}
}
有以下幾點需要注意:
- 為了使用方便,導航面板不支持隨意調整導航選單的寬度,只能在初始化時一次性指定,
- 為了簡化邏輯,表單實體系結在按鈕的Tag屬性上,每次點擊需要回圈點擊遍歷控制元件,此處可以根據自己的需求進行優化,
- 如果想在導航的子表單顯示時做一些操作,建議在表單的VisibleChanged事件中進行,
使用方法
使用起來也很簡單,在主界面中添加一個Panel控制元件命名為panel_Navigation,直接在主表單的建構式里面添加如下代碼:
LeftNavigation leftNavigation = new LeftNavigation();
Dictionary<string, Form> btnFormDic = new Dictionary<string, Form>()
{
{"form1",new Form1()},
{"form2",new Form2()},
{"form3",new Form3()},
{"退出",null}
};
leftNavigation.SetNavigation(btnFormDic);
leftNavigation.BtnClick += (sender, e) =>
{
Control ctr = sender as Control;
if (ctr.Text == "退出")
{
this.Close();
}
};
panel_Navigation.Controls.Add(leftNavigation);
實作標題欄
標題欄的功能和系統表單的標題欄功能類似,主要有表單拖拽及最大化、自定義表單按鈕兩個功能,自定義的標題欄擁有極大的可操作性,設計表單時還是很有必要的,
先將主表單重命名為MainForm,然后在主表單中添加一個Panel控制元件命名為panel_Title,
表單拖拽及最大化
在主界面的后臺代碼中實作表單拖拽及最大化功能,代碼如下:
#region 無邊框表單移動及最大化
// 滑鼠按下
private bool isMouse = false; // 滑鼠是否按下
// 原點位置
private int originX = 0;
private int originY = 0;
// 滑鼠按下位置
private int mouseX = 0;
private int mouseY = 0;
private void windowMove_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{ // 判斷滑鼠按鍵
isMouse = true;
// 螢屏坐標位置
originX = this.Location.X;
originY = this.Location.Y;
// 滑鼠按下位置
mouseX = originX + e.X;
mouseY = originY + e.Y;
}
}
// 滑鼠移動
private void windowMove_MouseMove(object sender, MouseEventArgs e)
{
if (isMouse)
{
// 移動距離
int moveX = (e.X + this.Location.X) - mouseX;
int moveY = (e.Y + this.Location.Y) - mouseY;
int targetX = originX + moveX;
int targetY = originY + moveY;
this.Location = new Point(targetX, targetY);
}
}
// 滑鼠釋放
private void windowMove_MouseUp(object sender, MouseEventArgs e)
{
if (isMouse)
{
isMouse = false;
}
}
// 滑鼠雙擊
private void windowMove_DoubleClick(object sender, MouseEventArgs e)
{
if (isMouse)
{
this.WindowState = this.WindowState == FormWindowState.Normal ? FormWindowState.Maximized : FormWindowState.Normal;
}
}
#endregion
上面只是滑鼠事件的處理程式,需要系結到控制元件上才會生效,這里使用Panel控制元件作為標題欄,系結到標題欄Panel控制元件的事件上即可,如果沒有使用控制元件作為標題欄,則需要系結到主表單的事件上,
在主界面的建構式里面添加系結事件,代碼如下:
this.FormBorderStyle = FormBorderStyle.None;
this.StartPosition = FormStartPosition.CenterScreen;
this.BackColor = SystemColors.ActiveCaption;
panel_Title.MouseDown += windowMove_MouseDown;
panel_Title.MouseUp += windowMove_MouseUp;
panel_Title.MouseMove += windowMove_MouseMove;
panel_Title.MouseDoubleClick += windowMove_DoubleClick;
自定義表單按鈕
自定義表單按鈕正常的操作是派生一個Button控制元件的子類,然后實作一些自定義的功能,這里不想搞得那么復雜,直接使用PictureBox控制元件作為按鈕即可,
在標題欄控制元件panel_Title中添加pictureBoxBtn_Min、pictureBoxBtn_Max、pictureBoxBtn_Close三個PictureBox控制元件,另外添加一個Label控制元件命名為label_Title顯示標題,
標題顯示
在主表單的建構式中設定標題Label控制元件的屬性,代碼如下:
label_Title.AutoSize = true;
label_Title.Anchor = AnchorStyles.None;
label_Title.Location = new Point((panel_Title.Size.Width - label_Title.Size.Width) / 2, (panel_Title.Size.Height - label_Title.Size.Height) / 2);
標題字體的樣式按自己的喜好設定,這里就不一一介紹了,
按鈕設定
首先需要匯入按鈕的圖示,分別是以下幾個(文末原始碼里面有完整圖示):
- 最小化

- 正常化

- 最大化

- 退出

在PictureBox控制元件的Image屬性中點擊“...”匯入,如下圖所示:


在主界面的后臺代碼中實作按鈕的單擊事件,代碼如下:
#region 自定義表單按鈕
//滑鼠進入按鈕
private void pictureBoxBtn_MouseEnter(object sender, EventArgs e)
{
PictureBox pictureBox = sender as PictureBox;
pictureBox.BackColor = Color.FromArgb(67, 92, 200);
}
//最小化
private void pictureBoxBtn_Min_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
//最大化
private void pictureBoxBtn_Max_Click(object sender, EventArgs e)
{
PictureBox pictureBox = sender as PictureBox;
if (this.WindowState == FormWindowState.Normal)
{
pictureBox.Image = Properties.Resources.MaxNormal;
this.WindowState = FormWindowState.Maximized;
}
else if (this.WindowState == FormWindowState.Maximized)
{
pictureBox.Image = Properties.Resources.Max;
this.WindowState = FormWindowState.Normal;
}
}
//退出
private void pictureBoxBtn_Close_Click(object sender, EventArgs e)
{
this.Close();
}
//滑鼠離開按鈕
private void pictureBoxBtn_MouseLeave(object sender, EventArgs e)
{
PictureBox pictureBox = sender as PictureBox;
pictureBox.BackColor = SystemColors.ActiveCaption;
}
#endregion
在主界面的建構式中設定按鈕屬性并系結事件,代碼如下:
pictureBoxBtn_Max.Image = Properties.Resources.Max;
pictureBoxBtn_Min.Anchor = AnchorStyles.Top | AnchorStyles.Right;
pictureBoxBtn_Max.Anchor = AnchorStyles.Top | AnchorStyles.Right;
pictureBoxBtn_Close.Anchor = AnchorStyles.Top | AnchorStyles.Right;
pictureBoxBtn_Min.SizeMode = PictureBoxSizeMode.CenterImage;
pictureBoxBtn_Max.SizeMode = PictureBoxSizeMode.CenterImage;
pictureBoxBtn_Close.SizeMode = PictureBoxSizeMode.CenterImage;
pictureBoxBtn_Min.Click += pictureBoxBtn_Min_Click;
pictureBoxBtn_Max.Click += pictureBoxBtn_Max_Click;
pictureBoxBtn_Close.Click += pictureBoxBtn_Close_Click;
pictureBoxBtn_Min.MouseEnter +=pictureBoxBtn_MouseEnter;
pictureBoxBtn_Max.MouseEnter += pictureBoxBtn_MouseEnter;
pictureBoxBtn_Close.MouseEnter += pictureBoxBtn_MouseEnter;
pictureBoxBtn_Min.MouseLeave += pictureBoxBtn_MouseLeave;
pictureBoxBtn_Max.MouseLeave += pictureBoxBtn_MouseLeave;
pictureBoxBtn_Close.MouseLeave += pictureBoxBtn_MouseLeave;
實作狀態欄
狀態欄就沒什么好說的,根據專案的情況自行添加,此處只實作一個實時時間顯示標簽意思一下,
在主界面中添加一個Panel控制元件命名為panel_State,在Panel控制元件中添加一個Label控制元件命名為label_Time,調整一下大小和位置,
在主界面的建構式中設定定時器,代碼如下:
label_Time.Text = DateTime.Now.ToString();
label_Time.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
Timer timer = new Timer();
timer.Interval = 1000;
timer.Tick += (sender, obj) =>
{
label_Time.Text = DateTime.Now.ToString();
};
timer.Start();
定時器的啟動有一點的延遲,需要先設定一下label_Time的顯示內容,
整體使用
在表單設定界面調整好Panel控制元件的尺寸,主要是標題欄、狀態欄的高度,
在主界面的建構式中設定Panel控制元件的布局,代碼如下:
panel_Title.Dock = DockStyle.Top;
panel_Navigation.Dock = DockStyle.Fill;
panel_State.Dock = DockStyle.Bottom;
//必須置于頂層
panel_Navigation.BringToFront();
然后新建Form1、Form2、Form3等表單,實作自己的業務邏輯,
本文專案的下載鏈接(提取碼:sfbk):https://pan.baidu.com/s/18w85F7ebwV1PFY-4CkmBWA
參考文章
- winform表單最大化、最小化、還原
- winform設定無邊框表單移動,效果更好看
- C#自定義Button按鈕控制元件
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/308052.html
標籤:WinForm
