C# combobox with lines - c#

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.

Related

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()

Drawstring on a panel change when other panel move over

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;

Painting a TextBox

I'm in need of a way to make TextBox appear like a parallelogram but i can't figure out how to do so. I currently have this code:
private void IOBox_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point cursor = PointToClient(Cursor.Position);
Point[] points = { cursor, new Point(cursor.X + 50, cursor.Y), new Point(cursor.X + 30, cursor.Y - 20),
new Point(cursor.X - 20, cursor.Y - 20) };
Pen pen = new Pen(SystemColors.MenuHighlight, 2);
g.DrawLines(pen, points);
}
But apparently it's not working. Either i misplaced/misused it or i'm not doing something right.
This is the method that i use to add it.
int IOCounter = 0;
private void inputOutput_Click(object sender, EventArgs e)
{
IOBox box = new IOBox();
box.Name = "IOBox" + IOCounter;
IOCounter++;
box.Location = PointToClient(Cursor.Position);
this.Controls.Add(box);
}
Any idea how i can fix it? IOBox is a UserControl made by me which contains a TextBox. Is that rightful to do?
If its possible, you should make your application using WPF. WPF is designed to do exactly what you are trying to do.
However, it can be done in WinForms, though not easily. You will need to make a new class that inherits the TextBox WinForm control. Here is an example that makes a TextBox look like a circle:
public class MyTextBox : TextBox
{
public MyTextBox() : base()
{
SetStyle(ControlStyles.UserPaint, true);
Multiline = true;
Width = 130;
Height = 119;
}
public override sealed bool Multiline
{
get { return base.Multiline; }
set { base.Multiline = value; }
}
protected override void OnPaintBackground(PaintEventArgs e)
{
var buttonPath = new System.Drawing.Drawing2D.GraphicsPath();
var newRectangle = ClientRectangle;
newRectangle.Inflate(-10, -10);
e.Graphics.DrawEllipse(System.Drawing.Pens.Black, newRectangle);
newRectangle.Inflate(1, 1);
buttonPath.AddEllipse(newRectangle);
Region = new System.Drawing.Region(buttonPath);
base.OnPaintBackground(e);
}
}
Keep in mind that you will still have to do other things, such as clipping the text, etc. But this should get you started.

C# - Custom Interface from Image

I'm creating a WinForm program in C# and I want to customize its skin or interface using PNG or other image files. I'm not only concerned at the buttons but also its background, progress bar, and other few controls. I realize from most of my research that TransparentKey is used for an irregular shape form then panel supports PNG transparency.
My question: I was wondering if there's a better way, the actual and recommended way, to implement the custom interface for my program.
My current code uses the BackgroundImage property to replace the image. Yet I do believe this is not the way I should be implementing this.
Here's a piece my current code:
public List<Image> BaseImage = new List<Image>();
public List<Image> ClickedImage = new List<Image>();
public List<object> ControlAction = new List<object>();
public List<Image> HoverImage = new List<Image>();
private int counter = 0;
public Control CreatePanel(Image BaseImage, Image HoverImage, Image ClickedImage,
string Name, int x, int y, ButtonAction action)
{
this.BaseImage.Add(BaseImage);
this.HoverImage.Add(HoverImage);
this.ClickedImage.Add(ClickedImage);
int width = BaseImage.Width;
int height = BaseImage.Height;
Actions Action = new Actions();
EventHandler SelectedAction = null;
switch (action)
{
case ButtonAction.Cancel:
SelectedAction = Action.Cancel;
break;
case ButtonAction.Exit:
SelectedAction = Action.Exit;
break;
case ButtonAction.Minimize:
SelectedAction = Action.Minimize;
break;
case ButtonAction.Open:
SelectedAction = Action.Open;
break;
case ButtonAction.Pause:
SelectedAction = Action.Pause;
break;
case ButtonAction.Start:
SelectedAction = Action.Start;
break;
}
Simplify.Invoke(() =>
{
this.newPanel.SuspendLayout();
this.newPanel.Location = new System.Drawing.Point(x, y);
this.newPanel.Name = Name;
this.newPanel.TabIndex = counter;
this.newPanel.Size = new System.Drawing.Size(width, height);
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = this.BaseImage[counter];
this.newPanel.Click += new EventHandler(SelectedAction);
this.newPanel.MouseEnter += new EventHandler(PanelHover);
this.newPanel.MouseDown += new MouseEventHandler(PanelClicked);
this.newPanel.MouseLeave += new EventHandler(PanelLeave);
this.newPanel.ResumeLayout(false);
});
ControlAction.Add(action);
counter += 1;
return newPanel;
}
private void PanelClicked(object sender, MouseEventArgs e)
{
this.newPanel.SuspendLayout();
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = ClickedImage[newPanel.TabIndex];
this.newPanel.Size = new Size(ClickedImage[newPanel.TabIndex].Width, ClickedImage[newPanel.TabIndex].Height);
this.newPanel.ResumeLayout(false);
}
private void PanelHover(object sender, EventArgs e)
{
this.newPanel.SuspendLayout();
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = HoverImage[newPanel.TabIndex];
this.newPanel.Size = new Size(HoverImage[newPanel.TabIndex].Width, HoverImage[newPanel.TabIndex].Height);
this.newPanel.ResumeLayout(false);
}
private void PanelLeave(object sender, EventArgs e)
{
this.newPanel.SuspendLayout();
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = BaseImage[newPanel.TabIndex];
this.newPanel.Size = new Size(BaseImage[newPanel.TabIndex].Width, BaseImage[newPanel.TabIndex].Height);
this.newPanel.ResumeLayout(false);
}
My code above creates a Panel which functions as a button. Its action is taken from a class called Actions which basically just assign the method to be assigned when the user clicks it. I change the control state by replacing its BackgroundImage, at the same time changing its size to avoid getting cropped area.
Thanks in advance.
Additional Information
- I'm using .NET Framework 2.0
My question: I was wondering if there's a better way, the actual and
recommended way, to implement the custom interface for my program.
I would recommend using WPF (Windows presentation foundation). It offers highly customizable controls and also supports hardware accelerated graphics.
With winforms, you will get flickering screen, issues with resize and redraw which you will not face with wpf.

Button on drawn object

I'm trying to add a button so that it will appear on top of a screen that I'm drawing, in an XNA/Silverlight Windows phone game.
Currently the map is drawing over it, so the button appears invisible. Does anyone know how I could fix this?
The Button switchScreen is created, but not initialized, earlier in the code.
Here is the code where I'm adding the button:
private void OnDraw(object sender, GameTimerEventArgs e)
{
#region CommonStuff
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
#endregion CommonStuff
if (is3D)
{
OnDraw3D(sender, e);
}
else
{
OnDraw2D(sender, e);
}
switchScreen = new Button();
switchScreen.Height = 20.0;
switchScreen.Width = 100.0;
switchScreen.Content = "Switch to Shooting Screen";
switchScreen.Margin = new Thickness(phoneScreen.Height - switchScreen.Width -
20.0, 20.0, 20.0, phoneScreen.Width - switchScreen.Height - 20.0);
switchScreen.Visibility = System.Windows.Visibility.Visible;
}
I'm only testing the OnDraw2D so here's the code for that:
private void OnDraw2D(object sender, GameTimerEventArgs e)
{
spriteBatch.Begin();
// TODO: Add your drawing code here
map.Draw(e.ElapsedTime, e.TotalTime);
// npc.Draw(gameTime);
}
and the map.Draw is here
public override void Draw(TimeSpan elapsedTime, TimeSpan totalTime)
{
// Draw the Sky
gamePage.getSpriteBatch().Draw(background, Position, Color.White);
foreach (Person person in people)
{
person.Draw(elapsedTime, totalTime);
}
base.Draw(elapsedTime, totalTime);
}
background is a Texture.2D and Position is a Vector2.
I think, since you're creating the button manually, you may need to use this code instead (I am assuming that you're using the Windows Forms Button control):
switchScreen.Location = new System.Drawing.Point(xLocation, yLocation);
switchScreen.Name = name;
switchScreen.Size = new System.Drawing.Size(xSize, ySize);
switchScreen.TabIndex = 0;
switchScreen.Text = text;
switchScreen.UseVisualStyleBackColor = true;
Controls.Add(switchScreen);
How are you implementing a Windows Form? As far as I know, you can't add a Button without an underlying form for it to be on.

Categories