RaUI/Source/ryControls/ObjectListView/SubControls/GlassPanelForm.cs
鑫Intel 523add43be ### 2021-01-12 dev更新
------
#### ryControls    V2.1.2101.1201
- *.[更新]内置的ObjectListView从1.13更新到2.9.1版本,并对主要属性进行汉化。
- *.[修复]修复新版ObjectListView选中项有筛选结果时,筛选结果白色字体看不清的BUG。
- *.[改进]TextBoxEx2默认事件改为TextChanged2。
2021-01-12 16:32:29 +08:00

460 lines
18 KiB
C#

/*
* GlassPanelForm - A transparent form that is placed over an ObjectListView
* to allow flicker-free overlay images during scrolling.
*
* Author: Phillip Piper
* Date: 14/04/2009 4:36 PM
*
* Change log:
* 2010-08-18 JPP - Added WS_EX_TOOLWINDOW style so that the form won't appear in Alt-Tab list.
* v2.4
* 2010-03-11 JPP - Work correctly in MDI applications -- more or less. Actually, less than more.
* They don't crash but they don't correctly handle overlapping MDI children.
* Overlays from one control are shown on top of other other windows.
* 2010-03-09 JPP - Correctly Unbind() when the related ObjectListView is disposed.
* 2009-10-28 JPP - Use FindForm() rather than TopMostControl, since the latter doesn't work
* as I expected when the OLV is part of an MDI child window. Thanks to
* wvd_vegt who tracked this down.
* v2.3
* 2009-08-19 JPP - Only hide the glass pane on resize, not on move
* - Each glass panel now only draws one overlays
* v2.2
* 2009-06-05 JPP - Handle when owning window is a topmost window
* 2009-04-14 JPP - Initial version
*
* To do:
*
* Copyright (C) 2009-2014 Phillip Piper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace BrightIdeasSoftware
{
/// <summary>
/// A GlassPanelForm sits transparently over an ObjectListView to show overlays.
/// </summary>
internal partial class GlassPanelForm : Form
{
public GlassPanelForm() {
this.Name = "GlassPanelForm";
this.Text = "GlassPanelForm";
ClientSize = new System.Drawing.Size(0, 0);
ControlBox = false;
FormBorderStyle = FormBorderStyle.None;
SizeGripStyle = SizeGripStyle.Hide;
StartPosition = FormStartPosition.Manual;
MaximizeBox = false;
MinimizeBox = false;
ShowIcon = false;
ShowInTaskbar = false;
FormBorderStyle = FormBorderStyle.None;
SetStyle(ControlStyles.Selectable, false);
this.Opacity = 0.5f;
this.BackColor = Color.FromArgb(255, 254, 254, 254);
this.TransparencyKey = this.BackColor;
this.HideGlass();
NativeMethods.ShowWithoutActivate(this);
}
protected override void Dispose(bool disposing) {
if (disposing)
this.Unbind();
base.Dispose(disposing);
}
#region Properties
/// <summary>
/// Get the low-level windows flag that will be given to CreateWindow.
/// </summary>
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
cp.ExStyle |= 0x80; // WS_EX_TOOLWINDOW
return cp;
}
}
#endregion
#region Commands
/// <summary>
/// Attach this form to the given ObjectListView
/// </summary>
public void Bind(ObjectListView olv, IOverlay overlay) {
if (this.objectListView != null)
this.Unbind();
this.objectListView = olv;
this.Overlay = overlay;
this.mdiClient = null;
this.mdiOwner = null;
if (this.objectListView == null)
return;
// NOTE: If you listen to any events here, you *must* stop listening in Unbind()
this.objectListView.Disposed += new EventHandler(objectListView_Disposed);
this.objectListView.LocationChanged += new EventHandler(objectListView_LocationChanged);
this.objectListView.SizeChanged += new EventHandler(objectListView_SizeChanged);
this.objectListView.VisibleChanged += new EventHandler(objectListView_VisibleChanged);
this.objectListView.ParentChanged += new EventHandler(objectListView_ParentChanged);
// Collect our ancestors in the widget hierachy
if (this.ancestors == null)
this.ancestors = new List<Control>();
Control parent = this.objectListView.Parent;
while (parent != null) {
this.ancestors.Add(parent);
parent = parent.Parent;
}
// Listen for changes in the hierachy
foreach (Control ancestor in this.ancestors) {
ancestor.ParentChanged += new EventHandler(objectListView_ParentChanged);
TabControl tabControl = ancestor as TabControl;
if (tabControl != null) {
tabControl.Selected += new TabControlEventHandler(tabControl_Selected);
}
}
// Listen for changes in our owning form
this.Owner = this.objectListView.FindForm();
this.myOwner = this.Owner;
if (this.Owner != null) {
this.Owner.LocationChanged += new EventHandler(Owner_LocationChanged);
this.Owner.SizeChanged += new EventHandler(Owner_SizeChanged);
this.Owner.ResizeBegin += new EventHandler(Owner_ResizeBegin);
this.Owner.ResizeEnd += new EventHandler(Owner_ResizeEnd);
if (this.Owner.TopMost) {
// We can't do this.TopMost = true; since that will activate the panel,
// taking focus away from the owner of the listview
NativeMethods.MakeTopMost(this);
}
// We need special code to handle MDI
this.mdiOwner = this.Owner.MdiParent;
if (this.mdiOwner != null) {
this.mdiOwner.LocationChanged += new EventHandler(Owner_LocationChanged);
this.mdiOwner.SizeChanged += new EventHandler(Owner_SizeChanged);
this.mdiOwner.ResizeBegin += new EventHandler(Owner_ResizeBegin);
this.mdiOwner.ResizeEnd += new EventHandler(Owner_ResizeEnd);
// Find the MDIClient control, which houses all MDI children
foreach (Control c in this.mdiOwner.Controls) {
this.mdiClient = c as MdiClient;
if (this.mdiClient != null) {
break;
}
}
if (this.mdiClient != null) {
this.mdiClient.ClientSizeChanged += new EventHandler(myMdiClient_ClientSizeChanged);
}
}
}
this.UpdateTransparency();
}
void myMdiClient_ClientSizeChanged(object sender, EventArgs e) {
this.RecalculateBounds();
this.Invalidate();
}
/// <summary>
/// Made the overlay panel invisible
/// </summary>
public void HideGlass() {
if (!this.isGlassShown)
return;
this.isGlassShown = false;
this.Bounds = new Rectangle(-10000, -10000, 1, 1);
}
/// <summary>
/// Show the overlay panel in its correctly location
/// </summary>
/// <remarks>
/// If the panel is always shown, this method does nothing.
/// If the panel is being resized, this method also does nothing.
/// </remarks>
public void ShowGlass() {
if (this.isGlassShown || this.isDuringResizeSequence)
return;
this.isGlassShown = true;
this.RecalculateBounds();
}
/// <summary>
/// Detach this glass panel from its previous ObjectListView
/// </summary>
/// <remarks>
/// You should unbind the overlay panel before making any changes to the
/// widget hierarchy.
/// </remarks>
public void Unbind() {
if (this.objectListView != null) {
this.objectListView.Disposed -= new EventHandler(objectListView_Disposed);
this.objectListView.LocationChanged -= new EventHandler(objectListView_LocationChanged);
this.objectListView.SizeChanged -= new EventHandler(objectListView_SizeChanged);
this.objectListView.VisibleChanged -= new EventHandler(objectListView_VisibleChanged);
this.objectListView.ParentChanged -= new EventHandler(objectListView_ParentChanged);
this.objectListView = null;
}
if (this.ancestors != null) {
foreach (Control parent in this.ancestors) {
parent.ParentChanged -= new EventHandler(objectListView_ParentChanged);
TabControl tabControl = parent as TabControl;
if (tabControl != null) {
tabControl.Selected -= new TabControlEventHandler(tabControl_Selected);
}
}
this.ancestors = null;
}
if (this.myOwner != null) {
this.myOwner.LocationChanged -= new EventHandler(Owner_LocationChanged);
this.myOwner.SizeChanged -= new EventHandler(Owner_SizeChanged);
this.myOwner.ResizeBegin -= new EventHandler(Owner_ResizeBegin);
this.myOwner.ResizeEnd -= new EventHandler(Owner_ResizeEnd);
this.myOwner = null;
}
if (this.mdiOwner != null) {
this.mdiOwner.LocationChanged -= new EventHandler(Owner_LocationChanged);
this.mdiOwner.SizeChanged -= new EventHandler(Owner_SizeChanged);
this.mdiOwner.ResizeBegin -= new EventHandler(Owner_ResizeBegin);
this.mdiOwner.ResizeEnd -= new EventHandler(Owner_ResizeEnd);
this.mdiOwner = null;
}
if (this.mdiClient != null) {
this.mdiClient.ClientSizeChanged -= new EventHandler(myMdiClient_ClientSizeChanged);
this.mdiClient = null;
}
}
#endregion
#region Event Handlers
void objectListView_Disposed(object sender, EventArgs e) {
this.Unbind();
}
/// <summary>
/// Handle when the form that owns the ObjectListView begins to be resized
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Owner_ResizeBegin(object sender, EventArgs e) {
// When the top level window is being resized, we just want to hide
// the overlay window. When the resizing finishes, we want to show
// the overlay window, if it was shown before the resize started.
this.isDuringResizeSequence = true;
this.wasGlassShownBeforeResize = this.isGlassShown;
}
/// <summary>
/// Handle when the form that owns the ObjectListView finished to be resized
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Owner_ResizeEnd(object sender, EventArgs e) {
this.isDuringResizeSequence = false;
if (this.wasGlassShownBeforeResize)
this.ShowGlass();
}
/// <summary>
/// The owning form has moved. Move the overlay panel too.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Owner_LocationChanged(object sender, EventArgs e) {
if (this.mdiOwner != null)
this.HideGlass();
else
this.RecalculateBounds();
}
/// <summary>
/// The owning form is resizing. Hide our overlay panel until the resizing stops
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Owner_SizeChanged(object sender, EventArgs e) {
this.HideGlass();
}
/// <summary>
/// Handle when the bound OLV changes its location. The overlay panel must
/// be moved too, IFF it is currently visible.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void objectListView_LocationChanged(object sender, EventArgs e) {
if (this.isGlassShown) {
this.RecalculateBounds();
}
}
/// <summary>
/// Handle when the bound OLV changes size. The overlay panel must
/// resize too, IFF it is currently visible.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void objectListView_SizeChanged(object sender, EventArgs e) {
// This event is triggered in all sorts of places, and not always when the size changes.
//if (this.isGlassShown) {
// this.Size = this.objectListView.ClientSize;
//}
}
/// <summary>
/// Handle when the bound OLV is part of a TabControl and that
/// TabControl changes tabs. The overlay panel is hidden. The
/// first time the bound OLV is redrawn, the overlay panel will
/// be shown again.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void tabControl_Selected(object sender, TabControlEventArgs e) {
this.HideGlass();
}
/// <summary>
/// Somewhere the parent of the bound OLV has changed. Update
/// our events.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void objectListView_ParentChanged(object sender, EventArgs e) {
ObjectListView olv = this.objectListView;
IOverlay overlay = this.Overlay;
this.Unbind();
this.Bind(olv, overlay);
}
/// <summary>
/// Handle when the bound OLV changes its visibility.
/// The overlay panel should match the OLV's visibility.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void objectListView_VisibleChanged(object sender, EventArgs e) {
if (this.objectListView.Visible)
this.ShowGlass();
else
this.HideGlass();
}
#endregion
#region Implementation
protected override void OnPaint(PaintEventArgs e) {
if (this.objectListView == null || this.Overlay == null)
return;
Graphics g = e.Graphics;
g.TextRenderingHint = ObjectListView.TextRenderingHint;
g.SmoothingMode = ObjectListView.SmoothingMode;
//g.DrawRectangle(new Pen(Color.Green, 4.0f), this.ClientRectangle);
// If we are part of an MDI app, make sure we don't draw outside the bounds
if (this.mdiClient != null) {
Rectangle r = mdiClient.RectangleToScreen(mdiClient.ClientRectangle);
Rectangle r2 = this.objectListView.RectangleToClient(r);
g.SetClip(r2, System.Drawing.Drawing2D.CombineMode.Intersect);
}
this.Overlay.Draw(this.objectListView, g, this.objectListView.ClientRectangle);
}
protected void RecalculateBounds() {
if (!this.isGlassShown)
return;
Rectangle rect = this.objectListView.ClientRectangle;
rect.X = 0;
rect.Y = 0;
this.Bounds = this.objectListView.RectangleToScreen(rect);
}
internal void UpdateTransparency() {
ITransparentOverlay transparentOverlay = this.Overlay as ITransparentOverlay;
if (transparentOverlay == null)
this.Opacity = this.objectListView.OverlayTransparency / 255.0f;
else
this.Opacity = transparentOverlay.Transparency / 255.0f;
}
protected override void WndProc(ref Message m) {
const int WM_NCHITTEST = 132;
const int HTTRANSPARENT = -1;
switch (m.Msg) {
// Ignore all mouse interactions
case WM_NCHITTEST:
m.Result = (IntPtr)HTTRANSPARENT;
break;
}
base.WndProc(ref m);
}
#endregion
#region Implementation variables
internal IOverlay Overlay;
#endregion
#region Private variables
private ObjectListView objectListView;
private bool isDuringResizeSequence;
private bool isGlassShown;
private bool wasGlassShownBeforeResize;
// Cache these so we can unsubscribe from events even when the OLV has been disposed.
private Form myOwner;
private Form mdiOwner;
private List<Control> ancestors;
MdiClient mdiClient;
#endregion
}
}