Drawn line isn't shown on a combobox - c#

I need your help in following question (using .Net 3.5 and Windows Forms):
I just simply want to draw a line on a middle of a combobox (Windows Forms) that is situated on a form.
The code I use is:
void comboBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(new Pen(Brushes.DarkBlue),
this.comboBox1.Location.X,
this.comboBox1.Location.Y + (this.comboBox1.Size.Height / 2),
this.comboBox1.Location.X + this.comboBox1.Size.Width,
this.comboBox1.Location.Y + (this.comboBox1.Size.Height / 2));
}
To fire a paint event:
private void button1_Click(object sender, EventArgs e)
{
comboBox1.Refresh();
}
When I execute the code and press button the line isn't drawn. In debug the breakpoint at the paint handler isn't being hit. The strange thing is that on MSDN there is a paint event in ComBox's events list, but in VS 2010 IntelliSense doesn't find such event in ComboBox's members
Thanks.

public Form1()
{
InitializeComponent();
comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
comboBox1.DrawItem += new DrawItemEventHandler(comboBox1_DrawItem);
}
void comboBox1_DrawItem(object sender, DrawItemEventArgs e) {
e.DrawBackground();
e.Graphics.DrawLine(Pens.Black, new Point(e.Bounds.Left, e.Bounds.Bottom-1),
new Point(e.Bounds.Right, e.Bounds.Bottom-1));
TextRenderer.DrawText(e.Graphics, comboBox1.Items[e.Index].ToString(),
comboBox1.Font, e.Bounds, comboBox1.ForeColor, TextFormatFlags.Left);
e.DrawFocusRectangle();
}

Paint event will not fire.
What do you want is possible only when DropDownStyle == ComboBoxStyle.DropDownList:
comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox1.DrawItem += (sender, args) =>
{
var ordinate = args.Bounds.Top + args.Bounds.Height / 2;
args.Graphics.DrawLine(new Pen(Color.Red), new Point(args.Bounds.Left, ordinate),
new Point(args.Bounds.Right, ordinate));
};
This way you can draw selected item area yourself.

Related

How to draw border for panel which is continously resizing c#

Using panel as a selection indicator . The panel changes its size according to cursor position on MouseMove event . However when I draw the border as below , previous borders leave their mark on the panel and it displays too many border within same panel . Even tried refresh() before every draw but that makes it glitchy and slow
private void panel1_Paint(object sender, PaintEventArgs e)
{
this.panel1.Refresh();
ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
private void drawboard_MouseMove(object sender, MouseEventArgs e)
{
panel1.Width = e.Location.X - panel1.Left;
panel1.Height = e.Location.Y - panel1.Top;
}
First off, you should never call control paint affecting methods like Invalidate or Refresh inside the control paint handler.
You can solve the original issue by calling Invalidate or Refresh after modifying the size of the panel. Note that it's better to set the Size property with one call rather than Width and Height separately:
private void panel1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
private void drawboard_MouseMove(object sender, MouseEventArgs e)
{
var size = new Size(Math.Max(e.Location.X - panel1.Left, 0),
Math.Max(e.Location.Y - panel1.Top, 0));
if (panel1.Size != size)
{
panel1.Size = size;
panel1.Invalidate();
}
}
A better option is to set ResizeRedraw property of the selection panel to true. Since it's a protected property, you need to create and use your own Panel subclass. As a bonus, you can also set DoubleBuffered property to true to avoid the flickering, and also move the painting code inside:
class SelectionBox : Panel
{
public SelectionBox()
{
ResizeRedraw = true;
DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
ControlPaint.DrawBorder(e.Graphics, ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
}
Make your panel1 to be SelectionBox, remove the Paint event handler, and then the mouse move handler could be simple
private void drawboard_MouseMove(object sender, MouseEventArgs e)
{
panel1.Size = new Size(Math.Max(e.Location.X - panel1.Left, 0),
Math.Max(e.Location.Y - panel1.Top, 0));
}

How to draw a rectangle on a button click?

I want when I click button, add one rectangle to the form
I can add in form paint how much I want but I can't add shape like rectangle by click button and I searched about it but I didn't find a solution for it
is here somebody know how to do it?
This is my code in form paint
private void Form1_Paint(object sender, PaintEventArgs e)
{
locationX = locationX + 20;
locationY = locationY + 20;
e.Graphics.DrawRectangle(Pens.Black,
new Rectangle(10 + locationX, 10 + locationY, 50, 30));
}
and this one is my button code
private void button1_Click(object sender, EventArgs e)
{
this.Paint += Form1_Paint;
}
but its not working when I click button. why its not working?
The line
this.Paint += Form1_Paint;
Associate the event Paint of your Form to your function Form1_Paint. It doesn't trigger it. This is something you want to do only 1 time, not everytime you hit a button.
To trigger the Paint event, the usual way is to call the Invalidate() method of the Form class. In fact, Invalidate is a method of Control. But Form derivate from Control, so we have access to the method in Form too.
So the right way to trigger a repaint in Windows Forms is to put the subscribe in the Load method :
private void Form1_Load(object sender, EventArgs e)
{
this.Paint += Form1_Paint;
}
It should already be hidden in the auto generated code.
Your method Form1_Paint is ok.
Finally, the button click method should be :
private void button1_Click(object sender, EventArgs e)
{
this.Invalidate(); // force Redraw the form
}
From the doc :
Invalidate() : Invalidates the entire surface of the control and causes the control to be redrawn.
Edit :
With this method, you can draw only 1 rectangle at a time, because the whole surface is redrawn, so the surface is completly erase, and then it draws only what you asked in the Form1_Paint method.
For the answer on how to draw multiple rectangles, you should create a List of Rectangle. At each click button, you add a Rectangle to the list, and you redraw all the rectangles.
List<Rectangle> _rectangles = new List<Rectangle>();
private void button1_Click(object sender, EventArgs e)
{
locationX = locationX + 20;
locationY = locationY + 20;
var rectangle = new Rectangle(locationX, locationY, 50, 30));
this._rectangles.Add(rectangle);
this.Invalidate(); // force Redraw the form
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
foreach(var rectangle in this._rectangles)
{
e.Graphics.DrawRectangle(Pens.Black, rectangle);
}
}
to call a method you need the parenthesys.
private void button1_Click(object sender, EventArgs e)
{
Form1_Paint(sender, e);
}

How can I add events to a PictureBox dynamically created by clicking button? (C#)

I'm creating dynamically a bunch of PictureBoxes by clicking a button in my form, but I'm not clear on how can I add an event to each one. Here's my idea
private void insertarBloqueToolStripMenuItem_Click(object sender, EventArgs e)
{
pbA = new PictureBox{
Name = "picturebox" + contB,
Image = new Bitmap(bloque),
Size = pbA.Image.Size,
};
pbA.MouseDown += new MouseEventHandler(pbA_MouseDown);
pbA.MouseUp += new MouseEventHandler(pbA_MouseUp);
pbA.MouseMove += new MouseEventHandler(pbA_MouseMove);
pbA.Paint += new PaintEventHandler(pbA_Paint);
pbA.Cursor = Cursors.Hand;
listaBloques.Add(pbA);
panel1.Controls.Add(pbA);
}
That's what I got, can you help me with that idea? And How can I can create a single method to move one PictureBox on the list? Thank you
Your idea is quite good!
But it would be enought to write (but yours works also fine):
pbA.Paint += pbA_Paint;// generally: ... += methodName;
And you have to create the event itselve. For example:
void pbA_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
//do some cool stuff!
}
If it is painted on any of the PictureBoxes then this method will be called. If you want to know which PictureBox has been painted on then simply write:
void pbA_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
PictureBox picBox_active = sender as PictureBox;
//do some cool stuff!
}
And for making one single method to move just one PictureBox you could give the desired object as a parameter:
void move(PictureBox picBox, int moveX, int moveY)
{
picBox.Location = new Point(moveX, moveY);
}
And you can call this method by:
PictureBox picBoxTest = new PictureBox();
move(picBoxTest, 5, 5);
And to make it clearer with an example:
void pbA_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
PictureBox picBox_active = sender as PictureBox;
move(picBox_active, 5, 5);
}
The PictureBox that has been painted will be moved to the coordinates 5,5.

How to add and remove "custom" tabs in C#

I am making an application that requires tabs (tab-control) to be added or removed. I have done the add and remove for tabs fine, but I have custom buttons instead of using the tabs. (This button will go the the first tab page when clicked):
//This will make it go to TAB 1
private void button1_Click(object sender, EventArgs e)
{
tabControl1.SelectedIndex = 0;
}
//This will change the MOUSEENTER to the correct image
private void button1_MouseEnter(object sender, EventArgs e)
{
button1.MouseEnter += new EventHandler(button1_MouseEnter);
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.Tab_Down));
}
//This will change the MOUSELEAVE to the correct image
private void button1_MouseLeave(object sender, EventArgs e)
{
button1.MouseLeave += new EventHandler(button1_MouseLeave);
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.Tab_Norm));
}
However, how will I create it so when you click the "Add Tab" button it creates a new tab but it also creates a new button with tabControl1.SelectedIndex = 1; in it for example.
Edit
I have done this to add a new tab (to tabControl1):
private void button2_Click(object sender, EventArgs e)
{
string title = "TabPage " + (tabControl1.TabCount + 1).ToString();
TabPage myTabPage = new TabPage(title);
tabControl1.TabPages.Add(myTabPage);
}
This adds a new tab on top of the existing ones fine. But I how do I do it so that it also creates a new button with the properties of the button above but makes it so instead of tabControl1.SelectedIndex = 1; it does tabControl1.SelectedIndex = 3; and goes up every time I add a new tab? - Thanks
After a few updates here is a new version of my answer. It shows you how to
Add a Button which adds a new TabPages and selects it
Make the Tab control draw closing 'x' characters to the right of each tab, like Firefox or Visual Studio do it.
Make the add Button like you want it, maybe the Text="+" and the height like the tabs' height. It is automatically positioned on the tab. You may need to reposition it, if your Tab is moved or resized.
In the Form_Load event the Tab is set to OwnerDrawFixed and each page's text is append by a few blanks to make room for the closing x. The code creates a class variable closeX to hold the current x-rectangle and tests it in the MouseClick event.
Make sure to wire up the tabControl1_DrawItem and tabControl1_MouseClick events!
private void cb_addPage_Click(object sender, EventArgs e)
{
string title = "TabPage " + (tabControl1.TabCount + 1).ToString() + " ";
TabPage myTabPage = new TabPage(title);
tabControl1.TabPages.Add(myTabPage);
tabControl1.SelectedTab = myTabPage;
}
private void Form1_Load(object sender, EventArgs e)
{
tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
cb_addPage.Top = tabControl1.Top;
cb_addPage.Left = tabControl1.Right - cb_addPage.Width;
foreach (TabPage tp in tabControl1.TabPages) tp.Text += " ";
}
Rectangle closeX = Rectangle.Empty;
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Size xSize = new Size(13,13);
TabPage tp = tabControl3.TabPages[e.Index];
e.DrawBackground();
using (SolidBrush brush = new SolidBrush(e.ForeColor) )
e.Graphics.DrawString(tp.Text+ " ", e.Font, brush,
e.Bounds.X + 3, e.Bounds.Y + 4 );
if (e.State == DrawItemState.Selected)
{
closeX = new Rectangle(
e.Bounds.Right - xSize.Width, e.Bounds.Top, xSize.Width, xSize.Height);
using (SolidBrush brush = new SolidBrush(Color.LightGray))
e.Graphics.DrawString("x", e.Font, brush,
e.Bounds.Right - xSize.Width, e.Bounds.Y + 4);
}
}
private void tabControl1_MouseClick(object sender, MouseEventArgs e)
{
if (closeX.Contains(e.Location))
tabControl1.TabPages.Remove(tabControl1.SelectedTab);
}
If you want to use an Image to adorn the tabs, include an ImageList with your form, load it with the Image(s) and if the close button is the 1st image, change the code like this:
if (e.State == DrawItemState.Selected)
{
closeX = new Rectangle(e.Bounds.Right - xSize.Width - 3,
e.Bounds.Top + 5, xSize.Width, xSize.Height);
e.Graphics.DrawImage(imageList1.Images[0], closeX,
new Rectangle(0,0,16,16), GraphicsUnit.Pixel );
}
I append a screenshot of the Tab with a few pages
And one using this close button image:
Update: Note that if your TabPage contains IDisposable objects you should make sure they all get disposed before you remove the page! See here for example code!

c# combobox DrawItem - refreshing issue

I created a font list in a combobox. I set it's DrawMode to OwnerDrawFixed and the method DrawItem is simple:
void cmbFonts_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
e.DrawBackground();
Font newFont =
new Font(cmbFonts.Items[e.Index].ToString(), this.DefaultFontSize);
e.Graphics.DrawString(cmbFonts.Items[e.Index].ToString(),
newFont,
new SolidBrush(Color.Black),
new Rectangle(e.Bounds.Location, e.Bounds.Size));
e.DrawFocusRectangle();
}
In gerneral, it works correctly. The problem appears on mouse scrolling. Then some items look like random graphics until they are focused. Anybody knows solution for the problem?
Always call e.DrawBackground(), regardless of the index. Fix:
void cmbFonts_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index >= 0) {
// etc...
}
}

Categories