How to print a Windows Form without showing/displaying it - c#

I am trying to print automatically a series of Windows-Forms. I don't need to show them. The code examples I found on the internet work only when I display the Form with show()! I need to initialize the form with data and send it to the printer Here is the code I am using:
public partial class Form2_withoutShow : Form{
PrintDocument PrintDoc;
Bitmap memImage;
public Form2_withoutShow (Data data)
{
InitializeComponent();
/*
Initialize Data (texboxes, charts ect.) here
*/
this.PrintDoc = new PrintDocument();
this.PrintDoc.PrintPage += PrintDoc_PrintPage;
this.PrintDoc.DefaultPageSettings.Landscape = true;
}
public void Print()
{
this.PrintDoc.Print();
}
void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
{
int x = SystemInformation.WorkingArea.X;
int y = SystemInformation.WorkingArea.Y;
int width = this.Width;
int height = this.Height;
Rectangle bounds = new Rectangle(x, y, width, height);
Bitmap img = new Bitmap(width, height);
this.DrawToBitmap(img, bounds);
Point p = new Point(10, 10);
e.Graphics.DrawImage(img, p);
}
private void Form2_withoutShow_Load(object sender, EventArgs e)
{
// remove TITLEBar
this.ControlBox = false;
this.Text = String.Empty;
}
}
I call the Print() method from another class in for-loop and pass the Data to be initialized through the constructor.
The MSDN example captures the part on the screen where the forms should be displayed. This doesn't work for me. The approach I am using now yields only the print of the empty Window if I don't call show(). How do I get the data into the form without having to call the show() method? Approaches like mimize the windows when displaying it, also don't work because that is also the print result: a minimized window.

Before showing the form, the form and its controls are not in Created state. To force the form and its controls to be created it's enough to call internal CreateControl(bool fIgnoreVisible) method of your form:
var f = new Form1();
var createControl = f.GetType().GetMethod("CreateControl",
BindingFlags.Instance | BindingFlags.NonPublic);
createControl.Invoke(f, new object[] { true });
var bm = new Bitmap(f.Width, f.Height);
f.DrawToBitmap(bm, new Rectangle(0, 0, bm.Width, bm.Height));
bm.Save(#"d:\bm.bmp");
Also remove codes which you have in your form Load event handler, and put them in constructor of your form.
Note
There are also other workarounds for the problem:
For example you can show the form in a location outside of boundary of the screen and then hide it again. Set the Location to (-32000, -32000) and set StartPosition to be Manual then Show and Hide the form before drawing to bitmap.
Or as another example, you can show the form with Opacity set to 0 and then Show and Hide the form before drawing to bitmap.

Related

Picture Box doesn't show image

I have a form project in Vs2010.
This is my scenario:
I created a form that I want to use like a splash screen, without borders. Inside I have a picture box big like the form.
I set the image importing it and in designer i can see it.
But when I call the splashscreen form from another form and I show It, I can see only the picture box borders, but doesn't load the image.
Update
I load splashScreen in BmForm_Load (other form):
SplashScreen ss = new SplashScreen();
ss.TopMost = true;
ss.Show();
//Prepare bmForm....
ss.Close();
And this is the designer code snippet for the picture box in splash screen form:
this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.ImageLocation = "";
this.pictureBox1.InitialImage = null;
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(256, 256);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
this.pictureBox1.WaitOnLoad = true;
Update 2
If I doesn't close splashScreen form before other form load end, the image is shown after this!
Question
Somebody knows why isn't the picture showed?
What the problem seems to be is that the preparation of your BmForm is locking down the main UI thread that is trying to load the splash image as well as process the commands to prepare the BmForm.
To counter this, load the splash form in its own thread and close the thread/form when done loading.
Example of the code:
Inside your BmForm_Load
Thread splashThread = new Thread(ShowSplash);
splashThread.Start();
// Initialize bmForm
splashThread.Abort();
// This is just to ensure that the form gets its focus back, can be left out.
bmForm.Focus();
The method to show the splash screen
private void ShowSplash()
{
SplashScreen splashScreen = null;
try
{
splashScreen = new SplashScreen();
splashScreen.TopMost = true;
// Use ShowDialog() here because the form doesn't show when using Show()
splashScreen.ShowDialog();
}
catch (ThreadAbortException)
{
if (splashScreen != null)
{
splashScreen.Close();
}
}
}
You might need to add the using System.Threading; to your class and add some extra error handling to the BmForm_Load event in case errors occur, so that you can clean up the splashThread.
You can read more about threading here

Updating graphics on panels

OK so I am new to C# and I am confused about how events work. currently I have a panel in which I am drawing rectangles. the event is called when the panel is initialized but I want to call it after I do something in my data Grid. I simply want to know how to tell the object to update.
Do I not use an event in this situation? If so do I just create a new graphics instance and start?
Here is my code:
private void panel6_Paint(object sender, PaintEventArgs e)
{
if(dataGridView1.RowCount != 0 )
{
Pen lightRed = new Pen(Brushes.LightSalmon);
lightRed.Width = 1.0F;
lightRed.LineJoin = System.Drawing.Drawing2D.LineJoin.Miter;
int counter = 0;
foreach (var pair in currentPosition)
{
if(dataGridView1[0, counter].Style.BackColor == Color.Red)
{
e.Graphics.DrawRectangle(lightRed, new Rectangle(0, currentPosition.Count / panel6.Height * counter, 66, currentPosition.Count / panel6.Height * counter));
}
}
lightRed.Dispose();
}
}
Whenever you want to force a redraw, call:
Invalidate();
...or
Refresh();
Invalidate() is preferred...Refresh() attempts to be more immediate.
So, for your panel:
panel6.Invalidate();
You would call this at any point where you want to signal to the control that it should repaint itself. That will result in your Paint event firing.

c# - Methods that are called from another class are not running

I am having trouble doing something that I would have though to have been quite simple, all I am trying to do is to call a method from another class, heres how I am calling the method:
Gimjes_2D_Game_Framework1.Characters.Character_One.Create();
and here is the contents of the method I am trying to call:
public static void Create()
{
Form1 f = new Form1();
System.Windows.Forms.PictureBox s = new System.Windows.Forms.PictureBox();
//location of image (in thia case it is from resources):
s.BackgroundImage = Gimjes_2D_Game_Framework1.Properties.Resources.DefaultSprite;
//Set to height and width of image:
s.Height = 64;
s.Width = 64;
s.Size = new System.Drawing.Size(60, 60);
s.Location = new System.Drawing.Point(50, 50);
f.Controls.Add(s);
}
Try adding
f.Show();
or
f.ShowDialog();
to the end of your Create method.
Otherwise, you're making a form, and never displaying it.
Show gives you a modeless form, and ShowDialog gives you a modal dialog.
See documentation here for more information.
If you intend to return a Form1 object, to be shown at a later point, you need to change your method to:
public static Form1 Create()
{
Form1 f = new Form1();
...
return f;
}

Background image shows, but pen lines don't

I set a background of a picturebox as an image, which is fine.
I try to draw pen lines over the background image, but the lines do not appear on top of the image
This should happen: (I changed the code and not a clue what I changed to get this to happen, but this is without resetting the picturebox)
The picturebox is updated every 2000ms to show the flag signals, but can't for the life of me get the lines to appear.
Here are the bits of code:
lblSelectedChar.Text = inputchar.ToString();
picSemaphore.BackgroundImage = semaphore.Properties.Resources.human;
leftHandDown();
rightHandLow();
The leftHandDown() and rightHandLow() methods draw the lines, but I think they are drawn behind the image. I would like them drawn in front. Any idea how I would do this?
private void leftHandDown()
{
lblLeftHand.Hide();
display.DrawLine(penLeftArm, centXCoord, centYCoord, LHDownXCoord, LHDownYCoord);
lblLeftHand.Top = LHDownYCoord + 74;
lblLeftHand.Left = LHDownXCoord + 13;
lblLeftHand.Show();
lblLeftHand.Update();
}
Also, when the label is moved, it leaves a white space where it last was. Any idea how to stop this happening?
As LarsTech suggested, use the Paint() event of your PictureBox to draw the lines with the supplied Graphics via the e.Graphics value. You'd call picSemaphore.Refresh(); to make the lines update.
Here's a quick example to give you the basic idea:
public partial class Form1 : Form
{
private bool displayLeftArmLine = false;
private bool displayRightArmLine = false;
public Form1()
{
InitializeComponent();
picSemaphore.Paint += new PaintEventHandler(picSemaphore_Paint);
}
void picSemaphore_Paint(object sender, PaintEventArgs e)
{
if (displayLeftArmLine)
{
e.Graphics.DrawLine(penLeftArm, centXCoord, centYCoord, LHDownXCoord, LHDownYCoord);
}
if (displayRightArmLine)
{
e.Graphics.DrawLine(penRightArm, centXCoord, centYCoord, RHDownXCoord, RHDownYCoord);
}
}
private void leftHandDown()
{
lblLeftHand.Hide();
lblLeftHand.Top = LHDownYCoord + 74;
lblLeftHand.Left = LHDownXCoord + 13;
lblLeftHand.Show();
lblLeftHand.Update();
displayLeftArmLine = true;
picSemaphore.Refresh(); // force Paint() event for "picSemaphore" to fire
}
}

Adding Controls to Form from another class

I have a program, which creates one pictureBox in Form1, and then creates an instance of a class that I called InitialState. The InitialState puts the source to the Image so that it is displayed, and after some time has passed, for which I used a Timer, it creates the next class, MainMenuState. Now, in that MainMenuState class that I've created, I would like to create another pictureBox and make it display on that Form1. Later on, I would like to make the pictures inside it change a bit, and then (possibly) destroy that pictureBox. After that, the program enters the next state (which is in yet another class), and again I would like that class to add a picture box to the original form, and so on.
Basically, I would like to dynamically add controls to the main Form1, but not in the said form, but from the classes I create later on. I've been searching on the internet for a way to do that, and it seems like I would have to use a delegate in order to invoke the Controls.Add method of the Form1 class. I've tried that, and the code compiles, but the pictureBox still doesn't show up.
Here's my code:
Form1 class:
public const string RESOURCE_PATH = "C:/Users/Noel/Documents/Visual Studio 2010/Projects/A/Resources/Animations/";
public Form1()
{
InitializeComponent(); //here, the first pictureBox shows
iInitializeComponent();
zacetnaAnimacija.Dock = DockStyle.Fill; //zacetnaAnimacija is the first pictureBox that appears
zacetnaAnimacija.Anchor = AnchorStyles.Top | AnchorStyles.Left;
zacetnaAnimacija.SizeMode = PictureBoxSizeMode.StretchImage;
InitialState intialState = new InitialState(this, zacetnaAnimacija); //entering InitialState
}
InitialState class:
class InitialState : State
{
System.Timers.Timer initialTimer;
PictureBox pictureBox1;
Form1 form;
public InitialState (Form1 form, PictureBox pictureBox1) {
this.form = form;
GifImage zacetnaSlika = new GifImage(Form1.RESOURCE_PATH + "Presenting.gif"); //this is just a .gif picture I'm displaying
Image trenutnaSlika = zacetnaSlika.GetFrame(0); //a method that plays the .gif
pictureBox1.Image = trenutnaSlika; //makes the first .gif display
this.pictureBox1 = pictureBox1;
initialTimer = new System.Timers.Timer(2500);
initialTimer.Enabled = true;
initialTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
initialTimer.Enabled = false;
MainMenuState menuState = new MainMenuState(form, pictureBox1); //enters main menu state with the Form1 argument passed on
}
MainMenuState class:
class MainMenuState : State
{
Form1 form;
public MainMenuState (Form1 form, PictureBox pictureBox1) {
this.form = form;
GifImage zacetnaSlika = new GifImage(Form1.RESOURCE_PATH + "animated.gif");
Image trenutnaSlika = zacetnaSlika.GetFrame(0);
pictureBox1.Image = trenutnaSlika; //this simply makes another .gif appear in the picture box instead of the first one
PictureBox a = new PictureBox(); //HERE'S my problem, when I want to add ANOTHER pictureBox to that form.
a.BackgroundImage = trenutnaSlika;
a.Location = new System.Drawing.Point(0, 0);
a.Name = "zacetnaAnimacija";
a.Size = new System.Drawing.Size(150, 150);
a.TabIndex = 1;
a.TabStop = false;
AddControl(a); //calling the delegate
}
public delegate void AddControls(PictureBox a);
public void AddControl(PictureBox a)
{
if (form.InvokeRequired)
{
AddControls del = new AddControls(AddControl);
form.Invoke(del, new object[] { a });
}
else
{
form.Controls.Add(a);
}
}
As I've said, the code compiles, but it doesn't create the PictureBox a on the Form1, when the MainMenuState is created. The thing is, if I don't use the delegate in the MainMenuState and just try to do something like form.Controls.Add(a), then I get a "cross-thread operation not valid" exception, and it doesn't even compile. That's why I used the delegate, but even now, it doesn't work.
Can someone please help me?
initialTimer = new System.Timers.Timer(2500);
That's part of the reason you're having trouble. The Elapsed event runs on a threadpool thread, forcing you to do the BeginInvoke song and dance. Use a System.Windows.Forms.Timer instead, its Tick event runs on the UI thread.
You'll also run into trouble with memory management, these classes need to implement IDisposable.
Oh my God, I just found the reason X_x
It was the fact that since the first pictureBox was covering the entire form, and the second one, which was created by the delegate, showed behind it! I just need to bring it to front!
Thank you guys, nonetheless, I probably wouldn't have come to that without you.
Edit: However, may I ask how to bring that control to the front? The a.BringToFront() function doesn't seem to work.
Instead of
form.Invoke(del, new object[]{a});
try:
form.Invoke(new ThreadStart(delegate
{
form.Controls.Add(a);
}
));

Categories