網頁中浮動層的應用非常廣泛,但Windows程序中卻少有浮動層。難道Windows程序中不需要浮動層嗎 ?根據不同的需要實現相應的功能,有人會覺得直接在界面上添加控件更簡單,或者用對話窗口的方法實 現是一樣的,只要實現功能就可以了。當然,解決方法有很多種,這裡給出采用浮動層的實現方式。比如 在ComboBox控件中下拉選擇項時,只能顯示一列,而且項數很多的時候又不能查找,用一個帶查找功能的 多列下拉框會方便很多(如下圖)。
有些地方需要顯示多行信息,但空間卻比較小,像TextBox控件的Text屬性一樣,采用一個下拉文本框 來顯示和編輯,會更加靈活。
本示例包含三個組件,一個是提供下拉功能的組件DropdownComponent,一個是實現用鼠標移動控件位 置的組件MovableComponent,一個是實現用鼠標調整控件大小的組件ResizableComponent。在示例中包含 如何實現自定義可視化屬性設計器UITypeEditor,如何將一個屬性的值設置為多個枚舉以及分解成多個枚 舉,如何實現類似ToolTip給其他控件添加屬性的功能。
下面介紹各個組件的實現原理與要點:
下拉組件DropdownComponent
實現原理
1、點擊ComboBox的下拉標志時,顯示浮動控件
2、浮動控件失去焦點時,根據標志位判斷是否隱藏控件
實現要點
1、需要將ComboBox控件的DropDownHeight屬性設置為1(設置為0無效),避免出現多余的框
2、需要調整目標控件的顯示位置,不能超出窗體的邊框
3、可以根據需要不只針對ComboBox控件,只要在一個事件處理方法中顯示浮動控件即可
該組件實現起來非常簡單,主要是處理ComboBox控件的DropDown事件,以及浮動控件的Leave事件。
首先聲明內部字段和屬性
#region 字段和屬性
private const string c_ControlCategory = "控制";
private ComboBox m_TargetComboBox = null;
[Category(c_ControlCategory), Description("目標下拉控件。")]
public ComboBox TargetComboBox
{
get { return m_TargetComboBox; }
set
{
if (this.DesignMode)
{
m_TargetComboBox = value;
}
else
{
if (m_TargetComboBox != value)
{
this.RemoveDropDownEventHandler();
m_TargetComboBox = value;
this.AddDropDownEventHandler();
}
}
}
}
private Control m_FloatControl = null;
[Category(c_ControlCategory), Description("浮動控件。")]
public Control FloatControl
{
get { return m_FloatControl; }
set
{
if (this.DesignMode)
{
m_FloatControl = value;
}
else
{
if (m_FloatControl != value)
{
this.RemoveLeaveEventHandler();
m_FloatControl = value;
this.AddLeaveEventHanlder();
}
}
}
}
private bool m_AutoHide = true;
[Category(c_ControlCategory), Description("指示在浮動控件失去焦點時自動隱藏。 "), DefaultValue(true)]
public bool AutoHide
{
get { return m_AutoHide; }
set { m_AutoHide = value; }
}
private DropDownControlWidthModeEnum m_FloatControlWidthMode = DropDownControlWidthModeEnum.UserDefine;
[Category(c_ControlCategory),Description("下拉浮動控件的寬度模 式。"),DefaultValue(DropDownControlWidthModeEnum.UserDefine)]
public DropDownControlWidthModeEnum FloatControlWidthMode
{
get { return m_FloatControlWidthMode; }
set { m_FloatControlWidthMode = value; }
}
#endregion 字段和屬性
其中的FloatControlWidthMode屬性用來指示浮動控件的寬度模式,當設置為UserDefine時,按照控件 的設置尺寸顯示,當設置為AutoWidth時,調整浮動控件的寬度與下拉框的寬度相等。
下面是下拉框事件處理以及相關的方法。其中將顯示和隱藏方法重載了,一組是顯示指定的控件,一 組是用於顯示外部控件,而且都設置為public。這裡和之前用Label模擬網頁鏈接的組件一樣,可以根據 需要選擇是否公開方法,比如用其他控件控制顯示浮動控件就必須用public。還有在顯示浮動控件時,將 浮動控件添加到窗體中,這是為了避免在其他容器中,浮動控件只顯示一部分,添加到窗體後則可以完整 顯示,不會被其他容器遮擋。
#region 浮動控件操作及事件處理
private void AddDropDownEventHandler()
{
if (m_TargetComboBox != null)
{
TargetComboBox.DropDown += new EventHandler (TargetComboBox_DropDown);
this.m_TargetComboBox.DropDownHeight = 1;
}
}
private void RemoveDropDownEventHandler()
{
if (m_TargetComboBox != null)
{
TargetComboBox.DropDown -= new EventHandler (TargetComboBox_DropDown);
this.m_TargetComboBox.DropDownHeight = 106;
}
}
private void AddLeaveEventHanlder()
{
if (m_FloatControl != null)
{
FloatControl.Leave += new EventHandler (FloatControl_Leave);
this.m_FloatControl.Visible = false;
}
}
private void RemoveLeaveEventHandler()
{
if (m_FloatControl != null)
{
FloatControl.Leave -= new EventHandler (FloatControl_Leave);
}
}
void FloatControl_Leave(object sender, EventArgs e)
{
if (m_FloatControl != null && m_AutoHide)
this.HideFloatControl(this.m_FloatControl);
}
void TargetComboBox_DropDown(object sender, EventArgs e)
{
if (m_FloatControl != null)
this.ShowFloatControl(this.m_TargetComboBox, this.m_FloatControl);
}
/// <summary>
/// 顯示設置的浮動控件
/// </summary>
public void ShowFloatControl()
{
this.ShowFloatControl(this.m_TargetComboBox, this.m_FloatControl);
}
/// <summary>
/// 隱藏設置的浮動控件
/// </summary>
public void HideFloatControl()
{
this.HideFloatControl(this.m_FloatControl);
}
#endregion 浮動控件操作及事件處理
#region 浮動控件公共操作
/// <summary>
/// 顯示指定的浮動控件
/// </summary>
/// <param name="DropdownControl">下拉控件,可以不是 ComboBox</param>
/// <param name="FloatControl">要顯示的控件</param>
public void ShowFloatControl(Control DropdownControl, Control FloatControl)
{
if (DropdownControl == null)
throw new ArgumentNullException("DropdownControl", "指定的下 拉控件為null。");
if (FloatControl == null)
throw new ArgumentNullException("FloatControl", "指定要顯示的 浮動控件為null。");
if (this.OnFloatControlDisplayChanging(FloatControl).Cancel)
return;
Form ParentForm = DropdownControl.FindForm();
//將浮動控件添加到窗體上
if (!ParentForm.Controls.Contains(FloatControl))
ParentForm.Controls.Add(FloatControl);
//調整浮動控件寬度
if (this.m_FloatControlWidthMode == DropDownControlWidthModeEnum.AutoWidth)
FloatControl.Width = DropdownControl.Width;
//計算坐標
Control ParentControl = DropdownControl.Parent;
int intLeft = 0, intTop = 0;
intLeft = DropdownControl.Left;
intTop = DropdownControl.Top + DropdownControl.Height;
//獲取相對窗體的位置
while (ParentControl != ParentForm)
{
intLeft += ParentControl.Left;
intTop += ParentControl.Top;
ParentControl = ParentControl.Parent;
}
//判斷是否超出窗體范圍
if (intLeft + FloatControl.Width > ParentForm.Width)
{
int intOffset = FloatControl.Width - DropdownControl.Width;
if (intLeft - intOffset > 0)
{
intLeft -= intOffset;
}
if (this.m_FloatControlWidthMode == DropDownControlWidthModeEnum.AutoWidth)
FloatControl.Width = FloatControl.Width - intOffset;
}
if (intTop + FloatControl.Height > ParentForm.Height)
{
int intOffset = FloatControl.Height + DropdownControl.Height;
if (intTop - intOffset > 0)
{
intTop -= intOffset;
}
}
//設置浮動控件位置
FloatControl.Left = intLeft;
FloatControl.Top = intTop;
//顯示浮動控件
FloatControl.Visible = true;
FloatControl.BringToFront();
FloatControl.Select();
this.OnFloatControlDisplayChanged(FloatControl);
}
/// <summary>
/// 隱藏指定的浮動控件
/// </summary>
/// <param name="FloatControl">要隱藏的控件</param>
public void HideFloatControl(Control FloatControl)
{
if (FloatControl == null)
throw new ArgumentNullException("FloatControl", "指定要顯示的 浮動控件為null。");
Form ParentForm = this.m_TargetComboBox.FindForm();
if (ParentForm.Controls.Contains(FloatControl))
{
if (this.OnFloatControlDisplayChanging(FloatControl).Cancel)
return;
ParentForm.Controls.Remove(FloatControl);
FloatControl.SendToBack();
FloatControl.Visible = false;
this.OnFloatControlDisplayChanged(FloatControl);
}
}
#endregion 浮動控件公共操作
//另外組件中還添加了幾個事件,可以更靈活控制浮動控件的顯示。
#region 內部事件及事件處理
/// <summary>
/// 浮動控件顯示狀態即將改變事件
/// </summary>
[Description("浮動控件顯示狀態即將改變事件。")]
public event FloatControlDisplayChangingEventHandler FloatControlDisplayChanging;
/// <summary>
/// 浮動控件顯示狀態已改變事件
/// </summary>
[Description("浮動控件顯示狀態已改變事件。")]
public event FloatControlDisplayChangdEventHandler FloatControlDisplayChanged;
private FloatControlDisplayChangingEventArgs OnFloatControlDisplayChanging (Control FloatControl)
{
FloatControlDisplayChangingEventArgs myEventArgs = new FloatControlDisplayChangingEventArgs(FloatControl);
if (this.FloatControlDisplayChanging != null)
this.FloatControlDisplayChanging(this, myEventArgs);
return myEventArgs;
}
private void OnFloatControlDisplayChanged(Control FloatControl)
{
if (this.FloatControlDisplayChanged != null)
this.FloatControlDisplayChanged(this, new FloatControlDisplayChangedEventArgs(FloatControl));
}
#endregion 內部事件及事件處理
下面介紹鼠標相關的兩個組件,這兩個組件有一定的關聯性。
可移動組件MovableComponent
實現原理
1、鼠標移動到指定控件上,改變鼠標樣式為可移動狀態
2、按下鼠標時記錄鼠標的當前位置
3、鼠標移動時檢測是否按下左鍵,如果按下左鍵則根據當前位置和之前記錄的位置計算位移
4、根據鼠標的位移設置控件的坐標
5、鼠標離開則恢復默認鼠標樣式
實現要點
1、被移動控件和響應鼠標操作的控件不一定是同一個,比如示例中列標題響應操作,內容區域不響應 ,移動的是最外層的那個Panel。需要設置兩個屬性,一個響應操作,一個被移動,兩者可以一致。
2、響應操作的控件內部子控件也要有不同的響應,比如示例中標題欄中的圖標和標題文字響應操作, 但關閉按鈕不響應。這裡用擴展屬性實現該功能,可以給內部控件添加一個是否響應操作的屬性,讓設置 更加靈活。
本文配套源碼:http://www.bianceng.net/dotnet/201212/730.htm