mirror of https://github.com/dahall/Vanara.git
490 lines
20 KiB
C#
490 lines
20 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Drawing;
|
|||
|
using System.Drawing.Design;
|
|||
|
using System.Windows.Forms;
|
|||
|
using Vanara.Extensions;
|
|||
|
using static Vanara.PInvoke.User32;
|
|||
|
using ContentAlignment = System.Drawing.ContentAlignment;
|
|||
|
|
|||
|
namespace Vanara.Windows.Forms
|
|||
|
{
|
|||
|
/// <summary>State flags for controls derived from <see cref="CustomDrawBase"/>.</summary>
|
|||
|
[Flags]
|
|||
|
public enum ControlState
|
|||
|
{
|
|||
|
/// <summary>A mouse is hovering over the control.</summary>
|
|||
|
Hot = 1 << 0,
|
|||
|
|
|||
|
/// <summary>The control has been pressed or clicked.</summary>
|
|||
|
Pressed = 1 << 1,
|
|||
|
|
|||
|
/// <summary>The control is disabled.</summary>
|
|||
|
Disabled = 1 << 2,
|
|||
|
|
|||
|
/// <summary>The control is in the process of animating.</summary>
|
|||
|
Animating = 1 << 3,
|
|||
|
|
|||
|
/// <summary>The mouse button is down.</summary>
|
|||
|
MouseDown = 1 << 4,
|
|||
|
|
|||
|
/// <summary>The mouse button is up.</summary>
|
|||
|
InButtonUp = 1 << 5,
|
|||
|
|
|||
|
/// <summary>The control is defaulted (used primarily by buttons).</summary>
|
|||
|
Defaulted = 1 << 6,
|
|||
|
|
|||
|
/// <summary>The control has the focus.</summary>
|
|||
|
Focused = 1 << 7,
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Abstract class for implementing a custom-drawn control that tracks mouse movement and has text and/or an image. It exposes all
|
|||
|
/// property changes.
|
|||
|
/// </summary>
|
|||
|
/// <seealso cref="System.Windows.Forms.Control"/>
|
|||
|
/// <seealso cref="System.Windows.Forms.IButtonControl"/>
|
|||
|
/// <seealso cref="System.ComponentModel.INotifyPropertyChanged"/>
|
|||
|
public abstract class CustomDrawBase : Control, IButtonControl, INotifyPropertyChanged
|
|||
|
{
|
|||
|
private readonly ControlImage image;
|
|||
|
private bool autoEllipsis;
|
|||
|
private ContentAlignment imageAlign = ContentAlignment.MiddleCenter;
|
|||
|
|
|||
|
//private bool keyPressed;
|
|||
|
private ControlState lastState;
|
|||
|
|
|||
|
private EnumFlagIndexer<ControlState> state;
|
|||
|
private ContentAlignment textAlign = ContentAlignment.MiddleCenter;
|
|||
|
private TextImageRelation textImageRelation = TextImageRelation.Overlay;
|
|||
|
private ToolTip textToolTip;
|
|||
|
private bool useMnemonic = true;
|
|||
|
|
|||
|
/// <summary>Initializes a new instance of the <see cref="CustomDrawBase"/> class.</summary>
|
|||
|
protected CustomDrawBase()
|
|||
|
{
|
|||
|
image = new ControlImage(this);
|
|||
|
SetStyle(ControlStyles.Selectable | ControlStyles.StandardClick | ControlStyles.ResizeRedraw, true);
|
|||
|
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Occurs when the control is double-clicked.</summary>
|
|||
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
|
|||
|
public new event EventHandler DoubleClick
|
|||
|
{
|
|||
|
add { base.DoubleClick += value; }
|
|||
|
remove { base.DoubleClick -= value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Occurs when the control is double clicked by the mouse.</summary>
|
|||
|
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
|
|||
|
public new event MouseEventHandler MouseDoubleClick
|
|||
|
{
|
|||
|
add { base.MouseDoubleClick += value; }
|
|||
|
remove { base.MouseDoubleClick -= value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Occurs when a property value changes.</summary>
|
|||
|
public event PropertyChangedEventHandler PropertyChanged;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets a value indicating whether the ellipsis character (...) appears at the right edge of the control, denoting that the
|
|||
|
/// control text extends beyond the specified length of the control.
|
|||
|
/// </summary>
|
|||
|
/// <value>
|
|||
|
/// <c>true</c> if the additional label text is to be indicated by an ellipsis; otherwise, <c>false</c>. The default is <c>true</c>.
|
|||
|
/// </value>
|
|||
|
[Category("Behavior"), DefaultValue(true), Browsable(true), EditorBrowsable(EditorBrowsableState.Always), Description("")]
|
|||
|
public bool AutoEllipsis
|
|||
|
{
|
|||
|
get => autoEllipsis; set => SetField(ref autoEllipsis, value, nameof(AutoEllipsis), true, b => { if (b && textToolTip == null) textToolTip = new ToolTip(); });
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets the value returned to the parent form when the button is clicked.</summary>
|
|||
|
[Category("Behavior"), DefaultValue(typeof(DialogResult), "None")]
|
|||
|
[Description("The dialog result produced in a modal form by clicking the button.")]
|
|||
|
public virtual DialogResult DialogResult { get; set; }
|
|||
|
|
|||
|
/// <summary>Gets or sets the image that is displayed on a button control.</summary>
|
|||
|
/// <value>The Image displayed on the button control. The default value is <c>null</c>.</value>
|
|||
|
[Description(""), Localizable(true), Category("Appearance"), DefaultValue(null)]
|
|||
|
public Image Image
|
|||
|
{
|
|||
|
get => image.Image; set { if (image.Image != value) image.Image = value; else OnPropertyChanged(nameof(Image)); }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets the alignment of the image on the button control.</summary>
|
|||
|
/// <value>One of the <see cref="ContentAlignment"/> values. The default value is <c>MiddleCenter</c>.</value>
|
|||
|
[Category("Appearance"), DefaultValue((int)ContentAlignment.MiddleCenter)]
|
|||
|
[Description("The alignment of the image that will be displayed in the face of the control.")]
|
|||
|
public virtual ContentAlignment ImageAlign
|
|||
|
{
|
|||
|
get => imageAlign; set => SetField(ref imageAlign, value, nameof(imageAlign));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets the image list index value of the image displayed on the button control.</summary>
|
|||
|
/// <value>A zero-based index, which represents the image position in an <see cref="ImageList"/>. The default is -1.</value>
|
|||
|
[Description(""), Localizable(true), Category("Appearance"), DefaultValue(-1), RefreshProperties(RefreshProperties.Repaint)]
|
|||
|
[TypeConverter(typeof(ImageIndexConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
|
|||
|
public int ImageIndex
|
|||
|
{
|
|||
|
get => image.ImageIndex; set { if (image.ImageIndex != value) image.ImageIndex = value; else OnPropertyChanged(nameof(ImageIndex)); }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets the key accessor for the image in the <see cref="ImageList"/>.</summary>
|
|||
|
/// <value>A string representing the key of the image.</value>
|
|||
|
[Description(""), Localizable(true), Category("Appearance"), DefaultValue(""), RefreshProperties(RefreshProperties.Repaint)]
|
|||
|
[TypeConverter(typeof(ImageKeyConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
|
|||
|
public string ImageKey
|
|||
|
{
|
|||
|
get => image.ImageKey; set { if (image.ImageKey != value) image.ImageKey = value; else OnPropertyChanged(nameof(ImageKey)); }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets the <see cref="ImageList"/> that contains the <see cref="Image"/> displayed on a button control.</summary>
|
|||
|
/// <value>An <see cref="ImageList"/>. The default value is <c>null</c>.</value>
|
|||
|
[Description(""), Category("Appearance"), DefaultValue(null), RefreshProperties(RefreshProperties.Repaint)]
|
|||
|
public ImageList ImageList
|
|||
|
{
|
|||
|
get => image.ImageList; set { if (image.ImageList != value) image.ImageList = value; else OnPropertyChanged(nameof(ImageList)); }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets the alignment of the text on the button control.</summary>
|
|||
|
/// <value>One of the <see cref="ContentAlignment"/> values. The default value is <c>MiddleCenter</c>.</value>
|
|||
|
[Category("Appearance"), DefaultValue((int)ContentAlignment.MiddleCenter)]
|
|||
|
[Description("The alignment of the text that will be displayed in the face of the control.")]
|
|||
|
public virtual ContentAlignment TextAlign
|
|||
|
{
|
|||
|
get => textAlign; set => SetField(ref textAlign, value, nameof(TextAlign));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets the position of text and image relative to each other.</summary>
|
|||
|
/// <value>One of the values of <see cref="TextImageRelation"/>. The default is <c>Overlay</c>.</value>
|
|||
|
[DefaultValue(0), Localizable(true), Description(""), Category("Appearance")]
|
|||
|
public TextImageRelation TextImageRelation
|
|||
|
{
|
|||
|
get => textImageRelation; set => SetField(ref textImageRelation, value, nameof(TextImageRelation));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets or sets a value indicating whether the first character that is preceded by an ampersand (&) is used as the mnemonic key
|
|||
|
/// of the control.
|
|||
|
/// </summary>
|
|||
|
/// <value>
|
|||
|
/// <c>true</c> if the first character that is preceded by an ampersand (&) is used as the mnemonic key of the control;
|
|||
|
/// otherwise, <c>false</c>. The default is <c>true</c>.
|
|||
|
/// </value>
|
|||
|
[DefaultValue(true), Description(""), Category("Appearance")]
|
|||
|
public bool UseMnemonic
|
|||
|
{
|
|||
|
get => useMnemonic; set => SetField(ref useMnemonic, value, nameof(UseMnemonic));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets or sets a value indicating whether this <see cref="CustomDrawBase"/> is animating.</summary>
|
|||
|
/// <value><see langword="true"/> if animating; otherwise, <see langword="false"/>.</value>
|
|||
|
[Browsable(false)]
|
|||
|
protected virtual bool Animating
|
|||
|
{
|
|||
|
get => state[ControlState.Animating]; set => SetState(ControlState.Animating, value, false);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets the default size of the control.</summary>
|
|||
|
protected override Size DefaultSize => new Size(75, 23);
|
|||
|
|
|||
|
/// <summary>Gets or sets a value indicating whether the button control is the default button.</summary>
|
|||
|
/// <value><c>true</c> if the button control is the default button; otherwise, <c>false</c>.</value>
|
|||
|
[Browsable(false)]
|
|||
|
protected virtual bool IsDefault
|
|||
|
{
|
|||
|
get => state[ControlState.Defaulted]; set => SetState(ControlState.Defaulted, value);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Gets the last state of the control.</summary>
|
|||
|
/// <value>The last state.</value>
|
|||
|
protected virtual ControlState LastState => lastState;
|
|||
|
|
|||
|
/// <summary>Gets the current state of the control.</summary>
|
|||
|
/// <value>The state.</value>
|
|||
|
protected virtual ControlState State => state;
|
|||
|
|
|||
|
private bool ShowToolTip => !DesignMode && AutoEllipsis && textToolTip != null;
|
|||
|
|
|||
|
/// <summary>Notifies a control that it is the default button so that its appearance and behavior is adjusted accordingly.</summary>
|
|||
|
/// <param name="value">true if the control should behave as a default button; otherwise false.</param>
|
|||
|
public virtual void NotifyDefault(bool value)
|
|||
|
{
|
|||
|
if (IsDefault == value) return;
|
|||
|
IsDefault = value;
|
|||
|
Invalidate();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Generates a <see cref="E:System.Windows.Forms.Control.Click"/> event for the control.</summary>
|
|||
|
public void PerformClick()
|
|||
|
{
|
|||
|
if (CanSelect)
|
|||
|
OnClick(EventArgs.Empty);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.EnabledChanged"/> event.</summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnEnabledChanged(EventArgs e)
|
|||
|
{
|
|||
|
base.OnEnabledChanged(e);
|
|||
|
state[ControlState.Disabled] = !Enabled;
|
|||
|
if (Enabled) return;
|
|||
|
SetState(ControlState.MouseDown | ControlState.Pressed | ControlState.Hot, false);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.GotFocus"/> event.</summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnGotFocus(EventArgs e)
|
|||
|
{
|
|||
|
System.Diagnostics.Debug.WriteLine($"GotFocus[{Name}]");
|
|||
|
base.OnGotFocus(e);
|
|||
|
SetState(ControlState.Focused, true);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.KeyDown"/> event.</summary>
|
|||
|
/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnKeyDown(KeyEventArgs e)
|
|||
|
{
|
|||
|
if (e.KeyData == Keys.Space)
|
|||
|
{
|
|||
|
SetState(ControlState.MouseDown, true);
|
|||
|
e.Handled = true;
|
|||
|
}
|
|||
|
base.OnKeyDown(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.KeyUp"/> event.</summary>
|
|||
|
/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnKeyUp(KeyEventArgs e)
|
|||
|
{
|
|||
|
if (state[ControlState.MouseDown])
|
|||
|
{
|
|||
|
if (SetState(ControlState.MouseDown | ControlState.Pressed, false, false))
|
|||
|
Refresh();
|
|||
|
if (e.KeyCode == Keys.Space || e.KeyCode == Keys.Enter)
|
|||
|
OnClick(EventArgs.Empty);
|
|||
|
e.Handled = true;
|
|||
|
}
|
|||
|
base.OnKeyUp(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.LostFocus"/> event.</summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnLostFocus(EventArgs e)
|
|||
|
{
|
|||
|
System.Diagnostics.Debug.WriteLine($"LostFocus[{Name}]");
|
|||
|
base.OnLostFocus(e);
|
|||
|
Capture = false;
|
|||
|
SetState(ControlState.MouseDown | ControlState.Pressed | ControlState.Focused, false);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"/> event.</summary>
|
|||
|
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnMouseDown(MouseEventArgs e)
|
|||
|
{
|
|||
|
if (e.Button == MouseButtons.Left)
|
|||
|
SetState(ControlState.MouseDown | ControlState.Pressed, true);
|
|||
|
base.OnMouseDown(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.MouseEnter"/> event.</summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnMouseEnter(EventArgs e)
|
|||
|
{
|
|||
|
SetState(ControlState.Hot, true);
|
|||
|
if (ShowToolTip)
|
|||
|
try { textToolTip.Show(Text.RemoveMnemonic(), this); } catch { }
|
|||
|
base.OnMouseEnter(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.MouseLeave"/> event.</summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnMouseLeave(EventArgs e)
|
|||
|
{
|
|||
|
System.Diagnostics.Debug.WriteLine($"OnMouseLeave[{Name}]");
|
|||
|
SetState(ControlState.Hot, false);
|
|||
|
textToolTip?.Hide(this);
|
|||
|
base.OnMouseLeave(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"/> event.</summary>
|
|||
|
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnMouseMove(MouseEventArgs e)
|
|||
|
{
|
|||
|
if (e.Button != MouseButtons.None && state[ControlState.Pressed])
|
|||
|
{
|
|||
|
if (!ClientRectangle.Contains(e.X, e.Y))
|
|||
|
{
|
|||
|
if (state[ControlState.MouseDown])
|
|||
|
SetState(ControlState.MouseDown | ControlState.Pressed, false);
|
|||
|
}
|
|||
|
else if (!state[ControlState.MouseDown])
|
|||
|
{
|
|||
|
SetState(ControlState.MouseDown, true);
|
|||
|
}
|
|||
|
}
|
|||
|
base.OnMouseMove(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"/> event.</summary>
|
|||
|
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnMouseUp(MouseEventArgs e)
|
|||
|
{
|
|||
|
if (e.Button == MouseButtons.Left && state[ControlState.Pressed])
|
|||
|
{
|
|||
|
var mouseWasDown = state[ControlState.MouseDown];
|
|||
|
if (SetState(ControlState.MouseDown | ControlState.Pressed, false, false))
|
|||
|
Refresh();
|
|||
|
if (mouseWasDown && WindowFromPoint(PointToScreen(e.Location)) == Handle)
|
|||
|
{
|
|||
|
OnClick(e);
|
|||
|
OnMouseClick(e);
|
|||
|
}
|
|||
|
}
|
|||
|
base.OnMouseUp(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Raises the <see cref="E:System.Windows.Forms.Control.BackColorChanged"/> event when the <see
|
|||
|
/// cref="P:System.Windows.Forms.Control.BackColor"/> property value of the control's container changes.
|
|||
|
/// </summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnParentBackColorChanged(EventArgs e)
|
|||
|
{
|
|||
|
base.OnParentBackColorChanged(e);
|
|||
|
Invalidate();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Raises the <see cref="E:System.Windows.Forms.Control.BackgroundImageChanged"/> event when the <see
|
|||
|
/// cref="P:System.Windows.Forms.Control.BackgroundImage"/> property value of the control's container changes.
|
|||
|
/// </summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnParentBackgroundImageChanged(EventArgs e)
|
|||
|
{
|
|||
|
base.OnParentBackgroundImageChanged(e);
|
|||
|
Invalidate();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="PropertyChanged"/> event.</summary>
|
|||
|
/// <param name="propertyName">Name of the property that has changed.</param>
|
|||
|
protected virtual void OnPropertyChanged(string propertyName)
|
|||
|
{
|
|||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.TextChanged"/> event.</summary>
|
|||
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|||
|
protected override void OnTextChanged(EventArgs e)
|
|||
|
{
|
|||
|
base.OnTextChanged(e);
|
|||
|
Invalidate();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Processes a mnemonic character.</summary>
|
|||
|
/// <param name="charCode">The character to process.</param>
|
|||
|
/// <returns>true if the character was processed as a mnemonic by the control; otherwise, false.</returns>
|
|||
|
protected override bool ProcessMnemonic(char charCode)
|
|||
|
{
|
|||
|
if (!IsMnemonic(charCode, Text))
|
|||
|
return base.ProcessMnemonic(charCode);
|
|||
|
OnClick(EventArgs.Empty);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sets a field value to the new value. If the value has changed, the <see cref="PropertyChanged"/> event is raised and the control
|
|||
|
/// will optionally be invalidated.
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">The type of the field.</typeparam>
|
|||
|
/// <param name="field">A reference to the field.</param>
|
|||
|
/// <param name="value">The new value.</param>
|
|||
|
/// <param name="propertyName">The name of the property.</param>
|
|||
|
/// <param name="invalidateOnSet">if set to <c>true</c> the control is invalidated if this is a changed value.</param>
|
|||
|
/// <param name="validate">An optional action function that is called when it is determined that this is a changed value.</param>
|
|||
|
/// <returns><c>true</c> if the value has been changed; otherwise <c>false</c>.</returns>
|
|||
|
protected virtual bool SetField<T>(ref T field, T value, string propertyName, bool invalidateOnSet = true, Action<T> validate = null) where T : struct
|
|||
|
{
|
|||
|
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
|||
|
validate?.Invoke(value);
|
|||
|
if (typeof(T).IsEnum && !Enum.IsDefined(typeof(T), value))
|
|||
|
throw new InvalidEnumArgumentException(propertyName, Convert.ToInt32(value), typeof(T));
|
|||
|
field = value;
|
|||
|
OnPropertyChanged(propertyName);
|
|||
|
if (invalidateOnSet && IsHandleCreated)
|
|||
|
Invalidate();
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Sets the state of the control.</summary>
|
|||
|
/// <param name="stateVal">The state value.</param>
|
|||
|
/// <param name="value">
|
|||
|
/// if set to <see langword="true"/> sets the flag in <paramref name="stateVal"/> on; otherwise it removes the state.
|
|||
|
/// </param>
|
|||
|
/// <param name="invalidateOnSet">if set to <see langword="true"/>, invalidate the control once set.</param>
|
|||
|
/// <returns></returns>
|
|||
|
protected virtual bool SetState(ControlState stateVal, bool value, bool invalidateOnSet = true)
|
|||
|
{
|
|||
|
if (state[stateVal] == value) return false;
|
|||
|
lastState = state;
|
|||
|
state[stateVal] = value;
|
|||
|
OnPropertyChanged(nameof(State));
|
|||
|
if (invalidateOnSet && IsHandleCreated)
|
|||
|
Invalidate();
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Processes Windows messages.</summary>
|
|||
|
/// <param name="m">The Windows <see cref="T:System.Windows.Forms.Message"/> to process.</param>
|
|||
|
protected override void WndProc(ref Message m)
|
|||
|
{
|
|||
|
switch (m.Msg)
|
|||
|
{
|
|||
|
case (int)WindowMessage.WM_ERASEBKGND:
|
|||
|
DefWndProc(ref m);
|
|||
|
return;
|
|||
|
|
|||
|
/*case 0x2111:
|
|||
|
if (HIWORD(m.WParam) == 0)
|
|||
|
{
|
|||
|
OnClick(EventArgs.Empty);
|
|||
|
return;
|
|||
|
}
|
|||
|
break;*/
|
|||
|
|
|||
|
case (int)ButtonMessage.BM_CLICK:
|
|||
|
PerformClick();
|
|||
|
return;
|
|||
|
|
|||
|
case (int)ButtonMessage.BM_SETSTATE:
|
|||
|
// Ignore BM_SETSTATE -- Windows gets confused and paints things, even though we are ownerdraw.
|
|||
|
return;
|
|||
|
|
|||
|
case (int)WindowMessage.WM_KILLFOCUS:
|
|||
|
case (int)WindowMessage.WM_CANCELMODE:
|
|||
|
case (int)WindowMessage.WM_CAPTURECHANGED:
|
|||
|
if (!state[ControlState.InButtonUp] && state[ControlState.Pressed])
|
|||
|
SetState(ControlState.MouseDown | ControlState.Pressed, false);
|
|||
|
break;
|
|||
|
|
|||
|
case (int)WindowMessage.WM_LBUTTONUP:
|
|||
|
case (int)WindowMessage.WM_MBUTTONUP:
|
|||
|
case (int)WindowMessage.WM_RBUTTONUP:
|
|||
|
try
|
|||
|
{
|
|||
|
state[ControlState.InButtonUp] = true;
|
|||
|
base.WndProc(ref m);
|
|||
|
return;
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
state[ControlState.InButtonUp] = false;
|
|||
|
}
|
|||
|
}
|
|||
|
base.WndProc(ref m);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|