2

I have a question about UI in visual studio, in C#. I want to make my groupbox custom to look like this:

GROUP BOX DESIGN EXAMPLE

and then, I also want it to expend depends on the user's screen resolution, so the size of the group box isn't fixed, and I need it to be for example 80% of the screen.

So my question is actually two question:

  1. make a groupbox costum
  2. make it 80% (for example) wide of the screen.

EDIT: thanks to this answer: How to make group box text alignment center in win forms? I managed to do what I wanted with the colors, now I am just missing the round corners. any ideas?

Community
  • 1
  • 1
TheDaJon
  • 545
  • 2
  • 8
  • 24

4 Answers4

7

As an option you can create a custom control deriving from GroupBox:

  • You need to calculate a round rectangle shape. To do so, as an option you can use AddArc method and add arcs to four corners of a rectangle in a path.
  • To draw header background with hatch style, you can use a HatchBrush. So add a property for title hatch style. This way you can use different HatchStyle values for title back-ground.
  • To have a different title color and title font, add some properties to control.
  • In a more complete implementation, you should implement properties in a way which setting a new value to property cause repainting the control by calling this.Invalidate().
  • To prevent flicker while resizing turn on double buffering by setting DoubleBuffered to true in constructor.
  • To have transparent background in corners, use GroupBoxRenderer.DrawParentBackground. Screenshot

enter image description here

Code

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class RoundPanel : GroupBox
{
    public RoundPanel()
    {
        this.DoubleBuffered = true;
        this.TitleBackColor = Color.SteelBlue;
        this.TitleForeColor = Color.White;
        this.TitleFont = new Font(this.Font.FontFamily, Font.Size + 8, FontStyle.Bold);
        this.BackColor = Color.Transparent;
        this.Radious = 25;
        this.TitleHatchStyle = HatchStyle.Percent60;
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        GroupBoxRenderer.DrawParentBackground(e.Graphics, this.ClientRectangle, this);
        var rect = ClientRectangle;
        using (var path = GetRoundRectagle(this.ClientRectangle, Radious))
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            rect = new Rectangle(0, 0,
                rect.Width, TitleFont.Height + Padding.Bottom + Padding.Top);
            if(this.BackColor!= Color.Transparent)
                using (var brush = new SolidBrush(BackColor))
                    e.Graphics.FillPath(brush, path);
            var clip = e.Graphics.ClipBounds;
            e.Graphics.SetClip(rect);
            using (var brush = new HatchBrush(TitleHatchStyle,
                TitleBackColor, ControlPaint.Light(TitleBackColor)))
                e.Graphics.FillPath(brush, path);
            using (var pen = new Pen(TitleBackColor, 1))
                e.Graphics.DrawPath(pen, path);
            TextRenderer.DrawText(e.Graphics, Text, TitleFont, rect, TitleForeColor);
            e.Graphics.SetClip(clip);
            using (var pen = new Pen(TitleBackColor, 1))
                e.Graphics.DrawPath(pen, path);
        }
    }
    public Color TitleBackColor { get; set; }
    public HatchStyle TitleHatchStyle { get; set; }
    public Font TitleFont { get; set; }
    public Color TitleForeColor { get; set; }
    public int Radious { get; set; }
    private GraphicsPath GetRoundRectagle(Rectangle b, int r)
    {
        GraphicsPath path = new GraphicsPath();
        path.AddArc(b.X, b.Y, r, r, 180, 90);
        path.AddArc(b.X + b.Width - r - 1, b.Y, r, r, 270, 90);
        path.AddArc(b.X + b.Width - r - 1, b.Y + b.Height - r - 1, r, r, 0, 90);
        path.AddArc(b.X, b.Y + b.Height - r - 1, r, r, 90, 90);
        path.CloseAllFigures();
        return path;
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Nice. There is no real problem deriving it from GroupBox; but I'm not sure if it has any advantages either.. I wonder what the HatchStyle is supposed to achieve? – TaW Aug 30 '16 at 14:38
  • @TaW Thanks, Since I call `base.OnPaint(e)`, if I derive from a `GroupBox` then bottom corners of `GroupBox` will be visible in bottom corners of the control. Getting rid of borders is more simple using `Panel`. I did't try much, just wanted to share some idea about rendering custom controls. – Reza Aghaei Aug 30 '16 at 14:43
  • Well, I did my own try and solved that one by `e.Graphics.Clear(Parent.BackColor);` before `e.Graphics.FillPath(Brushes.White, gp1); e.Graphics.DrawPath(Pens.CornflowerBlue, gp1); e.Graphics.FillPath(Brushes.CornflowerBlue, gp2);` – TaW Aug 30 '16 at 15:06
  • @TaW You made me try more ;) Yes, it can be an option but `Parent.BackColor` may be not enough, for example the parent can have `BackGroundImage`. I used `GroupBoxRenderer.DrawParentBackground(e.Graphics, this.ClientRectangle, this);` to solve the problem :) – Reza Aghaei Aug 30 '16 at 15:11
  • thank you a lot sir, very very helpfull and much to learn from it. thumbs up – TheDaJon Aug 30 '16 at 15:19
  • @Reza: Yup, that is true. I tried to avoid setting the ClipBounds as they don't look smooth. _One way or the other, something's got to give_ ;-) – TaW Aug 30 '16 at 15:28
  • 1
    @Taw Thank you for your attention. I think now the answer is more better. I used `GroupBox` so I don't need to override `Text`. I used `GroupBoxRenderer.DrawParentBackground` so I don't need to use a `ContentBackColor` and `BackColor is enough`. I also turned on double buffering. – Reza Aghaei Aug 30 '16 at 15:29
  • 1
    There may be some other enhancements too, but probably I'll stop enhancing the answer and rest of features or corrections are up to future readers. – Reza Aghaei Aug 30 '16 at 15:32
  • 1
    @Reza. Sure. I suddenly can see the hatch pattern in the OP btw; I can't believe it to be anything but a screenshot artifact though ;-) – TaW Aug 30 '16 at 15:41
  • @Reza. is there a way to add a scrollable property to the group box or I must add a panel inside? – TheDaJon Sep 04 '16 at 05:30
  • as a simple workaround you can put an scrollable panel in groupubox. Also you can change the base class to panel. Look into previous revisions if you want to have panel base class. – Reza Aghaei Sep 04 '16 at 07:50
0

For WPF:

You can create a Style to make your GroupBox appear in a different way.

Maybe this can help you there: Styling a GroupBox

For Windows Forms:

To change the Layout you can have a look at this: https://stackoverflow.com/a/31828317/4610605

To resize the GroupBox you can use this:

System.Windows.SystemParameters.PrimaryScreenWidth
System.Windows.SystemParameters.PrimaryScreenHeight

GroupBox gb = new GroupBox();
gb.Width = (System.Windows.SystemParameters.PrimaryScreenWidth * 0.8) //Get your 80% ScreenWidth here.
Community
  • 1
  • 1
Felix D.
  • 4,811
  • 8
  • 38
  • 72
  • Hi, thank you. The url you gave me may be enough, tough how can I edit this XML they are editing? where can I see it? I am using VisualStudio – TheDaJon Aug 30 '16 at 12:15
  • this solution is for `WPF` only. In `WindowsForms` it is way more complicated to restyle controls. I will try to work out a short example for WF – Felix D. Aug 30 '16 at 12:25
  • Thanks to what you posted I managed to create what I needed with the colors, now the only thing that I am missing is the round edges. any ideas? – TheDaJon Aug 30 '16 at 13:59
0

One option is to develop your own custom control derived from GroupBox and override the OnPaint() method to do your drawing.

public class CustomGroupBox : GroupBox
{
    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.FillRectangle(Brushes.Azure, this.ClientRectangle);
        //base.OnPaint(e);
    }

}

The new control will appear in the Toolbox after a build automatically.

To draw this object the DrawPath method can be used to draw the outer rectangle and the FillPath method can be used to fill the upper bar.

https://msdn.microsoft.com/en-us/library/system.drawing.graphics.drawpath(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/system.drawing.graphics.fillpath(v=vs.110).aspx

Elastic design can be done with TableLayoutPanel.

Martin Staufcik
  • 8,295
  • 4
  • 44
  • 63
  • can you please write more details or give an example to how to do it? how can I develop my own control? – TheDaJon Aug 30 '16 at 12:25
  • While true it will not be rounded, will it..? – TaW Aug 30 '16 at 12:34
  • 1
    Developing custom WinForm controls is very wide topic, for this group box, it should suffice to override the OnPaint method and do the drawing with methods from the `Graphics` class. – Martin Staufcik Aug 30 '16 at 12:35
  • @TaW true, it will not, rounded rectangle can be drawn with `GraphicsPath`. For the upper rounded bar, it can be filled using a graphics path too. – Martin Staufcik Aug 30 '16 at 12:42
  • @Taw can you be my hero and save my day again? I am learning step by step thanks to your answers – TheDaJon Aug 30 '16 at 12:45
  • Rounded rectangles are simply not part of gdi+; it can be done but it is quite some work to get it right.. And I don't even understand what _80% of the screen_ is supposed to mean. – TaW Aug 30 '16 at 14:06
  • @Taw, by saying 80% of the screen, I mean that I have a block that captures for example 500px of the winform, and in the rest of the place I want to fill with this group box. the problem is that if the main block is 500px, then some screens have leftover of 300px for the groupbox to show for example, and others have 450px for example, so I need this groupbox to be able to stretch, and fill all the left space. – TheDaJon Aug 30 '16 at 14:14
  • Any Control with Dock=Fill will stetch to fill the container. You can use several container controls like Panel, FlowLayoutPanel, SplitContainer and several Fill and Docking options to automate the layout. Some things will have to be done it code though. When the Form is resized you can calculate to widths you want and set the GroupBox or other containers to the right sizes and/or positions.. – TaW Aug 30 '16 at 14:35
0

This is a pure XAML solution, no custom control or code. It just uses the standard WPF style/template technique. Generally it is preferred to use styles/templates over custom controls.

GroupBox header can be different size so I added the option of changing the header text with the "Tag" property (currently set to 18).

Demo of usage:

<GroupBox Style="{StaticResource GBStyled}" Tag="18" 
          Header="Hello" Height="150" Width="180">
    <TextBlock TextWrapping="Wrap">Text is different size to Header</TextBlock>
</GroupBox>

Style definition:

<Style x:Key="GBStyled" TargetType="GroupBox">
    <!-- These 2 setters make the GroupBox less blurry -->
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="UseLayoutRounding" Value="True"/>
    <!-- Default Background colour -->
    <Setter Property="Background" Value="White"/>
    <!-- Template of GroupBox -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="GroupBox">
                <ControlTemplate.Resources>
                    <!-- Custom hatched brush -->
                    <VisualBrush x:Key="MyVisualBrush" TileMode="Tile" Viewport="0,0,5,5" ViewportUnits="Absolute" Viewbox="0,0,15,15" ViewboxUnits="Absolute">
                        <VisualBrush.Visual>
                            <Grid Background="{StaticResource DarkBlueBrush}">
                                <Path Data="M 0 15 L 15 0" Stroke="White" />
                                <Path Data="M 0 0 L 15 15" Stroke="White" />
                            </Grid>
                        </VisualBrush.Visual>
                    </VisualBrush>
                </ControlTemplate.Resources>
                <Grid>
                    <Grid.Resources>
                    </Grid.Resources>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Border Grid.Row="0" CornerRadius="5,5,0,0" BorderThickness="1" BorderBrush="{StaticResource DarkBlueBrush}" Background="{StaticResource MyVisualBrush}">
                        <!-- FontSize of the header is changed via the Templates "Tag" property -->
                        <Label Foreground="White" FontSize="{Binding RelativeSource={RelativeSource AncestorType=GroupBox}, Path=Tag}" HorizontalAlignment="Center" FontWeight="Bold">
                            <!-- DropShadow makes the label standout from the background -->
                            <Label.Effect>
                                <DropShadowEffect ShadowDepth="0" BlurRadius="3" />
                            </Label.Effect>
                            <ContentPresenter Margin="0" ContentSource="Header" RecognizesAccessKey="True"/>
                        </Label>
                    </Border>
                    <Border Grid.Row="1" CornerRadius="0,0,5,5" BorderThickness="1,0,1,1" BorderBrush="{StaticResource DarkBlueBrush}" Background="{TemplateBinding Background}">
                        <ContentPresenter Margin="4" />
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>