Used by Scheduler for Source file selection
git-svn-id: https://duplicati.googlecode.com/svn/trunk@1016 59da171f-624f-0410-aa54-27559c288bec
This commit is contained in:
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* 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-2010 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@bigfoot.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
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);
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
// 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);
|
||||
|
||||
Control parent = this.objectListView.Parent;
|
||||
while (parent != null) {
|
||||
parent.ParentChanged += new EventHandler(objectListView_ParentChanged);
|
||||
TabControl tabControl = parent as TabControl;
|
||||
if (tabControl != null) {
|
||||
tabControl.Selected += new TabControlEventHandler(tabControl_Selected);
|
||||
}
|
||||
parent = parent.Parent;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
MdiClient mdiClient;
|
||||
/// <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)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
Control parent = this.objectListView.Parent;
|
||||
while (parent != null) {
|
||||
parent.ParentChanged -= new EventHandler(objectListView_ParentChanged);
|
||||
TabControl tabControl = parent as TabControl;
|
||||
if (tabControl != null) {
|
||||
tabControl.Selected -= new TabControlEventHandler(tabControl_Selected);
|
||||
}
|
||||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (this.mdiClient != null) {
|
||||
this.mdiClient.ClientSizeChanged -= new EventHandler(myMdiClient_ClientSizeChanged);
|
||||
}
|
||||
|
||||
this.objectListView = 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;
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
/*
|
||||
* HeaderControl - A limited implementation of HeaderControl
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 25/11/2008 17:15
|
||||
*
|
||||
* Change log:
|
||||
* 2011-05-11 JPP - Fixed bug that prevented columns from being resized in IDE Designer
|
||||
* by dragging the column divider
|
||||
* 2011-04-12 JPP - Added ability to draw filter indicator in a column's header
|
||||
* v2.4.1
|
||||
* 2010-08-23 JPP - Added ability to draw header vertically (thanks to Mark Fenwick)
|
||||
* - Uses OLVColumn.HeaderTextAlign to decide how to align the column's header
|
||||
* 2010-08-08 JPP - Added ability to have image in header
|
||||
* v2.4
|
||||
* 2010-03-22 JPP - Draw header using header styles
|
||||
* 2009-10-30 JPP - Plugged GDI resource leak, where font handles were created during custom
|
||||
* drawing, but never destroyed
|
||||
* v2.3
|
||||
* 2009-10-03 JPP - Handle when ListView.HeaderFormatStyle is None
|
||||
* 2009-08-24 JPP - Handle the header being destroyed
|
||||
* v2.2.1
|
||||
* 2009-08-16 JPP - Correctly handle header themes
|
||||
* 2009-08-15 JPP - Added formatting capabilities: font, color, word wrap
|
||||
* v2.2
|
||||
* 2009-06-01 JPP - Use ToolTipControl
|
||||
* 2009-05-10 JPP - Removed all unsafe code
|
||||
* 2008-11-25 JPP - Initial version
|
||||
*
|
||||
* TO DO:
|
||||
* - Put drawing code into header style object, so that it can be easily customized.
|
||||
*
|
||||
* Copyright (C) 2006-2010 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@bigfoot.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms.VisualStyles;
|
||||
using System.Drawing.Drawing2D;
|
||||
using BrightIdeasSoftware.Properties;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// Class used to capture window messages for the header of the list view
|
||||
/// control.
|
||||
/// </summary>
|
||||
public class HeaderControl : NativeWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a header control for the given ObjectListView.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
public HeaderControl(ObjectListView olv) {
|
||||
this.ListView = olv;
|
||||
this.AssignHandle(NativeMethods.GetHeaderControl(olv));
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Return the index of the column under the current cursor position,
|
||||
/// or -1 if the cursor is not over a column
|
||||
/// </summary>
|
||||
/// <returns>Index of the column under the cursor, or -1</returns>
|
||||
public int ColumnIndexUnderCursor {
|
||||
get {
|
||||
Point pt = this.ListView.PointToClient(Cursor.Position);
|
||||
pt.X += NativeMethods.GetScrollPosition(this.ListView, true);
|
||||
return NativeMethods.GetColumnUnderPoint(this.Handle, pt);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the Windows handle behind this control
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When an ObjectListView is initialized as part of a UserControl, the
|
||||
/// GetHeaderControl() method returns 0 until the UserControl is
|
||||
/// completely initialized. So the AssignHandle() call in the constructor
|
||||
/// doesn't work. So we override the Handle property so value is always
|
||||
/// current.
|
||||
/// </remarks>
|
||||
public new IntPtr Handle {
|
||||
get { return NativeMethods.GetHeaderControl(this.ListView); }
|
||||
}
|
||||
//TODO: The Handle property may no longer be necessary. CHECK! 2008/11/28
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a style that should be applied to the font of the
|
||||
/// column's header text when the mouse is over that column
|
||||
/// </summary>
|
||||
/// <remarks>THIS IS EXPERIMENTAL. USE AT OWN RISK. August 2009</remarks>
|
||||
[Obsolete("Use HeaderStyle.Hot.FontStyle instead")]
|
||||
public FontStyle HotFontStyle {
|
||||
get { return FontStyle.Regular; }
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the cursor is over a "locked" divider, i.e.
|
||||
/// one that cannot be dragged by the user.
|
||||
/// </summary>
|
||||
protected bool IsCursorOverLockedDivider {
|
||||
get {
|
||||
Point pt = this.ListView.PointToClient(Cursor.Position);
|
||||
pt.X += NativeMethods.GetScrollPosition(this.ListView, true);
|
||||
int dividerIndex = NativeMethods.GetDividerUnderPoint(this.Handle, pt);
|
||||
if (dividerIndex >= 0 && dividerIndex < this.ListView.Columns.Count) {
|
||||
OLVColumn column = this.ListView.GetColumn(dividerIndex);
|
||||
return column.IsFixedWidth || column.FillsFreeSpace;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the listview that this header belongs to
|
||||
/// </summary>
|
||||
protected ObjectListView ListView {
|
||||
get { return this.listView; }
|
||||
set { this.listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum height of the header. -1 means no maximum.
|
||||
/// </summary>
|
||||
public int MaximumHeight {
|
||||
get { return this.ListView.HeaderMaximumHeight; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the ToolTip that shows tips for the header
|
||||
/// </summary>
|
||||
public ToolTipControl ToolTip {
|
||||
get {
|
||||
if (this.toolTip == null) {
|
||||
this.CreateToolTip();
|
||||
}
|
||||
return this.toolTip;
|
||||
}
|
||||
protected set { this.toolTip = value; }
|
||||
}
|
||||
private ToolTipControl toolTip;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the text in column headers should be word
|
||||
/// wrapped when it is too long to fit within the column
|
||||
/// </summary>
|
||||
public bool WordWrap {
|
||||
get { return this.wordWrap; }
|
||||
set { this.wordWrap = value; }
|
||||
}
|
||||
private bool wordWrap;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Calculate how height the header needs to be
|
||||
/// </summary>
|
||||
/// <returns>Height in pixels</returns>
|
||||
protected int CalculateHeight(Graphics g) {
|
||||
TextFormatFlags flags = this.TextFormatFlags;
|
||||
int columnUnderCursor = this.ColumnIndexUnderCursor;
|
||||
float height = 0.0f;
|
||||
for (int i = 0; i < this.ListView.Columns.Count; i++) {
|
||||
OLVColumn column = this.ListView.GetColumn(i);
|
||||
height = Math.Max(height, CalculateColumnHeight(g, column, flags, columnUnderCursor == i, i));
|
||||
}
|
||||
return this.MaximumHeight == -1 ? (int)height : Math.Min(this.MaximumHeight, (int)height);
|
||||
}
|
||||
|
||||
private float CalculateColumnHeight(Graphics g, OLVColumn column, TextFormatFlags flags, bool isHot, int i) {
|
||||
Font f = this.CalculateFont(column, isHot, false);
|
||||
if (column.IsHeaderVertical)
|
||||
return TextRenderer.MeasureText(g, column.Text, f, new Size(10000, 10000), flags).Width;
|
||||
|
||||
const int fudge = 9; // 9 is a magic constant that makes it perfectly match XP behavior
|
||||
if (!this.WordWrap)
|
||||
return f.Height + fudge;
|
||||
|
||||
Rectangle r = this.GetItemRect(i);
|
||||
r.Width -= 6; // Match the "tweaking" done in CustomRender
|
||||
if (this.HasNonThemedSortIndicator(column))
|
||||
r.Width -= 16;
|
||||
if (column.HasHeaderImage)
|
||||
r.Width -= column.ImageList.ImageSize.Width + 3;
|
||||
if (this.HasFilterIndicator(column))
|
||||
r.Width -= this.CalculateFilterIndicatorWidth(r);
|
||||
SizeF size = TextRenderer.MeasureText(g, column.Text, f, new Size(r.Width, 100), flags);
|
||||
return size.Height + fudge;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should the given column show a sort indicator?
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HasSortIndicator(OLVColumn column) {
|
||||
if (!this.ListView.ShowSortIndicators) return false;
|
||||
return column == this.ListView.LastSortColumn && this.ListView.LastSortOrder != SortOrder.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should the given column be drawn with a filter indicator against it?
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HasFilterIndicator(OLVColumn column) {
|
||||
return (this.ListView.UseFiltering && this.ListView.UseFilterIndicator && column.HasFilterIndicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should the given column show a non-themed sort indicator?
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HasNonThemedSortIndicator(OLVColumn column) {
|
||||
if (!this.ListView.ShowSortIndicators) return false;
|
||||
if (VisualStyleRenderer.IsSupported)
|
||||
return !VisualStyleRenderer.IsElementDefined(VisualStyleElement.Header.SortArrow.SortedUp) &&
|
||||
this.HasSortIndicator(column);
|
||||
else
|
||||
return this.HasSortIndicator(column);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the bounds of the item with the given index
|
||||
/// </summary>
|
||||
/// <param name="itemIndex"></param>
|
||||
/// <returns></returns>
|
||||
public Rectangle GetItemRect(int itemIndex) {
|
||||
const int HDM_FIRST = 0x1200;
|
||||
const int HDM_GETITEMRECT = HDM_FIRST + 7;
|
||||
NativeMethods.RECT r = new NativeMethods.RECT();
|
||||
NativeMethods.SendMessageRECT(this.Handle, HDM_GETITEMRECT, itemIndex, ref r);
|
||||
return Rectangle.FromLTRB(r.left, r.top, r.right, r.bottom);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force the header to redraw by invalidating it
|
||||
/// </summary>
|
||||
public void Invalidate() {
|
||||
NativeMethods.InvalidateRect(this.Handle, 0, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tooltip
|
||||
|
||||
/// <summary>
|
||||
/// Create a native tool tip control for this listview
|
||||
/// </summary>
|
||||
protected virtual void CreateToolTip() {
|
||||
this.ToolTip = new ToolTipControl();
|
||||
this.ToolTip.Create(this.Handle);
|
||||
this.ToolTip.AddTool(this);
|
||||
this.ToolTip.Showing += new EventHandler<ToolTipShowingEventArgs>(this.ListView.headerToolTip_Showing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Windows messaging
|
||||
|
||||
/// <summary>
|
||||
/// Override the basic message pump
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
|
||||
protected override void WndProc(ref Message m) {
|
||||
const int WM_DESTROY = 2;
|
||||
const int WM_SETCURSOR = 0x20;
|
||||
const int WM_NOTIFY = 0x4E;
|
||||
const int WM_MOUSEMOVE = 0x200;
|
||||
const int HDM_FIRST = 0x1200;
|
||||
const int HDM_LAYOUT = (HDM_FIRST + 5);
|
||||
|
||||
switch (m.Msg) {
|
||||
case WM_SETCURSOR:
|
||||
if (!this.HandleSetCursor(ref m))
|
||||
return;
|
||||
break;
|
||||
|
||||
case WM_NOTIFY:
|
||||
if (!this.HandleNotify(ref m))
|
||||
return;
|
||||
break;
|
||||
|
||||
case WM_MOUSEMOVE:
|
||||
if (!this.HandleMouseMove(ref m))
|
||||
return;
|
||||
break;
|
||||
|
||||
case HDM_LAYOUT:
|
||||
if (!this.HandleLayout(ref m))
|
||||
return;
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
if (!this.HandleDestroy(ref m))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the SetCursor windows message
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HandleSetCursor(ref Message m) {
|
||||
if (this.IsCursorOverLockedDivider) {
|
||||
m.Result = (IntPtr)1; // Don't change the cursor
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the MouseMove windows message
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HandleMouseMove(ref Message m) {
|
||||
int columnIndex = this.ColumnIndexUnderCursor;
|
||||
|
||||
// If the mouse has moved to a different header, pop the current tip (if any)
|
||||
// For some reason, references this.ToolTip when in design mode, causes the
|
||||
// columns to not be resizable by dragging the divider in the Designer. No idea why.
|
||||
if (columnIndex != this.columnShowingTip && !this.ListView.IsDesignMode) {
|
||||
this.ToolTip.PopToolTip(this);
|
||||
this.columnShowingTip = columnIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private int columnShowingTip = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Handle the Notify windows message
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HandleNotify(ref Message m) {
|
||||
// Can this ever happen? JPP 2009-05-22
|
||||
if (m.LParam == IntPtr.Zero)
|
||||
return false;
|
||||
|
||||
NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR));
|
||||
switch (nmhdr.code) {
|
||||
|
||||
case ToolTipControl.TTN_SHOW:
|
||||
//System.Diagnostics.Debug.WriteLine("hdr TTN_SHOW");
|
||||
//System.Diagnostics.Trace.Assert(this.ToolTip.Handle == nmhdr.hwndFrom);
|
||||
return this.ToolTip.HandleShow(ref m);
|
||||
|
||||
case ToolTipControl.TTN_POP:
|
||||
//System.Diagnostics.Debug.WriteLine("hdr TTN_POP");
|
||||
//System.Diagnostics.Trace.Assert(this.ToolTip.Handle == nmhdr.hwndFrom);
|
||||
return this.ToolTip.HandlePop(ref m);
|
||||
|
||||
case ToolTipControl.TTN_GETDISPINFO:
|
||||
//System.Diagnostics.Debug.WriteLine("hdr TTN_GETDISPINFO");
|
||||
//System.Diagnostics.Trace.Assert(this.ToolTip.Handle == nmhdr.hwndFrom);
|
||||
return this.ToolTip.HandleGetDispInfo(ref m);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the CustomDraw windows message
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
internal virtual bool HandleHeaderCustomDraw(ref Message m) {
|
||||
const int CDRF_NEWFONT = 2;
|
||||
const int CDRF_SKIPDEFAULT = 4;
|
||||
const int CDRF_NOTIFYPOSTPAINT = 0x10;
|
||||
const int CDRF_NOTIFYITEMDRAW = 0x20;
|
||||
|
||||
const int CDDS_PREPAINT = 1;
|
||||
const int CDDS_POSTPAINT = 2;
|
||||
const int CDDS_ITEM = 0x00010000;
|
||||
const int CDDS_ITEMPREPAINT = (CDDS_ITEM | CDDS_PREPAINT);
|
||||
const int CDDS_ITEMPOSTPAINT = (CDDS_ITEM | CDDS_POSTPAINT);
|
||||
|
||||
NativeMethods.NMCUSTOMDRAW nmcustomdraw = (NativeMethods.NMCUSTOMDRAW)m.GetLParam(typeof(NativeMethods.NMCUSTOMDRAW));
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("header cd: {0:x}, {1}, {2:x}", nmcustomdraw.dwDrawStage, nmcustomdraw.dwItemSpec, nmcustomdraw.uItemState));
|
||||
switch (nmcustomdraw.dwDrawStage) {
|
||||
case CDDS_PREPAINT:
|
||||
this.cachedNeedsCustomDraw = this.NeedsCustomDraw();
|
||||
m.Result = (IntPtr)CDRF_NOTIFYITEMDRAW;
|
||||
return true;
|
||||
|
||||
case CDDS_ITEMPREPAINT:
|
||||
int columnIndex = nmcustomdraw.dwItemSpec.ToInt32();
|
||||
OLVColumn column = this.ListView.GetColumn(columnIndex);
|
||||
|
||||
// These don't work when visual styles are enabled
|
||||
//NativeMethods.SetBkColor(nmcustomdraw.hdc, ColorTranslator.ToWin32(Color.Red));
|
||||
//NativeMethods.SetTextColor(nmcustomdraw.hdc, ColorTranslator.ToWin32(Color.Blue));
|
||||
//m.Result = IntPtr.Zero;
|
||||
|
||||
if (this.cachedNeedsCustomDraw) {
|
||||
using (Graphics g = Graphics.FromHdc(nmcustomdraw.hdc)) {
|
||||
g.TextRenderingHint = ObjectListView.TextRenderingHint;
|
||||
this.CustomDrawHeaderCell(g, columnIndex, nmcustomdraw.uItemState);
|
||||
}
|
||||
m.Result = (IntPtr)CDRF_SKIPDEFAULT;
|
||||
} else {
|
||||
const int CDIS_SELECTED = 1;
|
||||
bool isPressed = ((nmcustomdraw.uItemState & CDIS_SELECTED) == CDIS_SELECTED);
|
||||
|
||||
Font f = this.CalculateFont(column, columnIndex == this.ColumnIndexUnderCursor, isPressed);
|
||||
|
||||
this.fontHandle = f.ToHfont();
|
||||
NativeMethods.SelectObject(nmcustomdraw.hdc, this.fontHandle);
|
||||
|
||||
m.Result = (IntPtr)(CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case CDDS_ITEMPOSTPAINT:
|
||||
if (this.fontHandle != IntPtr.Zero) {
|
||||
NativeMethods.DeleteObject(this.fontHandle);
|
||||
this.fontHandle = IntPtr.Zero;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool cachedNeedsCustomDraw;
|
||||
IntPtr fontHandle;
|
||||
|
||||
/// <summary>
|
||||
/// The message divides a ListView's space between the header and the rows of the listview.
|
||||
/// The WINDOWPOS structure controls the headers bounds, the RECT controls the listview bounds.
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HandleLayout(ref Message m) {
|
||||
if (this.ListView.HeaderStyle == ColumnHeaderStyle.None)
|
||||
return true;
|
||||
|
||||
NativeMethods.HDLAYOUT hdlayout = (NativeMethods.HDLAYOUT)m.GetLParam(typeof(NativeMethods.HDLAYOUT));
|
||||
NativeMethods.RECT rect = (NativeMethods.RECT)Marshal.PtrToStructure(hdlayout.prc, typeof(NativeMethods.RECT));
|
||||
NativeMethods.WINDOWPOS wpos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(hdlayout.pwpos, typeof(NativeMethods.WINDOWPOS));
|
||||
|
||||
using (Graphics g = this.ListView.CreateGraphics()) {
|
||||
g.TextRenderingHint = ObjectListView.TextRenderingHint;
|
||||
int height = this.CalculateHeight(g);
|
||||
wpos.hwnd = this.Handle;
|
||||
wpos.hwndInsertAfter = IntPtr.Zero;
|
||||
wpos.flags = NativeMethods.SWP_FRAMECHANGED;
|
||||
wpos.x = rect.left;
|
||||
wpos.y = rect.top;
|
||||
wpos.cx = rect.right - rect.left;
|
||||
wpos.cy = height;
|
||||
|
||||
rect.top = height;
|
||||
|
||||
Marshal.StructureToPtr(rect, hdlayout.prc, false);
|
||||
Marshal.StructureToPtr(wpos, hdlayout.pwpos, false);
|
||||
}
|
||||
|
||||
this.ListView.BeginInvoke((MethodInvoker)delegate {
|
||||
this.Invalidate();
|
||||
this.ListView.Invalidate();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when the underlying header control is destroyed
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
protected bool HandleDestroy(ref Message m) {
|
||||
if (this.ToolTip != null) {
|
||||
this.ToolTip.Showing -= new EventHandler<ToolTipShowingEventArgs>(this.ListView.headerToolTip_Showing);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rendering
|
||||
|
||||
/// <summary>
|
||||
/// Does this header need to be custom drawn?
|
||||
/// </summary>
|
||||
/// <remarks>Word wrapping and colored text require custom drawning. Funnily enough, we
|
||||
/// can change the font natively.</remarks>
|
||||
protected bool NeedsCustomDraw() {
|
||||
if (this.WordWrap)
|
||||
return true;
|
||||
|
||||
if (this.ListView.HeaderUsesThemes)
|
||||
return false;
|
||||
|
||||
if (this.NeedsCustomDraw(this.ListView.HeaderFormatStyle))
|
||||
return true;
|
||||
|
||||
foreach (OLVColumn column in this.ListView.Columns) {
|
||||
if (column.HasHeaderImage ||
|
||||
!column.ShowTextInHeader ||
|
||||
column.IsHeaderVertical ||
|
||||
this.HasFilterIndicator(column) ||
|
||||
column.TextAlign != column.HeaderTextAlign ||
|
||||
this.NeedsCustomDraw(column.HeaderFormatStyle))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool NeedsCustomDraw(HeaderFormatStyle style) {
|
||||
if (style == null)
|
||||
return false;
|
||||
|
||||
return (this.NeedsCustomDraw(style.Normal) ||
|
||||
this.NeedsCustomDraw(style.Hot) ||
|
||||
this.NeedsCustomDraw(style.Pressed));
|
||||
}
|
||||
|
||||
private bool NeedsCustomDraw(HeaderStateStyle style) {
|
||||
if (style == null)
|
||||
return false;
|
||||
|
||||
// If we want fancy colors or frames, we have to custom draw. Oddly enough, we
|
||||
// can handle font changes without custom drawing.
|
||||
if (!style.BackColor.IsEmpty)
|
||||
return true;
|
||||
|
||||
if (style.FrameWidth > 0f && !style.FrameColor.IsEmpty)
|
||||
return true;
|
||||
|
||||
return (!style.ForeColor.IsEmpty && style.ForeColor != Color.Black);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw one cell of the header
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="columnIndex"></param>
|
||||
/// <param name="itemState"></param>
|
||||
protected void CustomDrawHeaderCell(Graphics g, int columnIndex, int itemState) {
|
||||
Rectangle r = this.GetItemRect(columnIndex);
|
||||
OLVColumn column = this.ListView.GetColumn(columnIndex);
|
||||
const int CDIS_SELECTED = 1;
|
||||
bool isPressed = ((itemState & CDIS_SELECTED) == CDIS_SELECTED);
|
||||
|
||||
// Calculate which style should be used for the header
|
||||
HeaderStateStyle stateStyle = this.CalculateStyle(column, columnIndex == this.ColumnIndexUnderCursor, isPressed);
|
||||
|
||||
// If there is an owner drawn delegate installed, give it a chance to draw the header
|
||||
if (column.HeaderDrawing != null) {
|
||||
if (!column.HeaderDrawing(g, r, columnIndex, column, isPressed, stateStyle))
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw the background
|
||||
if (this.ListView.HeaderUsesThemes &&
|
||||
VisualStyleRenderer.IsSupported &&
|
||||
VisualStyleRenderer.IsElementDefined(VisualStyleElement.Header.Item.Normal))
|
||||
this.DrawThemedBackground(g, r, columnIndex, isPressed);
|
||||
else
|
||||
this.DrawUnthemedBackground(g, r, columnIndex, isPressed, stateStyle);
|
||||
|
||||
|
||||
// Draw the sort indicator if this column has one
|
||||
if (this.HasSortIndicator(column)) {
|
||||
if (this.ListView.HeaderUsesThemes &&
|
||||
VisualStyleRenderer.IsSupported &&
|
||||
VisualStyleRenderer.IsElementDefined(VisualStyleElement.Header.SortArrow.SortedUp))
|
||||
this.DrawThemedSortIndicator(g, r);
|
||||
else
|
||||
r = this.DrawUnthemedSortIndicator(g, r);
|
||||
}
|
||||
|
||||
if (this.HasFilterIndicator(column))
|
||||
r = this.DrawFilterIndicator(g, r);
|
||||
|
||||
// Finally draw the text
|
||||
this.DrawHeaderImageAndText(g, r, column, stateStyle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a background for the header, without using Themes.
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="columnIndex"></param>
|
||||
/// <param name="isSelected"></param>
|
||||
/// <param name="stateStyle"></param>
|
||||
protected void DrawUnthemedBackground(Graphics g, Rectangle r, int columnIndex, bool isSelected, HeaderStateStyle stateStyle) {
|
||||
if (stateStyle.BackColor.IsEmpty)
|
||||
// I know we're supposed to be drawing the unthemed background, but let's just see if we
|
||||
// can draw something more interesting than the dull raised block
|
||||
if (VisualStyleRenderer.IsSupported &&
|
||||
VisualStyleRenderer.IsElementDefined(VisualStyleElement.Header.Item.Normal))
|
||||
this.DrawThemedBackground(g, r, columnIndex, isSelected);
|
||||
else
|
||||
ControlPaint.DrawBorder3D(g, r, Border3DStyle.RaisedInner);
|
||||
else {
|
||||
using (Brush b = new SolidBrush(stateStyle.BackColor))
|
||||
g.FillRectangle(b, r);
|
||||
}
|
||||
|
||||
// Draw the frame if the style asks for one
|
||||
if (!stateStyle.FrameColor.IsEmpty && stateStyle.FrameWidth > 0f) {
|
||||
RectangleF r2 = r;
|
||||
r2.Inflate(-stateStyle.FrameWidth, -stateStyle.FrameWidth);
|
||||
g.DrawRectangle(new Pen(stateStyle.FrameColor, stateStyle.FrameWidth),
|
||||
r2.X, r2.Y, r2.Width, r2.Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a more-or-less pure themed header background.
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="columnIndex"></param>
|
||||
/// <param name="isSelected"></param>
|
||||
protected void DrawThemedBackground(Graphics g, Rectangle r, int columnIndex, bool isSelected) {
|
||||
int part = 1; // normal item
|
||||
if (columnIndex == 0 &&
|
||||
VisualStyleRenderer.IsElementDefined(VisualStyleElement.Header.ItemLeft.Normal))
|
||||
part = 2; // left item
|
||||
if (columnIndex == this.ListView.Columns.Count - 1 &&
|
||||
VisualStyleRenderer.IsElementDefined(VisualStyleElement.Header.ItemRight.Normal))
|
||||
part = 3; // right item
|
||||
|
||||
int state = 1; // normal state
|
||||
if (isSelected)
|
||||
state = 3; // pressed
|
||||
else if (columnIndex == this.ColumnIndexUnderCursor)
|
||||
state = 2; // hot
|
||||
|
||||
VisualStyleRenderer renderer = new VisualStyleRenderer("HEADER", part, state);
|
||||
renderer.DrawBackground(g, r);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a sort indicator using themes
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
protected void DrawThemedSortIndicator(Graphics g, Rectangle r) {
|
||||
VisualStyleRenderer renderer2 = null;
|
||||
if (this.ListView.LastSortOrder == SortOrder.Ascending)
|
||||
renderer2 = new VisualStyleRenderer(VisualStyleElement.Header.SortArrow.SortedUp);
|
||||
if (this.ListView.LastSortOrder == SortOrder.Descending)
|
||||
renderer2 = new VisualStyleRenderer(VisualStyleElement.Header.SortArrow.SortedDown);
|
||||
if (renderer2 != null) {
|
||||
Size sz = renderer2.GetPartSize(g, ThemeSizeType.True);
|
||||
Point pt = renderer2.GetPoint(PointProperty.Offset);
|
||||
// GetPoint() should work, but if it doesn't, put the arrow in the top middle
|
||||
if (pt.X == 0 && pt.Y == 0)
|
||||
pt = new Point(r.X + (r.Width / 2) - (sz.Width / 2), r.Y);
|
||||
renderer2.DrawBackground(g, new Rectangle(pt, sz));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a sort indicator without using themes
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
/// <returns></returns>
|
||||
protected Rectangle DrawUnthemedSortIndicator(Graphics g, Rectangle r) {
|
||||
// No theme support for sort indicators. So, we draw a triangle at the right edge
|
||||
// of the column header.
|
||||
const int triangleHeight = 16;
|
||||
const int triangleWidth = 16;
|
||||
const int midX = triangleWidth / 2;
|
||||
const int midY = (triangleHeight / 2) - 1;
|
||||
const int deltaX = midX - 2;
|
||||
const int deltaY = deltaX / 2;
|
||||
|
||||
Point triangleLocation = new Point(r.Right - triangleWidth - 2, r.Top + (r.Height - triangleHeight) / 2);
|
||||
Point[] pts = new Point[] { triangleLocation, triangleLocation, triangleLocation };
|
||||
|
||||
if (this.ListView.LastSortOrder == SortOrder.Ascending) {
|
||||
pts[0].Offset(midX - deltaX, midY + deltaY);
|
||||
pts[1].Offset(midX, midY - deltaY - 1);
|
||||
pts[2].Offset(midX + deltaX, midY + deltaY);
|
||||
} else {
|
||||
pts[0].Offset(midX - deltaX, midY - deltaY);
|
||||
pts[1].Offset(midX, midY + deltaY);
|
||||
pts[2].Offset(midX + deltaX, midY - deltaY);
|
||||
}
|
||||
|
||||
g.FillPolygon(Brushes.SlateGray, pts);
|
||||
r.Width = r.Width - triangleWidth;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw an indication that this column has a filter applied to it
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
/// <returns></returns>
|
||||
protected Rectangle DrawFilterIndicator(Graphics g, Rectangle r) {
|
||||
int width = this.CalculateFilterIndicatorWidth(r);
|
||||
if (width <= 0)
|
||||
return r;
|
||||
|
||||
Image indicator = Resources.ColumnFilterIndicator;
|
||||
int x = r.Right - width;
|
||||
int y = r.Top + (r.Height - indicator.Height) / 2;
|
||||
g.DrawImageUnscaled(indicator, x, y);
|
||||
|
||||
r.Width -= width;
|
||||
return r;
|
||||
}
|
||||
|
||||
private int CalculateFilterIndicatorWidth(Rectangle r) {
|
||||
if (Resources.ColumnFilterIndicator == null || r.Width < 48)
|
||||
return 0;
|
||||
return Resources.ColumnFilterIndicator.Width + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the header's image and text
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="stateStyle"></param>
|
||||
protected void DrawHeaderImageAndText(Graphics g, Rectangle r, OLVColumn column, HeaderStateStyle stateStyle) {
|
||||
|
||||
TextFormatFlags flags = this.TextFormatFlags;
|
||||
flags |= TextFormatFlags.VerticalCenter;
|
||||
if (column.HeaderTextAlign == HorizontalAlignment.Center)
|
||||
flags |= TextFormatFlags.HorizontalCenter;
|
||||
if (column.HeaderTextAlign == HorizontalAlignment.Right)
|
||||
flags |= TextFormatFlags.Right;
|
||||
|
||||
Font f = this.ListView.HeaderUsesThemes ? this.ListView.Font : stateStyle.Font ?? this.ListView.Font;
|
||||
Color color = this.ListView.HeaderUsesThemes ? Color.Black : stateStyle.ForeColor;
|
||||
if (color.IsEmpty)
|
||||
color = Color.Black;
|
||||
|
||||
// Tweak the text rectangle a little to improve aethestics
|
||||
r.Inflate(-3, 0);
|
||||
r.Y -= 2;
|
||||
const int imageTextGap = 3;
|
||||
|
||||
if (column.IsHeaderVertical) {
|
||||
DrawVerticalText(g, r, column, f, color);
|
||||
} else {
|
||||
// Does the column have a header image and is there space for it?
|
||||
if (column.HasHeaderImage && r.Width > column.ImageList.ImageSize.Width * 2)
|
||||
DrawImageAndText(g, r, column, flags, f, color, imageTextGap);
|
||||
else
|
||||
DrawText(g, r, column, flags, f, color);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawText(Graphics g, Rectangle r, OLVColumn column, TextFormatFlags flags, Font f, Color color) {
|
||||
if (column.ShowTextInHeader)
|
||||
TextRenderer.DrawText(g, column.Text, f, r, color, Color.Transparent, flags);
|
||||
}
|
||||
|
||||
private void DrawImageAndText(Graphics g, Rectangle r, OLVColumn column, TextFormatFlags flags, Font f, Color color, int imageTextGap) {
|
||||
Rectangle textRect = r;
|
||||
textRect.X += (column.ImageList.ImageSize.Width + imageTextGap);
|
||||
textRect.Width -= (column.ImageList.ImageSize.Width + imageTextGap);
|
||||
|
||||
Size textSize = Size.Empty;
|
||||
if (column.ShowTextInHeader)
|
||||
textSize = TextRenderer.MeasureText(g, column.Text, f, textRect.Size, flags);
|
||||
|
||||
int imageY = r.Top + ((r.Height - column.ImageList.ImageSize.Height) / 2);
|
||||
int imageX = textRect.Left;
|
||||
if (column.HeaderTextAlign == HorizontalAlignment.Center)
|
||||
imageX = textRect.Left + ((textRect.Width - textSize.Width) / 2);
|
||||
if (column.HeaderTextAlign == HorizontalAlignment.Right)
|
||||
imageX = textRect.Right - textSize.Width;
|
||||
imageX -= (column.ImageList.ImageSize.Width + imageTextGap);
|
||||
|
||||
column.ImageList.Draw(g, imageX, imageY, column.ImageList.Images.IndexOfKey(column.HeaderImageKey));
|
||||
|
||||
this.DrawText(g, textRect, column, flags, f, color);
|
||||
}
|
||||
|
||||
private static void DrawVerticalText(Graphics g, Rectangle r, OLVColumn column, Font f, Color color) {
|
||||
try {
|
||||
// Create a matrix transformation that will rotate the text 90 degrees vertically
|
||||
// AND place the text in the middle of where it was previously. [Think of tipping
|
||||
// a box over by its bottom left edge -- you have to move it back a bit so it's
|
||||
// in the same place as it started]
|
||||
Matrix m = new Matrix();
|
||||
m.RotateAt(-90, new Point(r.X, r.Bottom));
|
||||
m.Translate(0, r.Height);
|
||||
g.Transform = m;
|
||||
StringFormat fmt = new StringFormat(StringFormatFlags.NoWrap);
|
||||
fmt.Alignment = StringAlignment.Near;
|
||||
fmt.LineAlignment = column.HeaderTextAlignAsStringAlignment;
|
||||
//fmt.Trimming = StringTrimming.EllipsisCharacter;
|
||||
|
||||
// The drawing is rotated 90 degrees, so switch our text boundaries
|
||||
Rectangle textRect = r;
|
||||
textRect.Width = r.Height;
|
||||
textRect.Height = r.Width;
|
||||
using (Brush b = new SolidBrush(color))
|
||||
g.DrawString(column.Text, f, b, textRect, fmt);
|
||||
}
|
||||
finally {
|
||||
g.ResetTransform();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What style should be applied to the header?
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="isHot"></param>
|
||||
/// <param name="isPressed"></param>
|
||||
/// <returns></returns>
|
||||
protected HeaderStateStyle CalculateStyle(OLVColumn column, bool isHot, bool isPressed) {
|
||||
HeaderFormatStyle headerStyle =
|
||||
column.HeaderFormatStyle ?? this.ListView.HeaderFormatStyle ?? new HeaderFormatStyle();
|
||||
if (this.ListView.IsDesignMode)
|
||||
return headerStyle.Normal;
|
||||
if (isPressed)
|
||||
return headerStyle.Pressed;
|
||||
if (isHot)
|
||||
return headerStyle.Hot;
|
||||
return headerStyle.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What font should be used to draw the header text?
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="isHot"></param>
|
||||
/// <param name="isPressed"></param>
|
||||
/// <returns></returns>
|
||||
protected Font CalculateFont(OLVColumn column, bool isHot, bool isPressed) {
|
||||
HeaderStateStyle stateStyle = this.CalculateStyle(column, isHot, isPressed);
|
||||
return stateStyle.Font ?? this.ListView.Font;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What flags will be used when drawing text
|
||||
/// </summary>
|
||||
protected TextFormatFlags TextFormatFlags {
|
||||
get {
|
||||
TextFormatFlags flags = TextFormatFlags.EndEllipsis |
|
||||
TextFormatFlags.NoPrefix |
|
||||
TextFormatFlags.WordEllipsis |
|
||||
TextFormatFlags.PreserveGraphicsTranslateTransform;
|
||||
if (this.WordWrap)
|
||||
flags |= TextFormatFlags.WordBreak;
|
||||
else
|
||||
flags |= TextFormatFlags.SingleLine;
|
||||
if (this.ListView.RightToLeft == RightToLeft.Yes)
|
||||
flags |= TextFormatFlags.RightToLeft;
|
||||
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* ToolStripCheckedListBox - Puts a CheckedListBox into a tool strip menu item
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 4-March-2011 11:59 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-04 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011 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@bigfoot.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class put a CheckedListBox into a tool strip menu item.
|
||||
/// </summary>
|
||||
public class ToolStripCheckedListBox : ToolStripControlHost {
|
||||
|
||||
/// <summary>
|
||||
/// Create a ToolStripCheckedListBox
|
||||
/// </summary>
|
||||
public ToolStripCheckedListBox()
|
||||
: base(new CheckedListBox()) {
|
||||
this.CheckedListBoxControl.MaximumSize = new Size(400, 700);
|
||||
this.CheckedListBoxControl.ThreeDCheckBoxes = true;
|
||||
this.CheckedListBoxControl.CheckOnClick = true;
|
||||
this.CheckedListBoxControl.SelectionMode = SelectionMode.One;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control embedded in the menu
|
||||
/// </summary>
|
||||
public CheckedListBox CheckedListBoxControl {
|
||||
get {
|
||||
return Control as CheckedListBox;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items shown in the checkedlistbox
|
||||
/// </summary>
|
||||
public CheckedListBox.ObjectCollection Items {
|
||||
get {
|
||||
return this.CheckedListBoxControl.Items;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether an item should be checked when it is clicked
|
||||
/// </summary>
|
||||
public bool CheckedOnClick {
|
||||
get {
|
||||
return this.CheckedListBoxControl.CheckOnClick;
|
||||
}
|
||||
set {
|
||||
this.CheckedListBoxControl.CheckOnClick = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of the checked items
|
||||
/// </summary>
|
||||
public CheckedListBox.CheckedItemCollection CheckedItems {
|
||||
get {
|
||||
return this.CheckedListBoxControl.CheckedItems;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a possibly checked item to the control
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="isChecked"></param>
|
||||
public void AddItem(object item, bool isChecked) {
|
||||
this.Items.Add(item);
|
||||
if (isChecked)
|
||||
this.CheckedListBoxControl.SetItemChecked(this.Items.Count - 1, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an item with the given state to the control
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="state"></param>
|
||||
public void AddItem(object item, CheckState state) {
|
||||
this.Items.Add(item);
|
||||
this.CheckedListBoxControl.SetItemCheckState(this.Items.Count - 1, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the checkedness of the i'th item
|
||||
/// </summary>
|
||||
/// <param name="i"></param>
|
||||
/// <returns></returns>
|
||||
public CheckState GetItemCheckState(int i) {
|
||||
return this.CheckedListBoxControl.GetItemCheckState(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the checkedness of the i'th item
|
||||
/// </summary>
|
||||
/// <param name="i"></param>
|
||||
/// <param name="checkState"></param>
|
||||
public void SetItemState(int i, CheckState checkState) {
|
||||
if (i >= 0 && i < this.Items.Count)
|
||||
this.CheckedListBoxControl.SetItemCheckState(i, checkState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check all the items in the control
|
||||
/// </summary>
|
||||
public void CheckAll() {
|
||||
for (int i = 0; i < this.Items.Count; i++)
|
||||
this.CheckedListBoxControl.SetItemChecked(i, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unchecked all the items in the control
|
||||
/// </summary>
|
||||
public void UncheckAll() {
|
||||
for (int i = 0; i < this.Items.Count; i++)
|
||||
this.CheckedListBoxControl.SetItemChecked(i, false);
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Listen for events on the underlying control
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
protected override void OnSubscribeControlEvents(Control c) {
|
||||
base.OnSubscribeControlEvents(c);
|
||||
|
||||
CheckedListBox control = (CheckedListBox)c;
|
||||
control.ItemCheck += new ItemCheckEventHandler(OnItemCheck);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop listening for events on the underlying control
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
protected override void OnUnsubscribeControlEvents(Control c) {
|
||||
base.OnUnsubscribeControlEvents(c);
|
||||
|
||||
CheckedListBox control = (CheckedListBox)c;
|
||||
control.ItemCheck -= new ItemCheckEventHandler(OnItemCheck);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell the world that an item was checked
|
||||
/// </summary>
|
||||
public event ItemCheckEventHandler ItemCheck;
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the ItemCheck event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnItemCheck(object sender, ItemCheckEventArgs e) {
|
||||
if (ItemCheck != null) {
|
||||
ItemCheck(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
* ToolTipControl - A limited wrapper around a Windows tooltip control
|
||||
*
|
||||
* For some reason, the ToolTip class in the .NET framework is implemented in a significantly
|
||||
* different manner to other controls. For our purposes, the worst of these problems
|
||||
* is that we cannot get the Handle, so we cannot send Windows level messages to the control.
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 2009-05-17 7:22PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.3
|
||||
* 2009-06-13 JPP - Moved ToolTipShowingEventArgs to Events.cs
|
||||
* v2.2
|
||||
* 2009-06-06 JPP - Fixed some Vista specific problems
|
||||
* 2009-05-17 JPP - Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2006-2009 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@bigfoot.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A limited wrapper around a Windows tooltip window.
|
||||
/// </summary>
|
||||
public class ToolTipControl : NativeWindow
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// These are the standard icons that a tooltip can display.
|
||||
/// </summary>
|
||||
public enum StandardIcons
|
||||
{
|
||||
/// <summary>
|
||||
/// No icon
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Info
|
||||
/// </summary>
|
||||
Info = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Warning
|
||||
/// </summary>
|
||||
Warning = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Error
|
||||
/// </summary>
|
||||
Error = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Large info (Vista and later only)
|
||||
/// </summary>
|
||||
InfoLarge = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Large warning (Vista and later only)
|
||||
/// </summary>
|
||||
WarningLarge = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Large error (Vista and later only)
|
||||
/// </summary>
|
||||
ErrorLarge = 6
|
||||
}
|
||||
|
||||
const int GWL_STYLE = -16;
|
||||
const int WM_GETFONT = 0x31;
|
||||
const int WM_SETFONT = 0x30;
|
||||
const int WS_BORDER = 0x800000;
|
||||
const int WS_EX_TOPMOST = 8;
|
||||
|
||||
const int TTM_ADDTOOL = 0x432;
|
||||
const int TTM_ADJUSTRECT = 0x400 + 31;
|
||||
const int TTM_DELTOOL = 0x433;
|
||||
const int TTM_GETBUBBLESIZE = 0x400 + 30;
|
||||
const int TTM_GETCURRENTTOOL = 0x400 + 59;
|
||||
const int TTM_GETTIPBKCOLOR = 0x400 + 22;
|
||||
const int TTM_GETTIPTEXTCOLOR = 0x400 + 23;
|
||||
const int TTM_GETDELAYTIME = 0x400 + 21;
|
||||
const int TTM_NEWTOOLRECT = 0x400 + 52;
|
||||
const int TTM_POP = 0x41c;
|
||||
const int TTM_SETDELAYTIME = 0x400 + 3;
|
||||
const int TTM_SETMAXTIPWIDTH = 0x400 + 24;
|
||||
const int TTM_SETTIPBKCOLOR = 0x400 + 19;
|
||||
const int TTM_SETTIPTEXTCOLOR = 0x400 + 20;
|
||||
const int TTM_SETTITLE = 0x400 + 33;
|
||||
const int TTM_SETTOOLINFO = 0x400 + 54;
|
||||
|
||||
const int TTF_IDISHWND = 1;
|
||||
//const int TTF_ABSOLUTE = 0x80;
|
||||
const int TTF_CENTERTIP = 2;
|
||||
const int TTF_RTLREADING = 4;
|
||||
const int TTF_SUBCLASS = 0x10;
|
||||
//const int TTF_TRACK = 0x20;
|
||||
//const int TTF_TRANSPARENT = 0x100;
|
||||
const int TTF_PARSELINKS = 0x1000;
|
||||
|
||||
const int TTS_NOPREFIX = 2;
|
||||
const int TTS_BALLOON = 0x40;
|
||||
const int TTS_USEVISUALSTYLE = 0x100;
|
||||
|
||||
const int TTN_FIRST = -520;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public const int TTN_SHOW = (TTN_FIRST - 1);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public const int TTN_POP = (TTN_FIRST - 2);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public const int TTN_LINKCLICK = (TTN_FIRST - 3);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public const int TTN_GETDISPINFO = (TTN_FIRST - 10);
|
||||
|
||||
const int TTDT_AUTOMATIC = 0;
|
||||
const int TTDT_RESHOW = 1;
|
||||
const int TTDT_AUTOPOP = 2;
|
||||
const int TTDT_INITIAL = 3;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Get or set if the style of the tooltip control
|
||||
/// </summary>
|
||||
internal int WindowStyle {
|
||||
get {
|
||||
return (int)NativeMethods.GetWindowLong(this.Handle, GWL_STYLE);
|
||||
}
|
||||
set {
|
||||
NativeMethods.SetWindowLong(this.Handle, GWL_STYLE, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set if the tooltip should be shown as a ballon
|
||||
/// </summary>
|
||||
public bool IsBalloon {
|
||||
get {
|
||||
return (this.WindowStyle & TTS_BALLOON) == TTS_BALLOON;
|
||||
}
|
||||
set {
|
||||
if (this.IsBalloon == value)
|
||||
return;
|
||||
|
||||
int windowStyle = this.WindowStyle;
|
||||
if (value) {
|
||||
windowStyle |= (TTS_BALLOON | TTS_USEVISUALSTYLE);
|
||||
// On XP, a border makes the ballon look wrong
|
||||
if (!ObjectListView.IsVistaOrLater)
|
||||
windowStyle &= ~WS_BORDER;
|
||||
} else {
|
||||
windowStyle &= ~(TTS_BALLOON | TTS_USEVISUALSTYLE);
|
||||
if (!ObjectListView.IsVistaOrLater) {
|
||||
if (this.hasBorder)
|
||||
windowStyle |= WS_BORDER;
|
||||
else
|
||||
windowStyle &= ~WS_BORDER;
|
||||
}
|
||||
}
|
||||
this.WindowStyle = windowStyle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set if the tooltip should be shown as a ballon
|
||||
/// </summary>
|
||||
public bool HasBorder {
|
||||
get {
|
||||
return this.hasBorder;
|
||||
}
|
||||
set {
|
||||
if (this.hasBorder == value)
|
||||
return;
|
||||
|
||||
if (value) {
|
||||
this.WindowStyle |= WS_BORDER;
|
||||
} else {
|
||||
this.WindowStyle &= ~WS_BORDER;
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool hasBorder = true;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the background color of the tooltip
|
||||
/// </summary>
|
||||
public Color BackColor {
|
||||
get {
|
||||
int color = (int)NativeMethods.SendMessage(this.Handle, TTM_GETTIPBKCOLOR, 0, 0);
|
||||
return ColorTranslator.FromWin32(color);
|
||||
}
|
||||
set {
|
||||
// For some reason, setting the color fails on Vista and messes up later ops.
|
||||
// So we don't even try to set it.
|
||||
if (!ObjectListView.IsVistaOrLater) {
|
||||
int color = ColorTranslator.ToWin32(value);
|
||||
NativeMethods.SendMessage(this.Handle, TTM_SETTIPBKCOLOR, color, 0);
|
||||
//int x2 = Marshal.GetLastWin32Error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the color of the text and border on the tooltip.
|
||||
/// </summary>
|
||||
public Color ForeColor {
|
||||
get {
|
||||
int color = (int)NativeMethods.SendMessage(this.Handle, TTM_GETTIPTEXTCOLOR, 0, 0);
|
||||
return ColorTranslator.FromWin32(color);
|
||||
}
|
||||
set {
|
||||
// For some reason, setting the color fails on Vista and messes up later ops.
|
||||
// So we don't even try to set it.
|
||||
if (!ObjectListView.IsVistaOrLater) {
|
||||
int color = ColorTranslator.ToWin32(value);
|
||||
NativeMethods.SendMessage(this.Handle, TTM_SETTIPTEXTCOLOR, color, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the title that will be shown on the tooltip.
|
||||
/// </summary>
|
||||
public string Title {
|
||||
get {
|
||||
return this.title;
|
||||
}
|
||||
set {
|
||||
if (String.IsNullOrEmpty(value))
|
||||
this.title = String.Empty;
|
||||
else
|
||||
if (value.Length >= 100)
|
||||
this.title = value.Substring(0, 99);
|
||||
else
|
||||
this.title = value;
|
||||
NativeMethods.SendMessageString(this.Handle, TTM_SETTITLE, (int)this.standardIcon, this.title);
|
||||
}
|
||||
}
|
||||
private string title;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the icon that will be shown on the tooltip.
|
||||
/// </summary>
|
||||
public StandardIcons StandardIcon {
|
||||
get {
|
||||
return this.standardIcon;
|
||||
}
|
||||
set {
|
||||
this.standardIcon = value;
|
||||
NativeMethods.SendMessageString(this.Handle, TTM_SETTITLE, (int)this.standardIcon, this.title);
|
||||
}
|
||||
}
|
||||
private StandardIcons standardIcon;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font that will be used to draw this control.
|
||||
/// is still.
|
||||
/// </summary>
|
||||
/// <remarks>Setting this to null reverts to the default font.</remarks>
|
||||
public Font Font {
|
||||
get {
|
||||
IntPtr hfont = NativeMethods.SendMessage(this.Handle, WM_GETFONT, 0, 0);
|
||||
if (hfont == IntPtr.Zero)
|
||||
return Control.DefaultFont;
|
||||
else
|
||||
return Font.FromHfont(hfont);
|
||||
}
|
||||
set {
|
||||
Font newFont = value ?? Control.DefaultFont;
|
||||
if (newFont == this.font)
|
||||
return;
|
||||
|
||||
this.font = newFont;
|
||||
IntPtr hfont = this.font.ToHfont(); // THINK: When should we delete this hfont?
|
||||
NativeMethods.SendMessage(this.Handle, WM_SETFONT, hfont, 0);
|
||||
}
|
||||
}
|
||||
private Font font;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many milliseconds the tooltip will remain visible while the mouse
|
||||
/// is still.
|
||||
/// </summary>
|
||||
public int AutoPopDelay {
|
||||
get { return this.GetDelayTime(TTDT_AUTOPOP); }
|
||||
set { this.SetDelayTime(TTDT_AUTOPOP, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many milliseconds the mouse must be still before the tooltip is shown.
|
||||
/// </summary>
|
||||
public int InitialDelay {
|
||||
get { return this.GetDelayTime(TTDT_INITIAL); }
|
||||
set { this.SetDelayTime(TTDT_INITIAL, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many milliseconds the mouse must be still before the tooltip is shown again.
|
||||
/// </summary>
|
||||
public int ReshowDelay {
|
||||
get { return this.GetDelayTime(TTDT_RESHOW); }
|
||||
set { this.SetDelayTime(TTDT_RESHOW, value); }
|
||||
}
|
||||
|
||||
private int GetDelayTime(int which) {
|
||||
return (int)NativeMethods.SendMessage(this.Handle, TTM_GETDELAYTIME, which, 0);
|
||||
}
|
||||
|
||||
private void SetDelayTime(int which, int value) {
|
||||
NativeMethods.SendMessage(this.Handle, TTM_SETDELAYTIME, which, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Create the underlying control.
|
||||
/// </summary>
|
||||
/// <param name="parentHandle">The parent of the tooltip</param>
|
||||
/// <remarks>This does nothing if the control has already been created</remarks>
|
||||
public void Create(IntPtr parentHandle) {
|
||||
if (this.Handle != IntPtr.Zero)
|
||||
return;
|
||||
|
||||
CreateParams cp = new CreateParams();
|
||||
cp.ClassName = "tooltips_class32";
|
||||
cp.Style = TTS_NOPREFIX;
|
||||
cp.ExStyle = WS_EX_TOPMOST;
|
||||
cp.Parent = parentHandle;
|
||||
this.CreateHandle(cp);
|
||||
|
||||
// Ensure that multiline tooltips work correctly
|
||||
this.SetMaxWidth();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take a copy of the current settings and restore them when the
|
||||
/// tooltip is poppped.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This call cannot be nested. Subsequent calls to this method will be ignored
|
||||
/// until PopSettings() is called.
|
||||
/// </remarks>
|
||||
public void PushSettings() {
|
||||
// Ignore any nested calls
|
||||
if (this.settings != null)
|
||||
return;
|
||||
this.settings = new Hashtable();
|
||||
this.settings["IsBalloon"] = this.IsBalloon;
|
||||
this.settings["HasBorder"] = this.HasBorder;
|
||||
this.settings["BackColor"] = this.BackColor;
|
||||
this.settings["ForeColor"] = this.ForeColor;
|
||||
this.settings["Title"] = this.Title;
|
||||
this.settings["StandardIcon"] = this.StandardIcon;
|
||||
this.settings["AutoPopDelay"] = this.AutoPopDelay;
|
||||
this.settings["InitialDelay"] = this.InitialDelay;
|
||||
this.settings["ReshowDelay"] = this.ReshowDelay;
|
||||
this.settings["Font"] = this.Font;
|
||||
}
|
||||
private Hashtable settings;
|
||||
|
||||
/// <summary>
|
||||
/// Restore the settings of the tooltip as they were when PushSettings()
|
||||
/// was last called.
|
||||
/// </summary>
|
||||
public void PopSettings() {
|
||||
if (this.settings == null)
|
||||
return;
|
||||
|
||||
this.IsBalloon = (bool)this.settings["IsBalloon"];
|
||||
this.HasBorder = (bool)this.settings["HasBorder"];
|
||||
this.BackColor = (Color)this.settings["BackColor"];
|
||||
this.ForeColor = (Color)this.settings["ForeColor"];
|
||||
this.Title = (string)this.settings["Title"];
|
||||
this.StandardIcon = (StandardIcons)this.settings["StandardIcon"];
|
||||
this.AutoPopDelay = (int)this.settings["AutoPopDelay"];
|
||||
this.InitialDelay = (int)this.settings["InitialDelay"];
|
||||
this.ReshowDelay = (int)this.settings["ReshowDelay"];
|
||||
this.Font = (Font)this.settings["Font"];
|
||||
|
||||
this.settings = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the given window to those for whom this tooltip will show tips
|
||||
/// </summary>
|
||||
/// <param name="window">The window</param>
|
||||
public void AddTool(IWin32Window window) {
|
||||
NativeMethods.TOOLINFO lParam = this.MakeToolInfoStruct(window);
|
||||
NativeMethods.SendMessageTOOLINFO(this.Handle, TTM_ADDTOOL, 0, lParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide any currently visible tooltip
|
||||
/// </summary>
|
||||
/// <param name="window"></param>
|
||||
public void PopToolTip(IWin32Window window) {
|
||||
NativeMethods.SendMessage(this.Handle, TTM_POP, 0, 0);
|
||||
}
|
||||
|
||||
//public void Munge() {
|
||||
// NativeMethods.TOOLINFO tool = new NativeMethods.TOOLINFO();
|
||||
// IntPtr result = NativeMethods.SendMessageTOOLINFO(this.Handle, TTM_GETCURRENTTOOL, 0, tool);
|
||||
// System.Diagnostics.Trace.WriteLine("-");
|
||||
// System.Diagnostics.Trace.WriteLine(result);
|
||||
// result = NativeMethods.SendMessageTOOLINFO(this.Handle, TTM_GETBUBBLESIZE, 0, tool);
|
||||
// System.Diagnostics.Trace.WriteLine(String.Format("{0} {1}", result.ToInt32() >> 16, result.ToInt32() & 0xFFFF));
|
||||
// NativeMethods.ChangeSize(this, result.ToInt32() & 0xFFFF, result.ToInt32() >> 16);
|
||||
// //NativeMethods.RECT r = new NativeMethods.RECT();
|
||||
// //r.right
|
||||
// //IntPtr x = NativeMethods.SendMessageRECT(this.Handle, TTM_ADJUSTRECT, true, ref r);
|
||||
|
||||
// //System.Diagnostics.Trace.WriteLine(String.Format("{0} {1} {2} {3}", r.left, r.top, r.right, r.bottom));
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the given window from those managed by this tooltip
|
||||
/// </summary>
|
||||
/// <param name="window"></param>
|
||||
public void RemoveToolTip(IWin32Window window) {
|
||||
NativeMethods.TOOLINFO lParam = this.MakeToolInfoStruct(window);
|
||||
NativeMethods.SendMessageTOOLINFO(this.Handle, TTM_DELTOOL, 0, lParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the maximum width of a tooltip string.
|
||||
/// </summary>
|
||||
public void SetMaxWidth() {
|
||||
this.SetMaxWidth(SystemInformation.MaxWindowTrackSize.Width);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the maximum width of a tooltip string.
|
||||
/// </summary>
|
||||
/// <remarks>Setting this ensures that line breaks in the tooltip are honoured.</remarks>
|
||||
public void SetMaxWidth(int maxWidth) {
|
||||
NativeMethods.SendMessage(this.Handle, TTM_SETMAXTIPWIDTH, 0, maxWidth);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Make a TOOLINFO structure for the given window
|
||||
/// </summary>
|
||||
/// <param name="window"></param>
|
||||
/// <returns>A filled in TOOLINFO</returns>
|
||||
private NativeMethods.TOOLINFO MakeToolInfoStruct(IWin32Window window) {
|
||||
|
||||
NativeMethods.TOOLINFO toolinfo_tooltip = new NativeMethods.TOOLINFO();
|
||||
toolinfo_tooltip.hwnd = window.Handle;
|
||||
toolinfo_tooltip.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
|
||||
toolinfo_tooltip.uId = window.Handle;
|
||||
toolinfo_tooltip.lpszText = (IntPtr)(-1); // LPSTR_TEXTCALLBACK
|
||||
|
||||
return toolinfo_tooltip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a WmNotify message
|
||||
/// </summary>
|
||||
/// <param name="msg">The msg</param>
|
||||
/// <returns>True if the message has been handled</returns>
|
||||
protected virtual bool HandleNotify(ref Message msg) {
|
||||
|
||||
//THINK: What do we have to do here? Nothing it seems :)
|
||||
|
||||
//NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER)msg.GetLParam(typeof(NativeMethods.NMHEADER));
|
||||
//System.Diagnostics.Trace.WriteLine("HandleNotify");
|
||||
//System.Diagnostics.Trace.WriteLine(nmheader.nhdr.code);
|
||||
|
||||
//switch (nmheader.nhdr.code) {
|
||||
//}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a get display info message
|
||||
/// </summary>
|
||||
/// <param name="msg">The msg</param>
|
||||
/// <returns>True if the message has been handled</returns>
|
||||
public virtual bool HandleGetDispInfo(ref Message msg) {
|
||||
//System.Diagnostics.Trace.WriteLine("HandleGetDispInfo");
|
||||
this.SetMaxWidth();
|
||||
ToolTipShowingEventArgs args = new ToolTipShowingEventArgs();
|
||||
args.ToolTipControl = this;
|
||||
this.OnShowing(args);
|
||||
if (String.IsNullOrEmpty(args.Text))
|
||||
return false;
|
||||
|
||||
this.ApplyEventFormatting(args);
|
||||
|
||||
NativeMethods.NMTTDISPINFO dispInfo = (NativeMethods.NMTTDISPINFO)msg.GetLParam(typeof(NativeMethods.NMTTDISPINFO));
|
||||
dispInfo.lpszText = args.Text;
|
||||
dispInfo.hinst = IntPtr.Zero;
|
||||
if (args.RightToLeft == RightToLeft.Yes)
|
||||
dispInfo.uFlags |= TTF_RTLREADING;
|
||||
Marshal.StructureToPtr(dispInfo, msg.LParam, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ApplyEventFormatting(ToolTipShowingEventArgs args) {
|
||||
if (!args.IsBalloon.HasValue &&
|
||||
!args.BackColor.HasValue &&
|
||||
!args.ForeColor.HasValue &&
|
||||
args.Title == null &&
|
||||
!args.StandardIcon.HasValue &&
|
||||
!args.AutoPopDelay.HasValue &&
|
||||
args.Font == null)
|
||||
return;
|
||||
|
||||
this.PushSettings();
|
||||
if (args.IsBalloon.HasValue)
|
||||
this.IsBalloon = args.IsBalloon.Value;
|
||||
if (args.BackColor.HasValue)
|
||||
this.BackColor = args.BackColor.Value;
|
||||
if (args.ForeColor.HasValue)
|
||||
this.ForeColor = args.ForeColor.Value;
|
||||
if (args.StandardIcon.HasValue)
|
||||
this.StandardIcon = args.StandardIcon.Value;
|
||||
if (args.AutoPopDelay.HasValue)
|
||||
this.AutoPopDelay = args.AutoPopDelay.Value;
|
||||
if (args.Font != null)
|
||||
this.Font = args.Font;
|
||||
if (args.Title != null)
|
||||
this.Title = args.Title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a TTN_LINKCLICK message
|
||||
/// </summary>
|
||||
/// <param name="msg">The msg</param>
|
||||
/// <returns>True if the message has been handled</returns>
|
||||
/// <remarks>This cannot call base.WndProc() since the msg may have come from another control.</remarks>
|
||||
public virtual bool HandleLinkClick(ref Message msg) {
|
||||
//System.Diagnostics.Trace.WriteLine("HandleLinkClick");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a TTN_POP message
|
||||
/// </summary>
|
||||
/// <param name="msg">The msg</param>
|
||||
/// <returns>True if the message has been handled</returns>
|
||||
/// <remarks>This cannot call base.WndProc() since the msg may have come from another control.</remarks>
|
||||
public virtual bool HandlePop(ref Message msg) {
|
||||
//System.Diagnostics.Trace.WriteLine("HandlePop");
|
||||
this.PopSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a TTN_SHOW message
|
||||
/// </summary>
|
||||
/// <param name="msg">The msg</param>
|
||||
/// <returns>True if the message has been handled</returns>
|
||||
/// <remarks>This cannot call base.WndProc() since the msg may have come from another control.</remarks>
|
||||
public virtual bool HandleShow(ref Message msg) {
|
||||
//System.Diagnostics.Trace.WriteLine("HandleShow");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a reflected notify message
|
||||
/// </summary>
|
||||
/// <param name="msg">The msg</param>
|
||||
/// <returns>True if the message has been handled</returns>
|
||||
protected virtual bool HandleReflectNotify(ref Message msg) {
|
||||
|
||||
NativeMethods.NMHEADER nmheader = (NativeMethods.NMHEADER)msg.GetLParam(typeof(NativeMethods.NMHEADER));
|
||||
switch (nmheader.nhdr.code) {
|
||||
case TTN_SHOW:
|
||||
//System.Diagnostics.Trace.WriteLine("reflect TTN_SHOW");
|
||||
if (this.HandleShow(ref msg))
|
||||
return true;
|
||||
break;
|
||||
case TTN_POP:
|
||||
//System.Diagnostics.Trace.WriteLine("reflect TTN_POP");
|
||||
if (this.HandlePop(ref msg))
|
||||
return true;
|
||||
break;
|
||||
case TTN_LINKCLICK:
|
||||
//System.Diagnostics.Trace.WriteLine("reflect TTN_LINKCLICK");
|
||||
if (this.HandleLinkClick(ref msg))
|
||||
return true;
|
||||
break;
|
||||
case TTN_GETDISPINFO:
|
||||
//System.Diagnostics.Trace.WriteLine("reflect TTN_GETDISPINFO");
|
||||
if (this.HandleGetDispInfo(ref msg))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mess with the basic message pump of the tooltip
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
|
||||
override protected void WndProc(ref Message msg) {
|
||||
//System.Diagnostics.Trace.WriteLine(String.Format("xx {0:x}", msg.Msg));
|
||||
switch (msg.Msg) {
|
||||
case 0x4E: // WM_NOTIFY
|
||||
if (!this.HandleNotify(ref msg))
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x204E: // WM_REFLECT_NOTIFY
|
||||
if (!this.HandleReflectNotify(ref msg))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
base.WndProc(ref msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Tell the world that a tooltip is about to show
|
||||
/// </summary>
|
||||
public event EventHandler<ToolTipShowingEventArgs> Showing;
|
||||
|
||||
/// <summary>
|
||||
/// Tell the world that a tooltip is about to disappear
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs> Pop;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnShowing(ToolTipShowingEventArgs e) {
|
||||
if (this.Showing != null)
|
||||
this.Showing(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void OnPop(EventArgs e) {
|
||||
if (this.Pop != null)
|
||||
this.Pop(this, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user