Drawstring on a panel change when other panel move over - c#

I draw a rectangle panel with every button click. After that add a line on the edge of the rectangle and text on the center. But when I drag a panel move over other panel. The panel string will change. Please advice. I can't upload an image. How can I upload an image like that can show my problem more clearly.
This link http://i3.photobucket.com/albums/y53/ctkhai/1-4.png show the gui of my software. The upper left and lower left are picturebox. User add a "box" when click on button once and the "box" will show on upper left. User can drag the box to lower right and arrange all the box.
now the problem is when user drag the new added "box" and move over some other "box", the text i draw on previous box will change. like this http://i3.photobucket.com/albums/y53/ctkhai/2-4.png.
Update: I try to create a class for the tag. but it won't work, the number change again. It is the way I create class for the tag and read is wrong? code like below
Product _box = new Product();
List<Panel>product = new List<Panel>();
public class Product
{
public float X { set; get; } //box coordinate
public float Y { set; get; } //box coordinate
public int rotate { set; get; }
public int entryP { set; get; }
public int boxName { set; get; }
}
private void button_RecAdd_Click(object sender, EventArgs e)
{
locX = pictureBox_conveyor.Left + (pictureBox_conveyor.Width / 2 - box_y / 2);
locY = pictureBox_conveyor.Top + (pictureBox_conveyor.Height / 2 - box_x / 2);
_box.boxName = panelBoxNo;
_box.entryP = 1;
_box.rotate = 0;
_box.X = locX;
_box.Y = locY;
Panel box = new Panel();
box.Location = new Point(locX, locY);
box.Name = "box" + panelBoxNo;
box.Tag = _box;
box.Size = new Size(box_y, box_x);
box.BackColor = boxColor;
pbW = box.Width;
pbH = box.Height;
box.MouseDown += panelBox_MouseDown;
box.MouseMove += panelBox_MouseMove;
box.Paint += new PaintEventHandler((s, m) =>
{
Graphics g = m.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;]
Product b = box.Tag as Product;
string text = b.boxName.ToString();
SizeF textSize = m.Graphics.MeasureString(text, Font);
PointF locationToDraw = new PointF();
locationToDraw.X = (pbW / 2) - (textSize.Width / 2);
locationToDraw.Y = (pbH / 2) - (textSize.Height / 2);
g.DrawString(text, Font, Brushes.Black, locationToDraw);
g.DrawRectangle(new Pen(Color.Black), 0, 0, pbW - 1, pbH - 1);
g.DrawLine(drawLine, 0, 0, 0, pbH);
});
product.Add(box);
panel_pelletLayout.Controls.Add(box);
box.BringToFront();
label_boxNo.Text = panelBoxNo.ToString();
panelBoxNo++;
}
private void panelBox_MouseDown(object sender, MouseEventArgs e)
{
Panel p = sender as Panel;
if (e.Button == MouseButtons.Left)
{
xPos = e.X;
yPos = e.Y;
if (p != null)
{
activePnlBox = p.Name;
textBox_selectedName.Text = p.Name;
textBox_selectedX.Text = p.Left.ToString();
textBox_selectedY.Text = p.Top.ToString();
}
}
}
private void panelBox_MouseMove(object sender, MouseEventArgs e)
{
Panel p = sender as Panel;
if (p != null)
{
if (e.Button == MouseButtons.Left)
{
p.Left = ((e.X + p.Left - (p.Width / 2)) / gripGap) * gripGap;
p.Top = ((e.Y + p.Top - (p.Height / 2)) / gripGap) * gripGap;
textBox_selectedX.Text = p.Left.ToString();
textBox_selectedY.Text = p.Top.ToString();
}
}
}

Your Paint event handler has to be responsible for drawing everything each time. Your code looks like it only draws one object.
When you drag something over your box, the box becomes invalid and needs to be painted from scratch which means it erases everything and calls your Paint handler. Your Paint event handler then just draws one object.
I suspect that what you want to do is keep a data structure of each item you draw and then have a loop in your Paint event handler that will draw all the objects you add.

Don't use variables that you defined outside a loop, in the paint event. That might be your problem.
Try to paint ((Panel)s).Name. Does this work properly?

Your title has got all of us confused.. You don't draw Rectangles, you create new Panels on each ButtonClick, right?
The code for the Paint event is not quite right, though. Just like in any Paint event you should use the built-in Graphics object. And as Hans has noted, you should not destroy/dispose things you didn't create.
The main problem you describe seems to be that your boxes have to paint themselves without referring to their real numbers. You should store their numbers e.g. in their Tags..
(Or you could extract it from their Names, like you do it in the MouseDown!)
box[panelBoxNo].Name = "box" + panelBoxNo;
box[panelBoxNo].Tag = panelBoxNo; // < === !!
//..
box[panelBoxNo].Paint += new PaintEventHandler((s, m) =>
{
Graphics g = m.Graphics; // < === !!
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
string text = box[panelBoxNo].Tag.ToString(); // < ===
SizeF textSize = g.MeasureString(text, Font);
PointF locationToDraw = new PointF();
locationToDraw.X = (pbW / 2) - (textSize.Width / 2);
locationToDraw.Y = (pbH / 2) - (textSize.Height / 2);
g.DrawString(text, Font, Brushes.Black, locationToDraw);
g.DrawRectangle(new Pen(Color.Black), 0, 0, pbW - 1, pbH - 1);
g.DrawLine(drawLine, 0, 0, 0, pbH);
// g.Dispose(); // < === !!
// m.Graphics.DrawImageUnscaled(drawBox, new Point(0, 0)); // < === !!
// m.Dispose(); // < === !!
});
And, as I have noted you should only use arrays if you (or the code) really knows the number of elements. In your case a List<Panel> will be flexible to hold any number of elements without changing any other part of the code, except the adding. You can access the List just like an Array. (Which it is behind the scenes..)
Update: The way I see it now, your problems all are about scope in one way or another.
Scope in its most direct meaning is the part of your code where a variable is known and accessible. In a slightly broader meaning it is also about the time when it has the value your need.
Your original problem was of the latter kind: You accessed the partNo in thr Paint event of the Panels, when it had long changed to a new, probably higher value.
Your current problem is to understand the scope of the variables in the ButtonClick event.
Usually this is no problem; looking at the pairs of braces, the scope is obvious. But: Here we have a dynamically created Lambda event and this is completely out of the scope of the Click event!! Behind the scenes this Paint event is removed from the Click code, placed in a new event and replaced by a line that simply adds a delegate to the Paint handler just like any regular event.
So: nothing you declare in the ButtonClick is known in the Paint code!
To access these data you must place them in the Panel properties, in our case in the Tag and access them via casting from the Sender parameter s!
So you need to change this line in the Paint event
Product b = box.Tag as Product;
to something like this:
Product b = ( (Panel) s ).Tag as Product;

Related

Custom control using ScrollableControl with fixed position text

I'm building custom user control that will be used to display tiles map, as base class I've chosen ScrollableControl, because I want to have scrollbars in my control.
I've successfully created paint logic that is responsible for painting only needed elements.
Now I'm trying to add static text that will be always visible in same place (in my case white box with red text in top left corner):
This isn't clearly visible on above gif, but that white box blinks and jumps a bit when I scroll using mouse or scrollbars.
My question is how should I change my code to have scrollable content and fixed position content on top of that scrollable content?
Is ScrollableControl good choice as base class?
Below is my code:
class TestControl : ScrollableControl
{
private int _tileWidth = 40;
private int _tileHeight = 40;
private int _tilesX = 20;
private int _tilesY = 20;
public TestControl()
{
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
UpdateStyles();
ResizeRedraw = true;
AutoScrollMinSize = new Size(_tilesX * _tileWidth, _tilesY * _tileHeight);
Scroll += (sender, args) => { Invalidate(); };
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle);
e.Graphics.TranslateTransform(AutoScrollPosition.X, AutoScrollPosition.Y);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var offsetX = (AutoScrollPosition.X * -1) / _tileWidth;
var offsetY = (AutoScrollPosition.Y * -1) / _tileHeight;
var visibleX = Width / _tileWidth + 2;
var visibleY = Height / _tileHeight + 2;
var x = Math.Min(visibleX + offsetX, _tilesX);
var y = Math.Min(visibleY + offsetY, _tilesY);
for (var i = offsetX; i < x; i++)
{
for (var j = offsetY; j < y; j++)
{
e.Graphics.FillRectangle(Brushes.Beige, new Rectangle(i*_tileWidth, j*_tileHeight, _tileWidth, _tileHeight));
e.Graphics.DrawString(string.Format("{0}:{1}", i, j), Font, Brushes.Black, new Rectangle(i * _tileWidth, j * _tileHeight, _tileWidth, _tileHeight));
}
}
using (var p = new Pen(Color.Black))
{
for (var i = offsetX + 1; i < x; i++)
{
e.Graphics.DrawLine(p, i*_tileWidth, 0, i*_tileWidth, y*_tileHeight);
}
for (var i = offsetY + 1; i < y; i++)
{
e.Graphics.DrawLine(p, 0, i*_tileHeight, x*_tileWidth, i*_tileHeight);
}
}
e.Graphics.FillRectangle(Brushes.White, AutoScrollPosition.X * -1, AutoScrollPosition.Y * -1, 35, 14);
e.Graphics.DrawString("TEST", DefaultFont, new SolidBrush(Color.Red), AutoScrollPosition.X * -1, AutoScrollPosition.Y * -1);
}
}
EDIT:
I've searched a bit and found UserControl that has similar functionality - https://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView and after reading a bit more on control's author blog http://objectlistview.sourceforge.net/cs/blog1.html#blog-overlays I found out that he is using Transparent Form that is positioned on top of control.
I really would like to avoid that, but still have overlay on top of my control.
You are doing battle with a Windows system option named "Show window content while dragging". Always turned on by default, this web page shows how to turn it off.
Solves the problem, but it is not something you can rely on since it affects all scrollable window in all apps. Demanding that the user turns it off for you is unrealistic, users like this option so they'll just ignore you. That they did not provide an option to turn it off for a specific window was a pretty major oversight. It is an okay solution in a kiosk app.
Briefly, the way the option works is that Windows itself scrolls the window content with the ScrollWindowEx() winapi function. Using a bitblt of the window content to move pixels and only generating a paint request for the part of the window that was revealed by the scroll. Usually only a few lines of pixels, so completes very fast. Problem is, that bitblt moves your fixed pixels as well. The repaint moves them back. Pretty noticeable, the human eye is very sensitive to motion like that, helped avoid being lion lunch for the past million years.
You'll have to take the sting out of ScrollWindowsEx(), preventing it from moving pixels even though you can't stop it from being called. That takes a heavy sledgehammer, LockWindowUpdate(). You'll find code in this post.
using System.Runtime.InteropServices;
...
protected override void OnScroll(ScrollEventArgs e) {
if (e.Type == ScrollEventType.First) {
LockWindowUpdate(this.Handle);
}
else {
LockWindowUpdate(IntPtr.Zero);
this.Update();
if (e.Type != ScrollEventType.Last) LockWindowUpdate(this.Handle);
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool LockWindowUpdate(IntPtr hWnd);
Not that pretty, using a separate Label control ought to start sounding attractive.
can you just add a label to that control(on top), in other words - cant you use it as panel?

How to call a custom control onpaint event

I want to call OnPaint event in my custom control. I have created a class and inherited control to create custom control and want to call OnPaint or paint event when the object is initialized. But when i create a class paint event is not triggered. Se the below code.
internal class CallRectangle : Control
{
public CallRectangle()
{
this.Paint += CalloutRectangle_Paint;
}
void CalloutRectangle_Paint(object sender, PaintEventArgs e)
{
throw new NotImplementedException();
}
protected override void OnPaint(PaintEventArgs e)
{
}
}
// Create object to the custom control and call paint event using constructor
CallRectangle obj = new CallRectangle();
this.Controls.Add(obj);
Any one let me know how to call paint event.
Thanks,
Bharathi.
Calling Invalidate, Update or Refresh will not work for you.
Here is a downloadable Sample from Microsoft with a Step-By-Step Guide on how to Inherit Directly from Control and build the Draw Events, since inheriting from a existing control (Button let's say) is quite easy as it already draws it self.
What you'll see in the above example that you'll need to create multiple drawing events based on the complexity of your Control (like a List box that have items that need to be drawn). Sample code from above link:
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaint"]/*'/>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// draw on memory bitmap
CreateMemoryBitmap();
// TODO: Figure out how to avoid doing this on every paint
// calculate fields required to layout and draw list
RecalcItems(e.Graphics);
Graphics g = Graphics.FromImage(m_bmp);
// Draw the background and raise the PaintBackground event
OnPaintBackground(new ListPaintEventArgs(g));
// draw list
if (m_list != null)
DrawItems(g);
// Draw the frame around the list
Rectangle rc = new Rectangle(0, 0, this.Width - m_scrollBarWidth, this.Height - 1);
g.DrawRectangle(new Pen(Color.Black), rc);
// blit memory bitmap to screen
e.Graphics.DrawImage(m_bmp, 0, 0);
}
// This prevents the base class from doing OnPaintBackground.
// Since the OnPaint method paints the entire window there's no reason
// to let this go through. If we do it'll cause flashing.
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaintBackground"]/*'/>
protected override void OnPaintBackground(PaintEventArgs e)
{
}
// Called when it is time to draw the background. To take complete control of background
// drawing override this. To get called after the background is drawn by the base class
// register for the PaintBackground event.
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaintBackgroundII"]/*'/>
protected virtual void OnPaintBackground(ListPaintEventArgs e)
{
// Fill the background with the background colour
e.Graphics.Clear(this.BackColor);
if (PaintBackground != null)
PaintBackground(this, e);
}
// Called to draw a line item. To take complete control of drawing an item override this method.
// To let the base class draw the item first and then do your own additional work register for the
// PaintItem event.
/// <include file='DocumentationComments.xml' path='doc/members/member[#name="M:TwoLineListBox.OnPaintItem"]/*'/>
protected virtual void OnPaintItem(ListPaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle destRect = new Rectangle(); // Destination for the item image, if any
Rectangle srcRect = new Rectangle(); // Source region of the image to draw
Rectangle textRect = new Rectangle(); // Destination for the text
int lineIndent = 0; // How far the text is moved in from the left margin before drawing
Image imageToDraw = null;
string line1Text;
string line2Text;
// On the null case skip everything and just draw the separator line
if (e.Item == null)
goto DrawSeparator;
line1Text = GetStringProperty(e.Item, this.m_line1Property);
if (line1Text == null)
line1Text = e.Item.ToString();
line2Text = GetStringProperty(e.Item, this.m_line2Property);
if (line2Text == null)
line2Text = e.Item.ToString();
// Figure out if we're drawing an image per item from the object, or one for
// everything
imageToDraw = GetImageProperty(e.Item, this.m_itemImageProperty);
if (imageToDraw == null)
imageToDraw = m_itemImage;
// Calculate the position of the item image, if we have one, and the line indents
if (imageToDraw != null)
{
srcRect.X = 0;
srcRect.Y = 0;
srcRect.Width = imageToDraw.Width;
srcRect.Height = imageToDraw.Height;
// int vertPos = (m_itemHeight - m_itemImage.Height) / 2;
destRect.X = e.ClipRectangle.X + IMAGE_PADDING_X;
destRect.Y = e.ClipRectangle.Y + IMAGE_PADDING_Y;
// destRect.Y = (vertPos < 0) ? 0 : vertPos; // Center the image vertically in the line height. Handle the image being larger than the item height
// Account for an image that is taller than the item height
destRect.Height = (imageToDraw.Height > m_itemHeight - IMAGE_PADDING_Y) ? m_itemHeight - (IMAGE_PADDING_Y * 2) : imageToDraw.Height;
destRect.Width = destRect.Height;
// Set the text indent based on the image
lineIndent = IMAGE_PADDING_X + imageToDraw.Width + TEXT_PADDING_X; // Two pixels for the left indent of the image
}
else
{
// Set the text indent without using the image
lineIndent = TEXT_PADDING_X;
}
// Calculate the text rectangle
textRect.X = e.ClipRectangle.X + lineIndent;
textRect.Width = e.ClipRectangle.Width - TEXT_PADDING_X - textRect.X; // Allows for padding on the right edge too
textRect.Y = e.ClipRectangle.Y + 2;
textRect.Height = this.m_textHeightLine1;
// From here on we actually draw things. First the selected background, if necessary
if (e.Selected)
g.FillRectangle(m_brushSelBack, e.ClipRectangle);
// Draw the icon, if we have one
if (imageToDraw != null)
{
if (m_useTransparent)
g.DrawImage(imageToDraw, destRect, srcRect.Y, srcRect.Y, srcRect.Height, srcRect.Height,
GraphicsUnit.Pixel, m_imageAttributes);
else
g.DrawImage(imageToDraw, destRect, srcRect, GraphicsUnit.Pixel);
}
// Draw the text in a bounding rect to force it to truncate if too long
g.DrawString(line1Text, m_fontLine1, e.Selected ? m_brushSelText : m_brushText, textRect);
// Draw the second line
textRect.Y += m_textHeightLine1 + 3;
textRect.Height = this.m_textHeightLine2;
g.DrawString(line2Text, m_fontLine2, e.Selected ? m_brushSelText : m_brushText, textRect);
DrawSeparator:
// Draw the separator line
g.DrawLine(m_penSep, e.ClipRectangle.X, e.ClipRectangle.Y + e.ClipRectangle.Height,
e.ClipRectangle.X + e.ClipRectangle.Width, e.ClipRectangle.Y + e.ClipRectangle.Height);
// Let other people know it's time for them to draw
if (PaintItem != null)
PaintItem(this, e);
}
// Draw all the items.
private void DrawItems(Graphics g)
{
ListPaintEventArgs ListPaintEventArgs = new ListPaintEventArgs(g);
// Calculate our actual drawing area, accounting for the scroll bar
Rectangle rc = new Rectangle(0, 0, this.Width - m_scrollBarWidth, this.Height - 1);
// draw items that are visible
int curItem = 0;
for (int i = 0; (i < m_visibleCount); i++)
{
curItem = i + m_topItem;
if (curItem < m_list.Count)
{
// Calculate the drawing area for the item
ListPaintEventArgs.ClipRectangle = new Rectangle(rc.X,
rc.Y + (i * m_itemHeight),
rc.Width,
this.m_itemHeight);
// Get the item we'll be drawing with and whether it is selected
ListPaintEventArgs.Item = m_list[curItem];
ListPaintEventArgs.Selected = (m_selItem == curItem);
// Draw the item
OnPaintItem(ListPaintEventArgs);
}
}
}
// Recalculates the heights and visible counts for assorted items
// in the listbox.
// TODO: Get rid of this method by moving the rest of the items into the assorted
// properties.
private void RecalcItems(Graphics g)
{
// The text heights for a single line of text is the height of the font.
m_textHeightLine1 = g.MeasureString("W", this.m_fontLine1).ToSize().Height;
m_textHeightLine2 = g.MeasureString("W", this.m_fontLine2).ToSize().Height;
// The height for an individual item is two lines plus some padding
m_itemHeight = m_textHeightLine1 + m_textHeightLine2 + 5;
m_visibleCount = this.Height / m_itemHeight;
// Set the top item to draw to the current scroll position
m_topItem = m_scrollValue;
}
// Creates all the objects we need for drawing
private void CreateGdiObjects()
{
m_brushText = new SolidBrush(this.ForeColor);
m_brushSelText = new SolidBrush(this.m_selForeColor);
m_brushSelBack = new SolidBrush(this.m_selBackColor);
m_penSep = new Pen(this.m_separatorColor);
m_imageAttributes = new ImageAttributes();
}
// Creates a bitmap in memory to do our drawing. We'll draw on this first
// and then splat it to the screen.
private void CreateMemoryBitmap()
{
// Only create if don't have one and the size hasn't changed
if (m_bmp == null || m_bmp.Width != this.Width || m_bmp.Height != this.Height)
{
m_bmp = new Bitmap(this.Width - m_scrollBarWidth, this.Height);
// TODO: Figure out why this is here.
m_scrollBar.Left = this.Width - m_scrollBarWidth;
m_scrollBar.Top = 0;
m_scrollBar.Width = m_scrollBarWidth;
m_scrollBar.Height = this.Height;
}
}
I Hope this helps

c# move picturebox from an array of picturebox up when you hover mouse

I'm working on C# windows form. I have an array of picturebox, displayed on the form. The array has the size of 13, and they're all side by side. How can I make it so that when I click on a picturebox, it is moved up by let's say +20 on y.
My code to make the picture boxes. The pb1 and p1 are declared above
void print_Deck(int x, int y, double[] a){
double n;
for (int i = 0; i < 13; i++)
{
pb1[i] = new PictureBox();
// pb1[1].Image = Properties.Resources.img1;
pb1[i].Visible = true;
pb1[i].Location = new Point(0, 0);
this.Size = new Size(800, 600);
pb1[i].Size = new Size(46, 65);
pb1[i].SizeMode = PictureBoxSizeMode.StretchImage;
pb1[i].Location = new Point(x, y);
n= a[i];
im = face(n);
pb1[i].Image = im;
this.Controls.Add(pb1[i]);
x = x + 20;
}
}
You can try adding Click event on your Picturebox then you can try this code on the Click function.
You can manipulate the location by using Top propery.
Picturebox.Top -= 20; // move the picture box upward
or
Picturebox.Top += 20; // move the picture box downward
or use the .Location = New Point(X,Y)
Picturebox.Location = new Point(Picturebox.Location.X, Picturebox.Location.Y + 20);
Here's how you add the EventHandler to your picturebox.
Picturebox.Click += new System.EventHandler(this.Picturebox_ClickFunction);
then create a fucntion with the name Picturebox_ClickFunction
private void Picturebox_ClickFunction(object sender, EventArgs e)
{
PictureBox pb1 = (PictureBox)sender; // you need to cast(convert) the sende to a picturebox object so you can access the picturebox properties
}
then you can use the code I provided above.
You can try PictureBox.Top property with Anchor property
or use PictureBox.Location.
You could register the PictureBox's 'Click' event to adjust the 'Margin' property by the required amount

Drawing lines on a panel

I try to make a graphics.
When I click on my label, I want to draw a line. It works, it draw my line but at the last point there is another line going at the left top corner.. I don't know why.
(It's useless, but it's for another project, I try to understand how works the drawing)
Here's my code :
public partial class Form1 : Form
{
Pen myPen = new Pen(Color.Blue);
Graphics g = null;
int start_x = 0, start_y;
Point[] points = new Point[1000];
int test = 0;
public Form1()
{
InitializeComponent();
start_y = canvas.Height / 2;
points[0] = new Point (start_x,start_y);
myPen.Width = 1;
}
private void drawLine()
{
g.DrawLines(myPen, points);
}
private void incrementation_Click(object sender, EventArgs e)
{
test = test + 1;
incrementation.Text = test.ToString();
if(test == 1)
{
points[1] = new Point(100, start_y);
}
if (test == 2)
{
points[test] = new Point(200, 90),new Point(220, 10);
}
if (test == 3)
{
points[test] = new Point(220, 10);
drawLine();
}
}
private void canvas_Paint(object sender, PaintEventArgs e)
{
g = canvas.CreateGraphics();
}
}
A couple of issues.
You don't assign any values to points after points[3].
Point is a structure and will have a value of [0,0] at all further elements
so your lines go there.. (all 996 of them ;-)
There is more you should change:
Do the drawing in the Paint event or trigger it from there.
Do not store the Paint e.Grahpics object. You can pass it out to use it, but don't try to hold on to it.
After adding or changing the points, write canvas.Invalidate() to trigger the Paint event. This will make your drawing persistent.
To learn about persistent drawing minimize & restore the form!
Also you should use a List<Point> instead of an array. This lets you add Points without having to decide on the number of Points you want to support..
To create a new Point you write something like this:
points.Add(new Point(100, start_y) );
To draw you then use this format in the Paint event::
e.Graphics.DrawLines(myPen, points.toArray());
In the constructor you're filling first point as
points[0] = new Point (start_x,start_y);
At this moment, start_x = 0 (since you're not assigned anything else to it after declaration int start_x = 0).
Then in incrementation_Click you're assigning points[1], points[2] and points[3], but you don't changing anywhere in your code points[0].
So when you calling g.DrawLines - first point will always be (0, canvas.Height / 2)
Aside from this:
You don't need to create graphics explicitly in _Paint event handler since it can accessed as e.Graphics.
It's better to move all paintings into canvas_Paint like:
private void canvas_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLines(myPen, points);
}
and in your _Click handler instead of calling drawLine you should only call canvas.Refresh()

C# combobox with lines

This is a different approach to what I was doing earlier.
In my combo owner drawn combo box draw 3 lines(solid,dash,dashdot) to be drawn with the color selected from an earlier colpr picker drop down
this.DrawMode = DrawMode.OwnerDrawVariable;
this.DropDownStyle = ComboBoxStyle.DropDownList;
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
int startX = e.Bounds.Left + 5;
int startY = (e.Bounds.Y);
Point p1=new Point(startX,startY);
int endX = e.Bounds.Right - 5;
int endY = (e.Bounds.Y);
ComboBoxItem item = (ComboBoxItem)this.Items[e.Index];
Point p2=new Point(endX,endY);
base.OnDrawItem(e);
Pen SolidmyPen = new Pen(item.foreColor, 1);
Pen DashedPen = new Pen(item.foreColor, 1);
DashedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
Pen DashDot = new Pen(item.foreColor, 1);
DashDot.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
// Pen DashedPen = new Pen(item.foreColor, (Int32)this.Items[e.Index]);
Bitmap myBitmap = new Bitmap(item.Picture);
Graphics graphicsObj;
graphicsObj = Graphics.FromImage(myBitmap);
switch (e.Index)
{
case 0:
graphicsObj.DrawLine(SolidmyPen, p1, p2);
break;
case 1:
graphicsObj.DrawLine(DashedPen, p1, p2);
break;
case 2:
graphicsObj.DrawLine(DashDot, p1, p2);
break;
}
Here's what I'm trying to do. Draw 3lines(solid,dash,dashdot) in the combo box.
I don't see any lines in the combo box except some blue which is the selected color
Thank u
Try this.
I started a new winforms application. Created a class based off of ComboBox added your code and modified it a bit. I think you major problem was in your bitmap part. You create a new bitmap, then draw on that, but you never use the bitmap you created. If you wish to keep the code you created you would have to add to the end of the method item.Picture=myBitmap. But I think that would call the ondrawitem again and you would be in an infinite loop. Instead of creating a graphics object based off the item.Picture, just use the graphics object created for you in the DrawItemEventArgs.
e.Graphics
here is what I did, I think I cleaned it up a bit. And you may already know but you should always wrap pens, brushes and graphics in using {....} like I demonstrated below.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
public class MyComboBox : ComboBox
{
public MyComboBox()
{
this.DrawMode = DrawMode.OwnerDrawVariable;
this.DropDownStyle = ComboBoxStyle.DropDownList;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
//I removed you int startX... endy... stuff, unless you want to keep it for readability there is no need
Point p1 = new Point(e.Bounds.Left + 5, e.Bounds.Y + 5);
Point p2 = new Point(e.Bounds.Right - 5, e.Bounds.Y + 5);
//I am not sure why you would want to call the base.OnDrawItem, feel free to uncomment it if you wish though
//base.OnDrawItem(e);
switch (e.Index)
{
case 0:
using ( Pen SolidmyPen = new Pen(e.ForeColor, 1))
e.Graphics.DrawLine(SolidmyPen, p1, p2);
break;
case 1:
using (Pen DashedPen = new Pen(e.ForeColor, 1))
{
DashedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
e.Graphics.DrawLine(DashedPen, p1, p2);
}
break;
case 2:
using (Pen DashDot = new Pen(e.ForeColor, 1))
{
DashDot.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
e.Graphics.DrawLine(DashDot, p1, p2);
}
break;
}
}
}
I use a similar method to draw a combobox. But GDI+ keeps throwing an exception. This works fine on Windows XP, but not on Windows 7.
So I had to fix it with a hack.
I added a timer to fire off an event, 100 ms after the form was initially shown. This event shows the first item in the combo box list.
private void timer1_Tick(object sender, EventArgs e)
{
// Use a short 100 ms delay before showing the default items
// in the dropdown lists
predefinedComboBox.SelectedIndex = 0;
// Disable the timer
timer1.Enabled = false;
}
The Layout event was too early. The control was not ready yet to be drawn. So an exception was thrown. I am not sure what other events can be used to achieve the desired effect.

Categories