Laden...

Pseudo Button für GUIs mit großen Hintergrundbildern

Erstellt von jaensen vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.733 Views
jaensen Themenstarter:in
2.760 Beiträge seit 2006
vor 14 Jahren
Pseudo Button für GUIs mit großen Hintergrundbildern

Pseudo Button für GUIs mit großen Hintergrundbildern

In einigen Threads im Forum ging es um Controls (in erster Linie Buttons) die sich langsam aufbauen wenn auf der Form ein großes Hintergrundbild gesetzt ist. Dieses Problem ließ sich nach einigen Versuchen meinerseits nicht anders Umgehen als die Buttons durch selber gezeichnete zu ersetzten die dann allerdings nicht mehr im Designer bearbeitet werden konnten.

Dieses Snippet versucht das Problem zu umgehen in dem es während der Designzeit einen normalen Button benutzt der zur Laufzeit durch einen PseudoButton ausgetauscht wird.

Dazu benötigt es ein abgeleitetes Panel welches u.a. die Mouse-events an die PseudoButtons durchreicht sowie diese zeichnet, einen abgeleiteten Button der im Designer konfiguriert werden kann und gleichzeitig den PseudoButton konfiguriert sowie den PseudoButton an sich.

Verwendung:
Einfach das PseudoButtonContainer-Control der Form hinzufügen und dann wie gewohnt im Designer mit DesignerPseudoButtons bestücken und konfigurieren.

Anmerkung:
Die PseudoButtons kennen drei States: Normal, Hot und Pressed. Für jeden dieser States muss ein Bild verwendet werden welches auch im Designer aus den Resourcen konfiguriert werden kann sonst sieht man nachher nichts.

Außerdem sind bei weitem nicht alle Properties und Events auch mit dem PseudoButton nutzbar. Momentan beschränken sich die Events auf "Click" und die Properties auf "Bounds", "Name", "NormalImage", "HotImage" und PressedImage

Es gibt auf jeden Fall noch großes Verbesserungspotenzial aber da dieser Code für mich selbst nur von geringem Nutzen ist bleibt er (zumindest vorerst) so wie er ist.


   /// <summary>
   /// A special button which internally holds a PseudoButton.
   /// </summary>
   /// <remarks>
   /// This button is nessecary to support the PseudoButton in DesignMode.
   /// If a button of this type is placed on a PseudoButtonContainer it will behaive
   /// like a normal button during design time which allows that the internal
   /// PseudoButton is configured with the same values.
   /// 
   /// Currently mapped events are:
   ///  - Click
   /// 
   /// Currently mapped properties are:
   ///  - NormalImage
   ///  - HotImage
   ///  - PressedImage
   ///  - Name
   ///  - Bounds (via SetBoundsCore override)
   /// </remarks>
   public class DesignerPseudoButton : Button
   {
      #region Private variables / Public properties


      /// <summary>
      /// The pseudo button which will be displayed at runtime.
      /// </summary>
      public PseudoButton PseudoButton
      {
         get { return _pseudoButton; }
      }
      private PseudoButton _pseudoButton;

      /// <summary>
      /// Gets/sets the image for the normal state of the control.
      /// </summary>
      public Image NormalImage
      {
         get { return _pseudoButton.Normal; }
         set 
         {
            _pseudoButton.Normal = value;
            BackgroundImage = value;
         }
      }

      /// <summary>
      /// Gets/sets the image for the control in hot-state.
      /// </summary>
      public Image HotImage
      {
         get { return _pseudoButton.Hot; }
         set { _pseudoButton.Hot = value; }
      }

      /// <summary>
      /// Gets/sets the image for the control in pressed-state.
      /// </summary>
      public Image PressedImage
      {
         get { return _pseudoButton.Pressed; }
         set { _pseudoButton.Pressed = value; }
      }

      #endregion

      #region Constructor

      /// <summary>
      /// ctor.
      /// </summary>
      public DesignerPseudoButton ()
         : base()
      {
         _pseudoButton = new PseudoButton();
      }

      #endregion

      #region Public overridden/hidden methods/events
      
      protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
      {
         base.SetBoundsCore(x, y, width, height, specified);

         _pseudoButton.X = x;
         _pseudoButton.Y = y;
         _pseudoButton.W = width;
         _pseudoButton.H = height;
      }

      public new string Name
      {
         get
         {
            return base.Name;
         }
         set
         {
            base.Name = value;
            _pseudoButton.Name = value;
         }
      }

      public new event EventHandler Click
      {
         add
         {
            base.Click += value;
            _pseudoButton.Click += value;
         }
         remove
         {
            base.Click -= value;
            _pseudoButton.Click -= value;
         }
      }

      #endregion
   }

   /// <summary>
   /// A special Panel which automatically replaces DesignerPseudoButtons with PseudoButtons at runtime.
   /// </summary>
   public partial class PseudoButtonContainer : Panel
   {
      #region Private variables / Public properties

      /// <summary>
      /// Flag which indicates if the real buttons have already been hidden and were replaced with
      /// the PseudoButtons.
      /// </summary>
      private bool _replaced = false;

      /// <summary>
      /// Gets the list of PseudoButtons on the control.
      /// </summary>
      public List<PseudoButton> ButtonList
      {
         get { return _buttonList; }
      }
      private List<PseudoButton> _buttonList;

      #endregion

      #region Constructor

      /// <summary>
      /// ctor
      /// </summary>
      public PseudoButtonContainer ()
      {
         InitializeComponent();
         
         _buttonList = new List<PseudoButton>();
         DoubleBuffered = true;
      }

      #endregion

      #region Overridden OnXXX

      /// <summary>
      /// Replaces the real buttons with the pseudo buttons and then paints them.
      /// </summary>
      protected override void OnPaint (PaintEventArgs e)
      {
         if (!_replaced && !DesignMode)
         {
            // If the control is not in design mode and the real buttons have not yet been replaced by the pseudo buttons
            // we loop over the controls and look for DesignerPseudoButtons which we replace with the PseudoButtons they contain.
            foreach (Control ctrl in Controls)
            {
               DesignerPseudoButton btn = ctrl as DesignerPseudoButton;
               if (btn == null)
                  continue;

               btn.Visible = false;
               PseudoButton pButton = btn.PseudoButton;
               _buttonList.Add(pButton);
            }
            _replaced = true;
         }

         base.OnPaint(e);

         foreach (PseudoButton pseudoButton in _buttonList)
         {
            pseudoButton.Paint(e.Graphics);
         }
      }

      protected override void OnMouseMove (MouseEventArgs e)
      {
         base.OnMouseMove(e);

         foreach (PseudoButton pseudoButton in _buttonList)
         {
            if (pseudoButton.OuterBounds.Contains(e.Location))
            {
               pseudoButton.InvokeMouseMove(e);
               Invalidate(pseudoButton.OuterBounds);
            }
            else if (pseudoButton.State == PseudoButtonState.Hot || 
               pseudoButton.State == PseudoButtonState.Pressed)
            {
               pseudoButton.InvokeMouseLeave(EventArgs.Empty);
            }
         }
      }

      protected override void OnMouseDown (MouseEventArgs e)
      {
         base.OnMouseDown(e);

         foreach (PseudoButton pseudoButton in _buttonList)
         {
            if (pseudoButton.OuterBounds.Contains(e.Location))
            {
               pseudoButton.InvokeMouseDown(e);
               Invalidate(pseudoButton.OuterBounds);
            }
         }
      }

      protected override void OnMouseUp (MouseEventArgs e)
      {
         base.OnMouseUp(e);

         foreach (PseudoButton pseudoButton in _buttonList)
         {
            if (pseudoButton.OuterBounds.Contains(e.Location))
            {
               pseudoButton.InvokeMouseUp(e);
               Invalidate(pseudoButton.OuterBounds);
            }
         }
      }

      #endregion
   }

   /// <summary>
   /// Used by the DesignerPseudoButton.
   /// </summary>
   public class PseudoButton
   {
      #region Private variables / Public properties
      
      /// <summary>
      /// The horizontal position of the pseudo button.
      /// </summary>
      public int X
      {
         get { return _x; }
         set { _x = value; }
      }
      private int _x;
      
      /// <summary>
      /// The vertical position of the pseudo button.
      /// </summary>
      public int Y
      {
         get { return _y; }
         set { _y = value; }
      }
      private int _y;

      /// <summary>
      /// The width of the pseudo button.
      /// </summary>
      public int W
      {
         get { return _w; }
         set { _w = value; }
      }
      private int _w;

      /// <summary>
      /// The height of the pseudo button.
      /// </summary>
      public int H
      {
         get { return _h; }
         set { _h = value; }
      }
      private int _h;

      /// <summary>
      /// The name of the pseudo button.
      /// </summary>
      public string Name
      {
         get { return _name; }
         set { _name = value; }
      }
      private string _name;

      /// <summary>
      /// The bounds of the pseudo button.
      /// </summary>
      public Rectangle Bounds
      {
         get
         {
            return new Rectangle(_x, _y, _w, _h);
         }
         set
         {
            _x = value.X;
            _y = value.Y;
            _w = value.Width;
            _h = value.Height;
         }
      }

      /// <summary>
      /// The outer bounds of the pseudo button which is always 5 pixel bigger than its bounds.
      /// </summary>
      public Rectangle OuterBounds
      {
         get
         {
            return Rectangle.Inflate(Bounds, 5, 5);
         }
      }

      /// <summary>
      /// The location of the pseudo button.
      /// </summary>
      public Point Location
      {
         get
         {
            return new Point(_x, _y);
         }
         set
         {
            _x = value.X;
            _y = value.Y;
         }
      }


      public Image Normal
      {
        get { return _normal; }
        set { _normal = value; }
      }
      private Image _normal;

      public Image Hot
      {
        get { return _hot; }
        set { _hot = value; }
      }
      private Image _hot;

      public Image Pressed
      {
        get { return _pressed; }
        set { _pressed = value; }
      }
      private Image _pressed;

      public PseudoButtonState State
      {
         get { return _state; }
         set { _state = value; }
      }
      private PseudoButtonState _state;

      private PseudoButtonState _tempState;

      private bool _inBounds = false;

      #endregion

      #region Constructor

      public PseudoButton ()
      {
         _state = PseudoButtonState.Normal;
      }

      public PseudoButton (Rectangle bounds)
         :this()
      {
         Bounds = bounds;
      }

      public PseudoButton (Image normal, Image hot, Image pressed)
         :this()
      {
         _normal = normal;
         _hot = hot;
         _pressed = pressed;
      }

      public PseudoButton (Rectangle bounds, Image normal, Image hot, Image pressed)
         :this(normal, hot, pressed)
      {
         Bounds = bounds;
      }

      #endregion

      #region Public methods

      public void Paint (Graphics g)
      {
         switch (_state)
         {
            case PseudoButtonState.Normal:
               PaintButtonNormal(g);
               break;
            case PseudoButtonState.Hot:
               PaintButtonHot(g);
               break;
            case PseudoButtonState.Pressed:
               PaintButtonPressed(g);
               break;
         }
      }

      #endregion

      #region Internal methods

      internal void InvokeMouseLeave (EventArgs e)
      {
         OnMouseLeave(e);
      }

      internal void InvokeMouseMove (MouseEventArgs e)
      {
         if (OuterBounds.Contains(e.Location) && !_inBounds)
         {
            _inBounds = true;
            OnMouseEnter(EventArgs.Empty);
         }
         if (!Bounds.Contains(e.Location) && _inBounds)
         {
            _inBounds = false;
            OnMouseLeave(EventArgs.Empty);
         }
         OnMouseMove(e);
      }

      internal void InvokeMouseDown (MouseEventArgs e)
      {
         OnMouseDown(e);
      }

      internal void InvokeMouseUp (MouseEventArgs e)
      {
         OnMouseUp(e);
      }

      #endregion

      #region Protected virtual methods

      protected virtual void PaintButtonNormal (Graphics g)
      {
         if (_normal == null)
            return;

         g.DrawImage(_normal, Bounds);
      }
      protected virtual void PaintButtonHot (Graphics g)
      {
         if (_hot == null)
            return;

         g.DrawImage(_hot, Bounds);
      }
      protected virtual void PaintButtonPressed (Graphics g)
      {
         if (_pressed == null)
            return;

         g.DrawImage(_pressed, Bounds);
      }

      protected virtual void OnMouseEnter (EventArgs e)
      {
         _state = PseudoButtonState.Hot;

         EventHandler h = MouseEnter;
         if (h != null)
            h(this, e);
      }

      protected virtual void OnMouseLeave (EventArgs e)
      {
         _state = PseudoButtonState.Normal;

         EventHandler h = MouseLeave;
         if (h != null)
            h(this, e);
      }

      protected virtual void OnMouseMove (MouseEventArgs e)
      {
         MouseEventHandler h = MouseMove;
         if (h != null)
            h(this, e);
      }

      protected virtual void OnMouseDown (MouseEventArgs e)
      {
         _tempState = _state;
         _state = PseudoButtonState.Pressed;

         MouseEventHandler h = MouseDown;
         if (h != null)
            h(this, e);
      }

      protected virtual void OnMouseUp (MouseEventArgs e)
      {
         _state = _tempState;

         MouseEventHandler h = MouseUp;
         if (h != null)
            h(this, e);

         OnClick(EventArgs.Empty);
      }

      protected virtual void OnClick (EventArgs e)
      {
         EventHandler h = Click;
         if (h != null)
            h(this, e);
      }

      #endregion

      #region Public events

      public event EventHandler MouseEnter;
      public event EventHandler MouseLeave;

      public event MouseEventHandler MouseMove;
      public event MouseEventHandler MouseDown;
      public event MouseEventHandler MouseUp;

      public event EventHandler Click;

      #endregion
   }

   public enum PseudoButtonState
   {
      Normal = 0,
      Hot = 1,
      Pressed = 2
   }

[EDIT] Noch n Bildchen drangehängt wie das zur Design- und Runtime aussieht.

Schlagwörter: Langsam aufbauende Controls, Hintergrundbild, Flackernde Controls