Combine RoundRectShape with EnableHighlight & EnableBorderHighlight

3 Answers 67 Views
Buttons, RadioButton, CheckBox, etc
tar
Top achievements
Rank 2
Iron
Iron
Iron
tar asked on 05 Aug 2023, 11:05 PM

Hello,

I wonder if it is possible to combine the RoundRectShape with EnableHighlight and EnableBorderHighlight while also have the highlighted parts considering the rounded corners in order to not paint further as those.

 

Now, if I combine those, I have 3 issues (see picture):

1.  the complete "rectangle" button area is highlighted, means: the rounded corners are ignored

2. the highlighted border is also a rectangled border on the corners and not round.

3. when moving the mouse away (near the corners), the "lost focus" event (or something similar) seem not to fire correctly - thereby the highlighted area is still shown

 

Can this be resolved and if so: how?

 

Thanks

3 Answers, 1 is accepted

Sort by
0
Accepted
tar
Top achievements
Rank 2
Iron
Iron
Iron
answered on 09 Aug 2023, 08:33 PM | edited on 09 Aug 2023, 09:06 PM

Thanks to Dinkos answer I got to the direction of using OnPaint(). After further testing, I stumbled over ElementShape.GetElementContour() by which I finally was able to get the correct GraphicsPath of the rounded shape. Thereby I managed to write the effects of EnableHighlight and EnableHighlightBorder myself:

The hovered border:

We need to create a LinearGradientBrush with the actual mouse position and a ColorBlend which holds our colors and their positions. Then we just draw this to the GraphicsPath of our button shape.

The hovered background:

We need to create a GraphicsPath where we add an ellipse which exceeds the boundaries of our button in order to draw a "dimmed" background onto it (and not seeing the full eclipse).

Then we need to create a PathGradientBrush with the actual mouse position on this GraphicsPath with both the dimmed background color and the dimmed highlighted color around the mouse. Then we just fill this to the GraphicsPath of our button shape.

Example:

For an easier control handling (nobody wants to put complex design logic for every control in every form) I created a BaseButton class which inherits from RadButton and can simply be used via the toolbox to put into any form. I have also added the following properties which can be directly set within the Winforms Designer (see the attached properties.png):

  • ShapeBackgroundColor
  • ShapeBackgroundHoveredColor
  • ShapeBorderColor
  • ShapeBorderHoveredColor
  • ShapeMouseHoveredColor
  • ShapeRadius

If the ShapeRadius = 0 the button is displayed as a usual rectangle. Be aware, that ShapeBackgroundHoveredColor and ShapeMouseHoveredColor should have an alpha value below 255. Otherwise the button text/image will be drawn over.

Last but not least, everything which is not depending on the mouse position is only created once and cached:

  • _ButtonPath
  • _HoveredBorderColors
  • _HoveredMousePath

Those fields are set within the OnLoad() method.

The OnMouse...() events are used to determine the mouse position and if the button is hovered.

The Refresh() method calls the OnPaint() event in which PaintHoveredBackground() and PaintHoveredBorder() are called to draw the highlighted background and border when the button is hovered.

1
Dinko | Tech Support Engineer
Telerik team
answered on 08 Aug 2023, 08:24 AM

Hello M.S. ,

Thank you for the provided details. 

The highlight mouse effect behavior is designed to work only for rectangle shapes. That is why it does not respect the round corners of the RoundRectShape. Nevertheless, I have extracted the internal method to try to find a way to make this work. After several tries, I think I was able to implement your requirement. In a few words, I have modified the paint border method to respect the round corners. Keep in mind, that the customization respect only the RoundRectShape type. The custom method catches the scenario in which the mouse is leaving the shape in the corners to not draw the highlight border again.

private void PaintBorderHighlight(Graphics screenRadGraphics, Color color)
{
    Point mousePosition = this.radButton1.ButtonElement.ElementTree.Control.PointToClient(Control.MousePosition);
    var buttonElement = this.radButton1.ElementTree.GetElementAtPoint<RadButtonElement>(mousePosition);
    if (buttonElement != null)
    {

        Rectangle bounds = this.radButton1.ButtonElement.ControlBoundingRectangle;
        RectangleF highlightRect = new RectangleF(mousePosition.X - bounds.Width / 4, mousePosition.Y - bounds.Height * 2, bounds.Width / 2, bounds.Height * 4);
        if (highlightRect.Width <= 0 || highlightRect.Height <= 0)
        {
            return;
        }
        GraphicsPath gp = this.radButton1.ButtonElement.Shape.CreatePath(highlightRect);
        int borderWidth = this.radButton1.ButtonElement.BorderHighlightThickness;


        Graphics graphics = screenRadGraphics;
            GraphicsState state = graphics.Save();
        RectangleF clipRect = new RectangleF(bounds.X + borderWidth / 2, bounds.Y + borderWidth / 2, bounds.Width - (1 * borderWidth), bounds.Height - (1 * borderWidth));
        var p1 = this.radButton1.ButtonElement.Shape.CreatePath(clipRect);

        graphics.SetClip(highlightRect, CombineMode.Intersect);
        Pen pen = new Pen(Color.FromArgb(255, color), borderWidth);
        graphics.SmoothingMode = SmoothingMode.AntiAlias;

        graphics.DrawPath(pen, p1);
        graphics.Restore(state);
        graphics1 = graphics;
        p1.Dispose();
        pen.Dispose();
    }
}

Here is the result:

I am also attaching my sample project which contains the above implementation. Further customization can be performed on your side to cover all requirements. I hope that you find my reply helpful.

Regards,
Dinko | Tech Support Engineer
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

tar
Top achievements
Rank 2
Iron
Iron
Iron
commented on 08 Aug 2023, 02:26 PM

Thanks, it works but the fading color effects are missing. I guess this is too complex?

Meanwhile, I "solved" it by simply switching the shape & region on mouse enter/leave.

Unfortunately, both solutions are not completely satisfying.

tar
Top achievements
Rank 2
Iron
Iron
Iron
commented on 08 Aug 2023, 05:05 PM | edited

I have simplified your code to an own button class inherited from RadButton in order to get rid of PointToClient() GetElementAtPoint() and various unnecessary stuff:

public class BaseButton : RadButton {
  private bool  _IsHovered = false;
  private Point _MousePosition;

  public BaseButton() {
    ButtonElement.Shape = new RoundRectShape(
      10,
      true,
      true,
      true,
      true
    );

    ThemeClassName = "Telerik.WinControls.UI.RadButton"; // typeof(RadButton).FullName;
  }

  protected override void OnMouseEnter(EventArgs e) {
    base.OnMouseEnter(e);

    _IsHovered = true;
  }

  protected override void OnMouseLeave(EventArgs e) {
    base.OnMouseLeave(e);

    _IsHovered = false;
    Refresh();
  }

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

    _MousePosition = e.Location;
    Refresh();
  }

  protected override void OnPaint(PaintEventArgs e) {
    base.OnPaint(e);

    PaintBorderHighlight(
      e.Graphics,
      ButtonElement.BorderHighlightColor,
      ButtonElement.ControlBoundingRectangle,
      ButtonElement.BorderHighlightThickness
    );
  }

  private void PaintBorderHighlight(Graphics graphics, Color color, Rectangle bounds, int width) {
    if (_IsHovered) {
      RectangleF borderSection = new(
        bounds.X + width / 2,
        bounds.Y + width / 2,
        bounds.Width - width,
        bounds.Height - width
      );

      RectangleF highlightArea = new(
        _MousePosition.X - bounds.Width / 4,
        _MousePosition.Y - bounds.Height * 2,
        bounds.Width / 2,
        bounds.Height * 4
      );

      graphics.SmoothingMode = SmoothingMode.AntiAlias;

      graphics.SetClip(
        highlightArea,
        CombineMode.Intersect
      );

      graphics.DrawPath(
        new(
          Color.FromArgb(255, color),
          width
        ),
        ButtonElement
        .Shape
        .CreatePath(borderSection)
      );
    }
  }
}

Now, it should be possible to apply slightly fading off color adjustments (like in the Telerik EnableBorderHighlight which unfortunately does not work for round shapes) with the use of LinearGradientBrush.

I'll get back when I have found a solution.

tar
Top achievements
Rank 2
Iron
Iron
Iron
commented on 08 Aug 2023, 11:29 PM | edited

Got it!

The EnableBorderHighlight effect works (at least, horizontically) by using LinearGradientBrush with ColorBlend. I adjusted the PaintBorderHighlight method accordingly:

private void PaintBorderHighlight(Graphics graphics, Color color, Rectangle bounds, int width) {
      if (!_IsHovered) {
        return;
      }

      Color[] colors = new Color[33];
      float[] positions = new float[33];

      for (int i = -16; i <= 16; i++) {
        colors[i + 16] = Color.FromArgb(
          Math.Max(0, Math.Abs(i * 16) - 1),
          color
        );
      }

      for (int i = 0; i <= 32; i++) {
        positions[i] = (float)1 / 32 * i;
      }

      ColorBlend colorBlend = new() {
        Colors    = colors,
        Positions = positions
      }; LinearGradientBrush linearGradientBrush = new( new Point( _MousePosition.X, _MousePosition.Y ), new Point( _MousePosition.X + bounds.Width * 2, _MousePosition.Y ), Color.Black, Color.Black ) { InterpolationColors = colorBlend }; RectangleF borderSection = new( bounds.X + width / 2, bounds.Y + width / 2, bounds.Width - width, bounds.Height - width ); graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.DrawPath( new(linearGradientBrush), ButtonElement.Shape.CreatePath(borderSection) ); }

By that you also do not need highlightArea and SetClip() anymore.

I guess, the EnableHighlight effect could also be applied by drawing another LinearGradientBrush (or some type of RadialGradientBrush?) to the button shape (area, region) except the borders (of course) with the _MousePosition as centre point. Perhaps I find out how to do this, as well...

Dinko | Tech Support Engineer
Telerik team
commented on 09 Aug 2023, 06:52 AM

In my initial code, you can apply transparency to the color set to the Pen object.

. . . . . . . . . . . . .
graphics.SetClip(highlightRect, CombineMode.Intersect);
Pen pen = new Pen(Color.FromArgb(80, color), borderWidth);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
. . . . . . . . . . . . . 

Basically, we are modifying the border highlight behavior. The highlighted behavior can be applied separately. Setting the EnableHighlight property of the RootElement should do the trick. 

this.radButton1.RootElement.EnableHighlight = true;

Can you check it out and let me know if I am in the right direction?

tar
Top achievements
Rank 2
Iron
Iron
Iron
commented on 09 Aug 2023, 07:47 PM

Hi Dinko,

EnableHighlight will not work correctly as it will ignore the rounded corners and draw over their boundaries.

But I've found a complete solution and will post it in a moment.

0
Dinko | Tech Support Engineer
Telerik team
answered on 10 Aug 2023, 10:50 AM

Hello M. S.,

I am happy to hear that you were able to create a custom solution for your scenario and thank you for sharing it. This way the community can take advantage of it. Your Telerik Points have been updated for your effort.

If you have any other questions, feel free to open a new thread and we will be happy to help.

Regards,
Dinko | Tech Support Engineer
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Tags
Buttons, RadioButton, CheckBox, etc
Asked by
tar
Top achievements
Rank 2
Iron
Iron
Iron
Answers by
tar
Top achievements
Rank 2
Iron
Iron
Iron
Dinko | Tech Support Engineer
Telerik team
Share this question
or