Question

Having a little trouble making a Winforms ImageButton class (C#). My ImageButton class implements the IButtonControl interface but when I add it to a form, and set the button's DialogResult to 'OK' , and then call ShowDialog() on the form, pressing the button does not close the form and return the DialogResult, as the normal Winforms Button control does.

Here's my implementation of ImageButton, feel free to do whatever you like with it.

/// <summary>
/// A control that displays an image and responds to mouse clicks on the image.
/// </summary>
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[Designer(typeof(ImageButtonDesigner))]
[ToolboxItem(typeof(ImageButtonToolboxItem))]
public class ImageButton : PictureBox, IButtonControl, INotifyPropertyChanged
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the ImageButton class using the default initial values.
    /// </summary>
    public ImageButton()
    {
        DoubleBuffered = true;
        BackColor = Color.Transparent;
        SizeMode = PictureBoxSizeMode.AutoSize;
    }

    #endregion

    #region Properties

    /// <summary>
    /// Backing field for the DialogResult property.
    /// </summary>
    private DialogResult dialogResult;

    /// <summary>
    /// Gets or sets a value that is returned to the parent form when the button is clicked.
    /// </summary>
    [Category("Behavior")]
    [Description("The dialog-box result produced in a modal form by clicking the button")]
    public DialogResult DialogResult
    {
        get
        {
            return dialogResult;
        }

        set
        {
            if (Enum.IsDefined(typeof(DialogResult), value))
                dialogResult = value;
            else
                throw new InvalidEnumArgumentException("value", (int)value, typeof(DialogResult));
        }
    }

    /// <summary>
    /// Backing field for the Idle property.
    /// </summary>
    private Image idle;

    /// <summary>
    /// The image that will be displayed on the control when the mouse is not over a visible part of it.
    /// </summary>
    [Category("Appearance")]
    [Description("The image that will be displayed on the Control when the mouse is not over a visible part of it.")]
    public Image Idle
    {
        get
        {
            return idle;
        }

        set
        {
            idle = value;

            NotifyPropertyChanged();
        }
    }

    /// <summary>
    /// Backing field for the Mouseover property
    /// </summary>
    private Image mouseover;

    /// <summary>
    /// The image that will be displayed on the control when the mouse is over a visible part of it.
    /// </summary>
    [Category("Appearance")]
    [Description("The image that will be displayed on the control when the mouse is over a visible part of it.")]
    public Image Mouseover
    {
        get { return mouseover; }

        set
        {
            mouseover = value;

            NotifyPropertyChanged();
        }
    }

    /// <summary>
    /// Backing field for the Mousedown property
    /// </summary>
    private Image mousedown;

    /// <summary>
    /// The image that will be displayed on the control when the left mouse button is held down and the mouse is over a visible part of it.
    /// </summary>
    [Category("Appearance")]
    [Description("The image that will be displayed on the control when the left mouse button is held down and the mouse is over a visible part of it.")]
    public Image Mousedown
    {
        get { return mousedown; }

        set
        {
            mousedown = value;

            NotifyPropertyChanged();
        }
    }

    /// <summary>
    /// Gets or sets the text associated with the control.
    /// </summary>
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [Category("Appearance")]
    [Description("The text associated with the control.")]
    public override string Text
    {
        get
        {
            return base.Text;
        }
        set
        {
            base.Text = value;
        }
    }

    /// <summary>
    /// Gets or sets the font of the text displayed by the control.
    /// </summary>
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [Category("Appearance")]
    [Description("The font used to display text in the control.")]
    public override Font Font
    {
        get
        {
            return base.Font;
        }
        set
        {
            base.Font = value;
        }
    }

    /// <summary>
    /// Gets or sets the image that is displayed by the PictureBox.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Browsable(false)]
    [Category("Appearance")]
    [Description("The image displayed in the PictureBox.")]
    new public Image Image
    {
        get
        {
            return base.Image;
        }

        set
        {
            base.Image = value;
        }
    }

    /// <summary>
    /// Backing field for the ButtonState property.
    /// </summary>
    private ButtonStates buttonState = ButtonStates.None;

    /// <summary>
    /// The current state of the button.
    /// </summary>
    private ButtonStates ButtonState
    {
        get { return buttonState; }

        set
        {
            buttonState = value;

            NotifyPropertyChanged();
        }
    }

    /// <summary>
    /// Gets the default size of the control.
    /// </summary>
    protected override Size DefaultSize
    {
        get
        {
            return new Size(75, 23);
        }
    }

    #endregion

    #region Enums

    /// <summary>
    /// Specifies the current state of a button.
    /// </summary>
    [Flags]
    private enum ButtonStates : byte
    {
        /// <summary>
        /// 
        /// </summary>
        [Description("")]
        None = 0,

        /// <summary>
        /// 
        /// </summary>
        [Description("")]
        Default = 1 << 0,

        /// <summary>
        /// 
        /// </summary>
        [Description("")]
        Mouseover = 1 << 1,

        /// <summary>
        /// 
        /// </summary>
        [Description("")]
        Mousedown = 1 << 2
    }

    #endregion

    #region Events

    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    [Category("Property Changed")]
    [Description("Occurs when a property value changes.")]
    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    #region Methods

    /// <summary>
    /// Raises the System.ComponentModel.PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">the name of the property that changed.</param>
    protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        if (propertyName == "Idle")
            Image = Idle;
    }

    /// <summary>
    /// Notifies the button whether it is the default button so that it can adjust its appearance accordingly.
    /// </summary>
    /// <param name="value">true if the button is to have the appearance of the default button; otherwise, false.</param>
    public void NotifyDefault(bool value)
    {
        ButtonState = value ? ButtonState | ButtonStates.Default : ButtonState & ~ButtonStates.Default;
    }

    /// <summary>
    /// Generates a Click event for a button.
    /// </summary>
    public void PerformClick()
    {
        if (CanSelect)
        {
            OnClick(EventArgs.Empty);
        }
    }

    /// <summary>
    /// Raises the System.Windows.Control.TextChanged event.
    /// </summary>
    /// <param name="e">A System.EventArgs that contains the event data.</param>
    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);

        Refresh();
    }

    /// <summary>
    /// Raises the System.Windows.Forms.Paint event.
    /// </summary>
    /// <param name="pe">A PaintEventArgs that contains the event data.</param>
    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);

        if ((!string.IsNullOrEmpty(Text)) && (pe != null) && (base.Font != null))
        {
            SolidBrush drawBrush = new SolidBrush(base.ForeColor);

            // Calculate the size of the text that will be drawn onto the control.
            SizeF drawStringSize = pe.Graphics.MeasureString(base.Text, base.Font);

            // The registration point used to draw the text.
            PointF drawPoint;

            if (base.Image != null)
                drawPoint = new PointF(base.Image.Width / 2 - drawStringSize.Width / 2, base.Image.Height / 2 - drawStringSize.Height / 2);
            else
                drawPoint = new PointF(base.Width / 2 - drawStringSize.Width / 2, base.Height / 2 - drawStringSize.Height / 2);

            pe.Graphics.DrawString(base.Text, base.Font, drawBrush, drawPoint);
        }
    }

    /// <summary>
    /// Raises the System.Windows.Forms.MouseEnter event.
    /// </summary>
    /// <param name="e">A System.EventArgs that contains the event data.</param>
    protected override void OnMouseEnter(EventArgs e)
    {
        base.OnMouseEnter(e);

        ButtonState |= ButtonStates.Mouseover;
        Image = Mouseover;
    }

    /// <summary>
    /// Raises the System.Windows.Forms.MouseLeave event.
    /// </summary>
    /// <param name="e">A System.EventArgs that contains the event data.</param>
    protected override void OnMouseLeave(EventArgs e)
    {
        base.OnMouseLeave(e);

        ButtonState &= ~ButtonStates.Mouseover;
        Image = Idle;
    }

    /// <summary>
    /// Raises the System.Windows.Forms.MouseDown event.
    /// </summary>
    /// <param name="e">A System.Windows.Forms.MouseEventArgs that contains the event data.</param>
    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);

        ButtonState |= ButtonStates.Mousedown;
        Image = Mousedown;
    }

    /// <summary>
    /// Raises the System.Windows.Forms.MouseUp event.
    /// </summary>
    /// <param name="e">A System.Windows.Forms.MouseEventArgs that contains the event data.</param>
    protected override void OnMouseUp(MouseEventArgs e)
    {
        base.OnMouseUp(e);

        ButtonState &= ~ButtonStates.Mousedown;
        Image = ((ButtonState & ButtonStates.Mouseover) != 0) ? Mouseover : idle;
    }

    #endregion
}

[Serializable]
internal class ImageButtonToolboxItem : ToolboxItem
{
    public ImageButtonToolboxItem() : base(typeof(ImageButton)) { }

    protected ImageButtonToolboxItem(SerializationInfo info, StreamingContext context)
    {
        Deserialize(info, context);
    }

    protected override IComponent[] CreateComponentsCore(IDesignerHost host)
    {
        ImageButton imageButton = (ImageButton)host.CreateComponent(typeof(ImageButton));

        Assembly assembly = Assembly.GetAssembly(typeof(ImageButton));

        using (Stream streamMouseover = assembly.GetManifestResourceStream("Mvc.Mouseover.png"))
        using (Stream streamMousedown = assembly.GetManifestResourceStream("Mvc.Mousedown.png"))
        using (Stream streamIdle = assembly.GetManifestResourceStream("Mvc.Idle.png"))
        {
            imageButton.Idle = Image.FromStream(streamIdle);
            imageButton.Mouseover = Image.FromStream(streamMouseover);
            imageButton.Mousedown = Image.FromStream(streamMousedown);
        }

        return new IComponent[] { imageButton };
    }
}

internal class ImageButtonDesigner : ControlDesigner
{
    protected override void PostFilterAttributes(System.Collections.IDictionary attributes)
    {
        base.PostFilterAttributes(attributes);

        Attribute dockingBehaviour = new DockingAttribute(DockingBehavior.Never);

        attributes[typeof(DockingAttribute)] = dockingBehaviour;
    }

    public override SelectionRules SelectionRules
    {
        get
        {
            return SelectionRules.Moveable;
        }
    }
}

Sorry about the messy toolbox item and designer code..

My Question is, Does there need to be anything special implementation wise to get a button control to work on a modal form (in the same way the normal button does) (NB. The Form's BorderStyle property is set to None, don't know if thats important)

Thanks, in advance!

Était-ce utile?

La solution

You need to actually apply the assigned DialogResult property, it is not automatic. Do so by overriding the OnClick() method:

protected override void OnClick(EventArgs e) {
    var form = this.FindForm();
    if (form != null) form.DialogResult = dialogResult;
    base.OnClick(e);
}

Technically you should also notify accessibility clients of the state change, it isn't clear at all whether you care about this. It tends to be skipped for custom controls but you generally ought to. Insert these statements before the base.OnClick() call:

    base.AccessibilityNotifyClients(AccessibleEvents.StateChange, -1);
    base.AccessibilityNotifyClients(AccessibleEvents.NameChange, -1);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top