I am a novice in C# (following night school) and am experimenting with some stuff (haven't seen it yet in class).
I am trying to build a palletizing tool, which displays the optimal way to pallatize boxes on a pallet.
Before coding the alorithm to do so I was looking into how I could display the pallet.
My eye caught the Graphics.Drawline method in combination with an override of the OnPaint methode and I have no trouble drawing the line, but it is always to large!
For testing I set my form to 500*500 and I want a line of 500pxl. I would asume if I resize the window that I would immediatly see the end of the line, but it still goes on for about 5-7pxl.
Is there a difference in metrics or something? Below an example of the code I use.
Thanks for any help, couldn't find any related topics on the web that would explain this (perhaps it's just that obvious...).
protected override void OnPaint(PaintEventArgs paintEvnt)
{
// Get the graphics object
Graphics gfx = paintEvnt.Graphics;
// Create a new pen that we shall use for drawing the line
Pen myPen = new Pen(Color.Black);
// draw pallet
//lenght1
gfx.DrawLine(myPen, 0, 50, 500, 50);
//width1
gfx.DrawLine(myPen, 500, 50, 500, 100);
}
This is most likely being caused by the window you're drawing this in. If you look around the edges of a windows form popup (assuming you're using a windows forms application) you'll see a border.
This border is included in the width of the window. That's why the line seems to go on for a little bit past the edge of your window - you can't see the actual end of the line until you stretch the window and move its border.
Related
I have been searching for about 12 hours now trying to find a way to draw dots on a PictureBox, I've found many threads giving example code and yet I just can't seem to get done what I want.
In essance what I am trying to do is this:
I have a windows form with a PictureBox on it, I do not have any Image in the PictureBox, however I do have the BackColor set to Black. I am trying to create a new bitmap image then run code to create white dots in the following style:
..........
..........
..........
..........
Thus giving me a grid style Look on the PictureBox. However at every attempt I have failed, so if anyone could help me understand how to work with this I would appreciate it.
My most recent attempt was to use the ControlPaint.DrawGrid Method, like so:
private void picBox_Display_Paint(object sender, PaintEventArgs e)
{
Size size = new Size(35, 35);
Rectangle rect = new Rectangle(0,0,picBox_Display.Width, picBox_Display.Height);
ControlPaint.DrawGrid(Graphics.FromHwnd(picBox_Display.Handle), rect, size, Color.White);
}
The above code is in the PictureBox Paint event method. I know it runs through the code because I have a breakpoint at the end of the method, but nothing happens. I'm not sure I understand how the ControlPaint.DrawGrid works am I supposed to be adding something else?
I tried using the Bitmap.SetPixel method earlier today but kept having issues with it and kept looking for other ways to try to get it done.
Any help would be appreciated. Thanks!
You need to use e.Graphics for this. Note also that debugging this sort of code can be difficult because debugging often invalidates the drawing so it needs to be drawn again. The last parameter is meant to be the background color against what you are painting, so it looks like it draws the opposite of what you specify. If you background is black you need to pass in Color.Black
ControlPaint.DrawGrid(e.Graphics, rect, size, Color.Black);
I am trying to write a small interactive game-like application, where I need to have a Draw method that's gonna draw on screen, but can't figure out how to structure the method for WPF.
If this was Winforms, I could use:
public void Draw (Graphics g)
{
}
But for a WPF Window, what should I have on it in the xaml (currently only have a Grid), and what should this Draw method receive as an argument?
First I want to do it like this to get it working, then I can think about how to make it more WPF, etc. But now I am more interested in getting this to work.
Typically, you "draw" in WPF in a completely different manner.
In Windows Forms/GDI, the graphics API is an immediate mode graphics API. Each time the window is refreshed/invalidated, you explicitly draw the contents using Graphics.
In WPF, however, things work differently. You rarely ever directly draw - instead, it's a retained mode graphics API. You tell WPF where you want the objects, and it takes care of the drawing for you.
The best way to think of it is, in Windows Forms, you'd say "Draw a line from X1 to Y1. Then draw a line from X2 to Y2. Then ...". And you repeat this every time you need to "redraw" since the screen is invalidated.
In WPF, instead, you say "I want a line from X1 to Y1. I want a line from X2 to Y2." WPF then decides when and how to draw it for you.
This is done by placing the shapes on a Canvas, and then letting WPF do all of the hard work.
When there are just too many objects to be drawn very quickly (huge Visual Tree) another option would be to use a WriteableBitmap. Just use the Pixels property to set the pixels and/or use the Render method to draw UIElements.
I preffer to use OnRender method like in this example:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawRectangle(null, new Pen(Brushes.Black, 2), new Rect(0, 0, ActualWidth, Height));
}
To Implement a Draw loop type behavior in WPF you can use the CompositionTarget.Rendering event. This is raised once per frame when the WPF drawing system is painting frames.
As others have pointed out this is not very WPF friendly but it will work and can be used to get more immediate drawing behavior out of a WPF app.
In most cases you would use a single root canvas and update say the Canvas position of an element on the CompositionTarget.Rendering event.
For example to make a ellipse fly all over the screen do this:
In your XAML (For a Window that is 640 by 480 in size):
<Canvas x:Name="theCanvas">
<Ellipse x:Name="theEllipse" Height="10" Width="10" Fill="Black" />
</Canvas>
In your Code behind for the Window that the above XAML is in (Make sure to add a reference to System.Windows.Media in order to see the CompsitionTarget object :
public static Random rand = new Random();
public View()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, System.EventArgs e)
{
double newLeft = rand.Next(0, 640);
double newTop = rand.Next(0, 480);
theEllipse.SetValue(Canvas.LeftProperty,newLeft);
theEllipse.SetValue(Canvas.TopProperty, newTop);
}
Yow should add a Canvas (or change the Grid for a Canvas) and then draw over it. Here is Microsoft tut on drawing over a canvas
Also, I don't know how related is this other question to yours, but you might want to check it out.
I have drawn a circle in windows form
Pen pen = new Pen(Color.Black, 3);
Graphics gr = this.CreateGraphics();
gr.DrawEllipse(pen, 5,5,20,20);
How to delete it...
You have to clear your Graphic:
Graphics.Clear();
But all drawn figures will be cleared. Simply, you will then need to redraw all figures except that circle.
Also, you can use the Invalidate method:
Control.Invalidate()
It indicates a region to be redrawn inside your Graphics. But if you have intersecting figures you will have to redraw the figures you want visible inside the region except the circle.
This can become messy, you may want to check out how to design a control graph or use any graph layout library.
You can invalidate the draw region you want to refresh for example:
this.Invalidate();
on the form...
Assuming you're subscribing to the Paint event or overriding the protected OnPaint routine, then you will need to perform something like this:
bool paint = false;
protected override void OnPaint(object sender, PaintEventArgs e)
{
if (paint)
{
// Draw circle.
}
}
Then when you want to stop painting a circle:
paint = false;
this.Invalidate(); // Forces a redraw
You can make a figure of same dimensions using the backColor of your control in which you are drawing
use after your code to clear your figure.
Pen p = new Pen(this.BackColor);
gr.DrawEllipse(p, 5,5,20,20);
In fact, you can delete your circle and nothing but your circle.
Everything you need is something like a screenshot of the "before state" of the area you want to clear, to make a TextureBrush from it. You can achieve that step by something like this:
Bitmap _Background = new Bitmap(this.Width, this.Height);
Graphics.FromImage(_Background).CopyFromScreen(this.Left, this.Top, 0, 0, this.Size);
The first line will give you a bitmap in your windows forms size. The second line will save a screenshot of it in the _Background-bitmap.
Now you create a TextureBrush out of it:
Brush brsBackground = new TextureBrush(_Background);
The next thing you need are the dimensions of your circle, so you should save them into a variable, if they are not a fix value. When you got them at hand, you can clear the specific area like this:
Graphics gr = this.CreateGraphics();
gr.FillEllipse(brsBackground, 5, 5, 20, 20); // values referred to your example
Done!
Even complex figures are able to be deleted by this, like a GraphicsPath for example:
GraphicsPath gp = new GraphicsPath(); // any kind of GraphicsPath
gr.FillRegion(brsBackground, new Region(gp));
You don't "delete" it per se, there's nothing to delete. It's a drawing, you draw something else over it or you can call the Graphics.Clear() method.
If u are using Invalidate() and is not working, make a panel.Refresh().
That will work on you.
just make another control with the attributes etc. that you want, make the visibility to false and set the region of the control to the other control like this:
pen.Region = pen2.Region;
It is very simple to delete a drawn circle from c.
There is only four steps:-
Open turbo app
go to the command where you had drawn the circle
drag the command
click on delete button
I'm having trouble with a gradient drawing call. I have a Form that looks like this.
Screenshot http://img413.imageshack.us/img413/3570/30364682.png
The problem is every now and again the above gradient drawing bug will start happening. It should go right across of course. Sometimes it only takes some build-rebuild-mashing to fix and it'll simply just "start" after a build every now and again.
That control (the top white part) is a TableLayoutPanel. The BackColor is set to white and on the panel's Paint event I do this:
/// <summary>
/// Draws the background gradient.
/// </summary>
private void titleBarLayoutPanel_Paint(object sender, PaintEventArgs e)
{
Brush brush = new LinearGradientBrush(titleBarLayoutPanel.Bounds, TaskHeaderLeftColor, TaskHeaderRightColor, LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brush, titleBarLayoutPanel.Bounds);
}
Should I be doing something else? The problem is that it works, and then without so much as a rebuild or build this will start happening!
EDIT I have since rebuilt the class library it is contained in (it's a generic Form) then rebuilt the app it's used in and the gradient is now filling across completely. This is bizarre.
Building and re-building your application, with no changes, normally doesn't solve this (or most any other bug for that matter) save the ones in which you run your application without doing a clean/rebuild first and then notice that the code you just wrote doesn't run (not sure that's possible these days with the IDEs). I see this a lot with newer devs when they keep rebuilding hoping that somehow the compiler will make the code "correct" or that maybe the compiler is simply not generating the correct code to begin with. (Please note that I do not mean the aforementioned statements to be taken disparagingly.)
To solve the issue at hand, you might try deriving your own TableLayoutPanel class in which you override the OnBackgroundPaint event, painting your own background, or simply returning if you don't want to paint your own background. (You seem to be painting the background in the Paint event). What you are doing in the code above is simply painting over the background already painted by the control, hence the "bug" you see, (double paint). It appears that the form is not resizable. Try making it resizable. Then resize it and watch it paint, or simply move other windows over it.
class CustomTableLayoutPanel : TableLayoutPanel
{
protected override void OnPaintBackground(PaintEventArgs e)
{
Brush brush = new LinearGradientBrush(this.ClientRectangle, TaskHeaderLeftColor, TaskHeaderRightColor, LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brush, this.ClientRectangle);
//base.OnPaintBackground(e);
}
}
By the way, you should replace Bounds with ClientRectangle.
Bounds is the control's rectangle relative to its parent; ClientRectangle is relative to the control itself.
In this particular case, it won't make a difference, since the control is at 0, 0.
This is the high level problem I'm trying to solve...
I have a 3rd party plotting/graphing (IoComp Plot), and I want to embed a high quality (at least 600 dpi) bitmap of the Plot control in reports created by another 3rd party report package (Combit List & Label).
This is the approach that seems most promising so far...
---Edit---:
After trying many other approachs, the only one that I think I can make work is to create a hidden instance of the Plot control, with everything scaled up to printer size (approx 5 times screen size). That includes width and height, font sizes, line widths - every visible component of the control. Yuck!
------
I can get a Graphics object of the proper resolution from the plot control's PrintPage event, but converting it to a Bitmap so the report package will be happy is proving to be the major stumbling block. Several hours of searching has led to other people who asked the same question, but no viable answers.
The only promising lead I've found suggested using one of the Bitmap constructors, which takes a Graphics instance as a parameter.
However, it's not working for me. It creates Bitmap, but with no content from the Plot control - it is a pure black image.
Here's my code (edited to show drawing of red line to Graphics object):
void PrintDocument_PrintPage(object sender, PrintPageEventArgs e)
{
// Draw a red line on the Graphics object. When printed, this
// line is shown as part of the normal Plot graphics.
Pen myPen;
myPen = new Pen(Color.Red);
e.Graphics.DrawLine(myPen, 0, 0, 200, 200);
myPen.Dispose();
// Create a bitmap from the Graphics object
Bitmap bm = new Bitmap(1000, 1000, e.Graphics);
// Save to disk
// DOES NOT WORK - CREATES FILE THAT IS PURE BLACK (VIEWED
// WITH "PAINT" PROGRAM)
bm.Save(#"C:\Bicw_Dev\Bic.Net\FrontEnd\GraphicsToBmp.bmp", ImageFormat.Bmp);
bm.Dispose();
}
Can anyone suggest why this doesn't work? Is this even a valid approach?
Also, please note:
As far as I can determine (and I've spent quite a bit of time looking) there is no way to get a high resolution, print quality Bitmap from the Plot control directly!
I stress this because several others who asked the question got code samples in response that solved the opposite problem - converting a Bitmap to a Graphics.
I need to convert a Graphics object to a Bitmap object.
And if anyone can suggest an alternate approach that allows me to get a print quality image of my plots into my reports, please feel free. (For example, I can get a low quality (72 bpi) Bitmap from the Plot control, and have considered trying to stretch it - but I've never seen that approach work well in other applications).
Thanks,
-Tom Bushell
Edit in response to comment:
As an experiment, I added the following:
Pen myPen;
myPen = new Pen(Color.Red);
e.Graphics.DrawLine(myPen, 0, 0, 200, 200);
myPen.Dispose();
This caused a red line to be drawn over the plot graphics when I printed my plot. But it had no effect on the Bitmap - it's still pure black.
However, it's not working for me. It
creates Bitmap, but with no content
from the Plot control - it is a pure
black image.
Well you never do paint to the graphics, what do you expect?
You are suppose to do the actual drawing for the output in that event.
The constructor you're using does not copy any graphics, only sets the resolution equal to the graphics resolution. See msdn. As Leppie points out, you have to actually draw something to the bitmap. I suggest getting another graphics object for the item you just created.
Graphics g = Graphics.FromImage(bmp);
//Make the background white
g.FillRectangle(Brushes.White, 0, 0, 1000, 1000);
It is not easy to control the DPI in the Print(Preview) system.
But maybe you are going about it the wrong way. What you want is to create a (large) bitmap and then 'trick' the control in to using that bitmap as if it was the screen. See this list of links.
No need for PrintDocument.