I am creating a project that needs to have a vertical scroll bar with multiple pictures like the server explorer in discord:
For example:
how can I mimic in WinForms in C# (not only having pictures scrolling but also the pictures can have events attached to them?
First you need add a Parent Panel, i'm used the pnServers.
Set property value:
AutoScroll = True;
On Code Behind you can create a List of Rounded Pictures.
private void DiscordServerBarExample_Load(object sender, System.EventArgs e)
{
// Example, in your case this looping is based on return (Database, Api, ...).
for (int i = 1; i <= 10; i++)
{
Panel pnServer = new Panel()
{
Dock = DockStyle.Top,
Height = pnServers.Width,
Padding = new Padding(10)
};
RoundedPictureBox serverImage = new RoundedPictureBox()
{
SizeMode = PictureBoxSizeMode.CenterImage,
Dock = DockStyle.Fill
};
serverImage.Image = Properties.Resources._255352;
pnServer.Controls.Add(serverImage);
pnServers.Controls.Add(pnServer);
}
}
Rounded Picture Box Code:
public class RoundedPictureBox : PictureBox
{
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddEllipse(0, 0, Width - 1, Height - 1);
Region rg = new Region(gp);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawEllipse(new Pen(new SolidBrush(this.BackColor), 1), 0, 0, this.Width - 1, this.Height - 1);
Region = rg;
}
}
}
And this is final result.
Related
In my WinForm project I have a DataGridView control.
I achieved to merge the Header cells of 2 Columns. It is working fine but there is a problem when scrolling. The image below should explain it.
Can anyone help?
This is the code I use to merging the Column Header cells.
The indexes of the merged Columns are 20 and 21.
private void loadEvents()
{
this.dgv_db_door.Paint += new PaintEventHandler(dgv_db_door_Paint);
this.dgv_db_door.Scroll += new ScrollEventHandler(dgv_db_door_Scroll);
this.dgv_db_door.ColumnWidthChanged += DataGridView1_ColumnWidthChanged;
this.dgv_db_door.Resize += DataGridView1_Resize;
}
private void dgv_db_door_Paint(object sender, PaintEventArgs e)
{
string doorCloser = "DOOR CLOSER";
//Index numbers of merged columns ara 20 and 21;
int mergedColumn1 = 20;
int mergedColumn2 = 21;
Rectangle r1 = dgv_db_door.GetCellDisplayRectangle(mergedColumn1, -1, true);
int w2 = dgv_db_door.GetCellDisplayRectangle(mergedColumn2, -1, true).Width;
r1.X += 1;
r1.Y += 1;
r1.Width = r1.Width + w2 - 2;
r1.Height = r1.Height / 2 - 2;
e.Graphics.FillRectangle(new SolidBrush(dgv_db_door.ColumnHeadersDefaultCellStyle.BackColor), r1);
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(doorCloser, dgv_db_door.ColumnHeadersDefaultCellStyle.Font,
new SolidBrush(dgv_db_door.ColumnHeadersDefaultCellStyle.ForeColor), r1, format);
}
private void dgv_db_door_Scroll(object sender, ScrollEventArgs e)
{
if (e.OldValue > e.NewValue)
{
}
else
{
this.InvalidateHeader();
}
}
private void DataGridView1_Resize(object sender, EventArgs e)
{
this.InvalidateHeader();
}
private void DataGridView1_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
this.InvalidateHeader();
}
private void InvalidateHeader()
{
Rectangle rtHeader = this.dgv_db_door.DisplayRectangle;
rtHeader.Height = this.dgv_db_door.ColumnHeadersHeight / 2;
this.dgv_db_door.Invalidate(rtHeader);
}
A few modifications to the drawing procedure:
The custom Header drawing Rectangle is sized calculating the Width of all the Columns in the specified Range.
The position of the drawing area is calculated using the left-most Column's position, the RowHeadersWidth and the current DataGridView.HorizontalScrollingOffset, which defines the horizontal scroll position of the client rectangle.
To draw the custom Header, a clipping region is created - using Graphics.SetClip() - to exclude the Row Header from the drawing area.
The custom Header is only rendered when visible on screen.
I'm using TextRenderer.DrawText instead of Graphics.DrawString(), since this is the method used by this control to render its content.
Hence, I'm calculating the height of the text bounds using TextRenderer.MeasureText()
TextFormatFlags.PreserveGraphicsClipping is used to instruct TextRenderer not to draw the text outside the clipping region of the Graphics object.
Note 1: I've added Double-Buffering to the DataGridView, to avoid any flickering when clicking the Header Cells. It may have an impact when the grid needs to render a high number of Rows.
Note 2: You can remove all those InvalidateHeader() calls, not needed here.
► This new behavior allows to reset the range of Columns to include in the custom Header and the Header's Text at any time (as shown in the visual example).
This is how it looks now:
using System.Reflection;
TextFormatFlags centerTopflags =
TextFormatFlags.HorizontalCenter | TextFormatFlags.Top | TextFormatFlags.PreserveGraphicsClipping;
string mergedHeaderText = string.Empty;
int[] mergedColumns = null;
public void SomeForm()
{
this.InitializeComponent();
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
dgvTest.GetType().GetProperty("DoubleBuffered", flags).SetValue(dgvTest, true);
mergedColumns = new int[] { 20, 21 };
mergedHeaderText = "DOOR CLOSER"
}
private void dgv_db_door_Paint(object sender, PaintEventArgs e)
{
var dgv = sender as DataGridView;
var headerStyle = dgv.ColumnHeadersDefaultCellStyle;
int colsWidth = -1;
int colsLeft = 1;
// Absolute Width of the merged Column range
for (int i = 0; i < mergedColumns.Length; i++) {
var col = dgv.Columns[mergedColumns[i]];
colsWidth += col.Visible ? col.Width : 0;
}
// Absolute Left position of the first Column to merge
if (mergedColumns[0] > 0) {
colsLeft += dgv.Columns.OfType<DataGridViewColumn>()
.Where(c => c.Visible).Take(mergedColumns[0]).Sum(c => c.Width);
}
// Merged Headers raw drawing Rectangle
var r = new Rectangle(
dgv.RowHeadersWidth + colsLeft - dgv.HorizontalScrollingOffset, 2,
colsWidth, dgv.ColumnHeadersHeight);
// Measure the Height of the text to render - no wrapping
r.Height = TextRenderer.MeasureText(e.Graphics, mergedHeaderText, headerStyle.Font, r.Size, centerTopflags).Height;
// Draw the merged Headers only if visible on screen
if (r.Right > dgv.RowHeadersWidth || r.X < dgv.DisplayRectangle.Right) {
// Clip the drawing Region to exclude the Row Header
var clipRect = new Rectangle(
dgv.RowHeadersWidth + 1, 0,
dgv.DisplayRectangle.Width - dgv.RowHeadersWidth, dgv.ColumnHeadersHeight);
e.Graphics.SetClip(clipRect);
using (var brush = new SolidBrush(headerStyle.BackColor)) e.Graphics.FillRectangle(brush, r);
TextRenderer.DrawText(e.Graphics, mergedHeaderText, headerStyle.Font, r, headerStyle.ForeColor, centerTopflags);
e.Graphics.ResetClip();
}
}
I have WPF app with ContentControl where the content is WindowsFormsHost with a Child having custom panel, which renders SDL stream. Now I have added button to disable/enable audio of the stream. Everything works fine, but I cannot make the button icon transparent. How can I do that? Is it possible at all?
AudioButton = new System.Windows.Forms.Button()
{
Enabled = AudioButtonEnabled,
BackColor = Color.Transparent,
Image = Image.FromFile(#".\Images\audioDisabled.png"),
Width = 30,
Height = 30,
FlatStyle = System.Windows.Forms.FlatStyle.Flat
};
AudioButton.FlatAppearance.BorderSize = 0;
AudioButton.Click += (object sender, EventArgs e) =>
{
};
SDLRenderer.AddButton(AudioButton);
The image (icon) is transparent as well.
The workaround can be to create custom WinForms button, override OnPaint event and make the specified color transparent for bitmap by calling Bitmap.MakeTransparent()
public class CustomButton : Button
{
private Color TransparentColor;
public CustomButton() : base()
{
TransparentColor = Color.FromArgb(192, 192, 192);
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.Image != null)
{
Bitmap bmp = ((Bitmap)this.Image);
bmp.MakeTransparent(TransparentColor);
int x = (this.Width - bmp.Width) / 2;
int y = (this.Height - bmp.Height) / 2;
e.Graphics.DrawImage(bmp, x, y);
}
base.OnPaint(e);
}
}
I want to include the colored panel below into my form:
For this I have created custom panel which will change Border color based on Radio Button selection. My panel code is
InfoPanel.cs
class InfoPanel : Panel
{
private Color colorBorder = Color.Transparent;
public InfoPanel()
: base()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(
new SolidBrush(colorBorder), 2),
e.ClipRectangle);
e.Graphics.DrawLine(new Pen(new SolidBrush(colorBorder), 0), 50, 0, 50, 50); //drawing a line to split the child & parent info panel
}
public Color BorderColor
{
get
{
return colorBorder;
}
set
{
colorBorder = value;
}
}
}
In my form,
1. created one parent Info Panel
2. created one child panel with Picture box
3. One label in parent info panel to show the information
Now for the parent panel I am changing the colors [back, border] & text based on user selection & for child panel I am not changing border color but updating back color based on user selection.
Below is the code for changing the panel colors, image, text update:
private void rbIPAddress_CheckedChanged(object sender, EventArgs e)
{
if (rbIPAddress.Checked)
{
ParentInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#FFFFEE");
ParentInfoPanel.BorderColor = System.Drawing.ColorTranslator.FromHtml("#DADA85");
ChildInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#F6F6D8");
InfoPanelPictureBox.Image = Template.InfoPanelInfoImage;
Infolabel.Text = "IP Address is already configured. You can switch to Forward Lookup Zone by choosing other configuration. *IP Address \ncan be either LB IP Address.";
txtBoxIPAddress.Enabled = true;
textBoxPort.Enabled = true;
}
else
{
Infolabel.Text = "";
txtBoxIPAddress.Text = "";
txtBoxIPAddress.Enabled = false;
textBoxPort.Enabled = false;
}
}
private void rbForwardLookupZone_CheckedChanged(object sender, EventArgs e)
{
if (rbForwardLookupZone.Checked)
{
ParentInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#FFFFEE");
ParentInfoPanel.BorderColor = System.Drawing.ColorTranslator.FromHtml("#DADA85");
ChildInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#F6F6D8");
InfoPanelPictureBox.Image = Template.InfoPanelInfoImage;
Infolabel.Text = "Forward Lookup Zone is already configured. You can switch to IP Address by choosing other configuration and \nchanging port number will affect Firewall rules.";
textBoxControlPlane.Enabled = true;
if (string.IsNullOrEmpty(textBoxControlPlane.Text))
{
textBoxControlPlane.Text = Constants.DefaultControlPlaneDomain;
}
}
else
{
Infolabel.Text = "";
textBoxControlPlane.Text = "";
textBoxControlPlane.Enabled = false;
}
}
Note: used next line character to display label text in multiple line
Output: Everything is ok but in the end of label text I am getting another rectangle box. I'm wondering why is showing like this? Am I doing wrong? Please help me on this.
The issue is that you're using e.ClipRectangle. It informs you which portion of the control needs to be redrawn. This is sometimes only a small part of the control rather than the whole thing (in your case the area of the extra rectangle). Always draw the control's full rectangle instead.
Also, you must dispose of both the Pen and SolidBrush. Failing to do so causes memory leaks. Utilize the using statement.
using(SolidBrush brush = new SolidBrush(colorBorder))
using(Pen pen = new Pen(brush, 2))
{
e.Graphics.DrawRectangle(pen, new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1));
e.Graphics.DrawLine(pen, 50, 0, 50, 50);
}
I've made a custom control in C# and anytime that the user's cursor is hovering over the custom control I want the cursor to be displayed as the 'Hand'. Where do i place the code to do such a thing?
????.Cursor = Cursors.Hand;
in order to make it so the Hand Cursor is being displayed when hovering over this custom control?
namespace CustomRangeBar
{
public partial class RangeBar : UserControl
{
public RangeBar()
{
InitializeComponent();
label1.ForeColor = Color.Black;
this.ForeColor = SystemColors.Highlight; // set the default color the rangeBar
this.Click += new EventHandler(RangeBar_Click);
}
protected float percent = 0.0f; // Protected because we don't want this to be accessed from the outside
// Create a Value property for the rangeBar
public float Value
{
get
{
return percent;
}
set
{
// Maintain the value between 0 and 100
if (value < 0) value = 0;
else if (value > 100) value = 100;
percent = value;
label1.Text = value.ToString();
//redraw the rangeBar every time the value changes
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Brush b = new SolidBrush(this.ForeColor); //create brush that will draw the background of the range bar
// create a linear gradient that will be drawn over the background. FromArgb means you can use the Alpha value which is the transparency
LinearGradientBrush lb = new LinearGradientBrush(new Rectangle(0, 0, this.Width, this.Height), Color.FromArgb(255, Color.White), Color.FromArgb(50, Color.White), LinearGradientMode.Vertical);
// calculate how much has the rangeBar to be filled for 'x' %
int width = (int)((percent / 100) * this.Width);
e.Graphics.FillRectangle(b, 0, 0, width, this.Height);
e.Graphics.FillRectangle(lb, 0, 0, width, this.Height);
b.Dispose(); lb.Dispose();
}
private void RangeBar_SizeChanged(object sender, EventArgs e)
{
// maintain the label in the center of the rangeBar
label1.Location = new Point(this.Width / 2 - 21 / 2 - 4, this.Height / 2 - 15 / 2);
}
}
}
public void RangeBar_Click(object obj, EventArgs ea)
{
// This get executed if the pictureBox gets clicked
label1.text = "Increment 1";
}
UserControl derives from Control and therefore should already have a Cursor property inherited from that class. Do you not see a Cursor property in code/Properties?
I am having a problem on my extendedtabcontol class, I cannot get rid of the dotted box or the visual style box on the selected tab. I have my own DrawItem (see below), I have overridden several methods from the tabcontrol and I have even overridden the WM_SETFOCUS and WM_PAINT in WndProc but the box and highlight will not go away. Is there anyway to turn them off (the box or visual style) or a simple way to draw over them / stop them drawing?
The user can tell which tab is selected because it is drawn in black when the others are grey.
protected void OnDrawItem(object sender, DrawItemEventArgs e)
{
// VisualStyleRenderer renderer =
// new VisualStyleRenderer(VisualStyleElement.Tab.TabItem.Disabled);
DrawItemState ds = e.State;
SolidBrush mybrush = new SolidBrush(Color.FromArgb(255, 151, 0, 11));
Rectangle tabArea2 = new Rectangle(0, 0, this.Size.Width+10, this.Size.Height+10);
//renderer.DrawBackground(e.Graphics, tabArea2);
e.Graphics.FillRectangle(mybrush, tabArea2);
int i = 0;
foreach (TabPage tb in this.TabPages)
{
Rectangle tabArea = this.GetTabRect(i);
Point newp = new Point(tabArea.Location.X,tabArea.Location.Y + 2);
tabArea.Location = newp;
if (this.SelectedIndex != i)
{
RectangleF tabTextArea = (RectangleF)this.GetTabRect(i);
e.Graphics.DrawImage(global::Config.Properties.Resources.Tab2, tabArea.Location);
}
else
{
e.Graphics.DrawImage(global::Config.Properties.Resources.Tab1, tabArea.Location);
}
SizeF size = e.Graphics.MeasureString(tb.Name.ToString().Trim(), drawFont);
PointF pf = new PointF();
pf.X = tabArea.X + (tabArea.Width / 2) - (size.Width/2);
pf.Y = tabArea.Y + (tabArea.Height / 2) - (size.Height/2);
e.Graphics.DrawString(tb.Name.ToString().Trim(), drawFont, drawBrush, pf);
i++;
}
}
I would post an image but I don't have the reputation.
Similar question for example:
Can I remove the dotted focus rectangle over tabs on a TabControl?