Working in .NET 3.5.
Summary:
Trying to replicate functionality of an existing third party component, which breaks in Windows 7.
Until now the user could select a bunch of image files to print, specify a page size for each image and then send them off to print all in one go. I am in dire need of a conceptual explanation of how to go about printing switching the page size on the fly when printing each page.
Details
So far I have figured out how to print multiple images all with the same page size. I use a list of images and use a PrintDocument object, setting the HasMorePages property of the PrintPageEventArgs to true until I reach the end of the list.
Here's a class I quickly threw together to test this:
public partial class Form1 : Form
{
private List<Image> images { get; set; }
private PrintDocument printDocument { get; set; }
public Form1()
{
InitializeComponent();
this.images = new List<Image>();
this.images.Add(Image.FromFile(#"C:\test60.bmp"));
this.images.Add(Image.FromFile(#"C:\SuperLargeTest.jpg"));
this.printDocument = new PrintDocument()
{
PrinterSettings = new PrinterSettings()
};
this.printDocument.PrintPage += printDocument_PrintPage;
}
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
e.PageSettings.PaperSize = this.paperSizes[this.currentImageIndex];
RectangleF marginBounds = e.MarginBounds;
RectangleF printableArea = e.PageSettings.PrintableArea;
int availableWidth = (int)Math.Floor(printDocument.OriginAtMargins ? marginBounds.Width : (e.PageSettings.Landscape ? printableArea.Height : printableArea.Width));
int availableHeight = (int)Math.Floor(printDocument.OriginAtMargins ? marginBounds.Height : (e.PageSettings.Landscape ? printableArea.Width : printableArea.Height));
g.DrawRectangle(Pens.Red, 0, 0, availableWidth - 1, availableHeight - 1);
g.DrawImage(this.images[currentImageIndex], printableArea);
e.HasMorePages = ++currentImageIndex < this.images.Count();
}
private void button1_Click(object sender, EventArgs e)
{
this.printDocument.OriginAtMargins = false;
this.printDocument.Print();
}
}
The thing that I really can't figure out is how to go about changing the page size for, say, the second image.
If I wanted the first image to print in A4 and then the second one to print on A3, how would I go about doing that?
I found this SO question here which seemed to suggest changing the PageSize in the PrintPageEventArgs, but had no joy there.
I also tried to use the QueryPageSettingsEventArgs event and set the PageSettings there, but that didn't seem to work either...
What I would like to achieve is print multiple pages of different size as a single document. Any suggestions, links, explanations, sample code would be very much appreciated.
Anything in C# or VB.NET is fine.
That's work for me too.
Translated to C#:
private bool SetPaperSize(PrintDocument pd, PaperKind nKind)
{
foreach(System.Drawing.Printing.PaperSize ps in pd.PrinterSettings.PaperSizes)
{
if (ps.Kind == nKind)
{
pd.DefaultPageSettings.PaperSize = ps;
return true;
}
}
return false;
}
In VB.NET .. You can use this Sub ..
DocPrint is PrintDocument var
Sub SetPaperSize(ByVal nKind As PaperKind)
Dim ps As PaperSize
For ix As Integer = 0 To DocPrint.PrinterSettings.PaperSizes.Count - 1
If DocPrint.PrinterSettings.PaperSizes(ix).Kind = nKind Then
ps = DocPrint.PrinterSettings.PaperSizes(ix)
DocPrint.DefaultPageSettings.PaperSize = ps
End If
Next
End Sub
Hope this help ..
If you want all the pages to appear as one job (in short avoid being interleaved with other jobs), you can set the page size for the next page inside the PrintPage event handler by changing the default page size of the PrintDocument object.
Related
I have a problem I've been looking into for a few days now, I just can't think of a logical way of doing what I want.
I have an app which has a task list. It starts of with 3 controls: a textbox, datetimepicker and a PictureBox which changes image on click. The user can then press an image which will add another row of controls below (It gets the properties of the dynamic controls from the controls already created):
https://www.dropbox.com/s/o2pub6orww24w25/tasklist.png (This is a screenshot to make it clearer)
Now what I want to do is save the values from each of the rows (A row being defined as: Textbox, Date, Status) into an SQLite DB.
For the first row it is easy, because that has a unique design name (and is a 'static' control).
However, the problem hits when I attempt to save values from the dynamic controls:
Problem a) I cannot reference the dynamic control because 'It does not Exist in the current Context'. -The function for creating the controls has a public access modifier so I thought that should do the trick? -It didn't. I've also tried:Panel1.pb.blah but it still didn't recognize the control?
Problem b) How can I tell my program that each row is a new set of data? In other words, how can I run a new insert command for each row? -I thought of doing this as a for-each-textbox loop, however would that not just pick up the first dynamic date everytime?
I've also thought of using the tag property and setting it to the counter variable, to group the controls in the row. (The counter being an integer which increments every time a new row is added.) However I cannot do that because the picture box uses the tag property as part of its function to change image on click (Changes multiple times).
Code:
Adding the Controls:
public void pictureBox1_Click(object sender, EventArgs e)
{
//TextBox Control
int tbh = tasktb.Location.Y + (counter*25);
int tbsh = tasktb.Size.Height;
int tbsw = tasktb.Size.Width;
TextBox tb = new TextBox();
tb.Location = new Point(9, tbh);
tb.Size = new System.Drawing.Size(tbsw, tbsh);
tb.Tag = counter.ToString();
//Date Time Control
int dth = duedatedb.Location.Y + (counter * 25);
int dtsh = duedatedb.Size.Height;
int dtsw = duedatedb.Size.Width;
DateTimePicker dtp = new DateTimePicker();
dtp.Location = new Point(300, dth);
dtp.Size = new Size(dtsw, dtsh);
dtp.Format = System.Windows.Forms.DateTimePickerFormat.Short;
//Picture Box Control
int stsh = status.Location.Y + (counter * 25);
int stssh = status.Size.Height;
int stssw = status.Size.Width;
PictureBox pb = new PictureBox();
pb.Location = new Point(429, stsh);
pb.Size = new Size(stssw, stssh);
pb.Image = Red;
pb.Click += new System.EventHandler(pb_Click);
panel1.Controls.Add(tb);
panel1.Controls.Add(dtp);
panel1.Controls.Add(pb);
++counter;
}
Trying to Reference the control: (For purposes of changing the image on click) [Found the Control.Find function from researching this in the MSDN Website]
public void pb_Click(object sender, EventArgs e)
{
PictureBox pb = (panel1.Controls.Find("pb",false));
if (pb.Image == Red) { pb.Image = Orange; status.Tag = "Orange"; }
else if (pb.Image == Orange) { pb.Image = green; status.Tag = "Green"; }
else if (pb.Image == green) { pb.Image = Red; status.Tag = "Red"; }
}
The essential problem here is Problem a, if you guys could see where I have gone wrong with that, I'd be able to go away and attempt to write some code to get around problem b.
(I have included Problem b in this for your suggestions on the best way to do this. -At the moment I have no clue!)
Thank you for any help received! It really is appreciated!
ControlCollection.Find looks for a control with the specified name, and you haven't set any. The variable names in your code aren't related. So, either:
pb.Name = "pb";
But that would mean you'd eventually have several items with the same name. So, seeing how you want to change the picture of the clicked PictureBox, just do this:
public void pb_Click(object sender, EventArgs e)
{
PictureBox pb = (PictureBox)sender;
if (pb.Image == Red) { pb.Image = Orange; status.Tag = "Orange"; }
else if (pb.Image == Orange) { pb.Image = green; status.Tag = "Green"; }
else if (pb.Image == green) { pb.Image = Red; status.Tag = "Red"; }
}
The sender argument always contains a reference to whichever control raised the event, in this case whichever picturebox was clicked!
Edit: As for your other question, I assume you'll need to do stuff to the controls later on, so I suggest you store a reference to all of them (or at least the ones you need), something like this:
// helper class
private class Entry
{
public TextBox TextBox { get; private set; }
public DateTimePicker DateTimePicker { get; private set; }
public PictureBox PictureBox { get; private set; }
public Entry( TextBox tb, DateTimePicker dtp, PictureBox pb )
{
this.TextBox = tb;
this.DateTimePicker = dtp;
this.PictureBox = pb;
}
}
// member field
private List<Entry> m_Entries = new List<Entry>();
// at the end of pictureBox1_Click
public void pictureBox1_Click(object sender, EventArgs e)
{
....
m_Entries.Add( new Entry( tb, dtp, pb ) );
}
Then you can use the items in that list to interact with your rows. You might also want to add an index, or a reference to whatever the original data structure is. Also, you might want to think about if you really should be creating the controls yourself like that or actually use some kind of table/grid control to host them!
Or perhaps just wrap up all those controls in a single UserControl, with logic included and all!
I have a WPF application in which I have 3 forms of prints. In 2 ways, I use directly a window's visual and use PrintVisual(myPanel, "Title") to print it. And in 3rd, I got to print multiple pages so I am using FlowDocument, StackPanel and finally IDocumentPaginatorSource and call PrintDocument to print.
All the code is working perfectly on my system. I can perform Print on OneNote and XPS and it works as expected. But the same app, when I try to run on other system it shows blank page for all 3 prints. PrintVisual is supported on .NET 3.0, 3.5, 4.0, 4.5, so I can't find a reason for it not to work.
I will also share my some code, for better clarity :
// 1 PRINT
public void PrintRegister()
{
PrintDialog pd = new PrintDialog();
if (pd.ShowDialog() == true)
pd.PrintVisual(this, "Register Window");
return;
}
// 2 PRINT
public static void PrintAttendanceOf(System.Data.DataRow r1) {
PrintLayoutWindowPORT plw = PrintUtility.CreatePrintLayoutWindowPORTObject(r1); // PORTRAIT
Canvas p1 = PrintUtility.CloneCanvas(plw._PrintCanvas, 5);
Grid myPanel = new Grid();
myPanel.Margin = new Thickness(10);
myPanel.Children.Add(p1);
PrintDialog pd = new PrintDialog();
pd.PageRangeSelection = PageRangeSelection.AllPages;
PrintQueue pq = pd.PrintQueue;
pq.DefaultPrintTicket.PageOrientation = PageOrientation.Portrait;
pd.PrintTicket = pq.DefaultPrintTicket;
double pageWidth = pd.PrintableAreaWidth;
double pageHeight = pd.PrintableAreaHeight;
myPanel.Measure(new Size(pageWidth, pageHeight));
//myPanel.Arrange(new Rect(new Point(1, 1), myPanel.DesiredSize));
myPanel.UpdateLayout();
if (pd.ShowDialog().GetValueOrDefault(false))
{
pd.PrintVisual(myPanel, "Attendance Chart");
}
pd = null;
plw.Close();
plw = null;
myPanel = null;
p1 = null;
return;
}
Code for the 3rd Print i.e. Multiple Pages is quiet long, hence have not added it now here. If required, can post it.
Can anyone help me know the reason for printing blank page and no contents on other system. I feel some sort of compatibility or system or resource may be required or what else?
Any help is highly appreciated.
Thanks
I have made a voting system for our school. I already made an Add, Update, Delete, and retrieving data from database. I have a problem making a dynamic control automatically with the exact amount of data i have in database. For example, I have added pictures to the two Candidates in the position of President, then, I want it to dynamically create a PictureBox in my new form and retrieve the pictures in a 2 pictureboxes and radiobutton for their names under it. Is it possible for me to do it in array? I'm new to programming so bear with me please.
I'm a little bit confused. Can you make an example of it, if possible please.. :)
This should get you going.
Add this code to your form. And you can use this code for any buttons or anything you want. But you should maybe read up on the FlowLayoutPanel or GroupBox to get this to work in reality.
Point _imagePos = new Point(10,10);
int _imageCounter = 1;
private void NewPictureBox(string pathToImg, string imageName)
{
var img = new PictureBox
{
Name = "imageBox" + _imageCounter,
ImageLocation = pathToImg,
Left = _imagePos.X,
Top = _imagePos.Y,
SizeMode = PictureBoxSizeMode.StretchImage,
Height = 50,
Width = 50
};
var txt = new TextBox
{
Text = imageName,
Left = _imagePos.X,
Top = img.Bottom + 10
};
this.Controls.Add(img);
this.Controls.Add(txt);
_imageCounter++;
_imagePos.Y += 10 + img.Height + txt.Height;
}
private void Form1_Load(object sender, EventArgs e)
{
NewPictureBox(#"C:\test\QuestionMark.jpg", "image1");
NewPictureBox(#"C:\test\QuestionMark.jpg", "image2");
}
How can I flip/rotate the label in C# Windows Forms?
I set the background image to my label.
At every time interval it moves three pixels to the right side. When it reaches the form end position I need the label to be flipped and turned back.
I have tried the following way, but I didn't get the solution.
private void timer1_Tick(object sender, EventArgs e){
if (label2.Location.X < this.Width)
label2.Location = new Point(label2.Location.X + incr, label2.Location.Y);
else
{
incr = -2;
label2.Location = new Point(label2.Location.X - 50, label2.Location.Y);
label1.Image.RotateFlip();
}
this.Refresh();
}
Create a class, newlabel, which can rotate its Text on any angle specified by the user.
extend label class& override paint method
You can use it by code or simply dragging from the ToolBox.
using System.Drawing;
class newLabel : System.Windows.Forms.Label
{
public int RotateAngle { get; set; }
public string NewText { get; set; }
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Brush b =new SolidBrush(this.ForeColor);
e.Graphics.TranslateTransform(this.Width / 2, this.Height / 2);
e.Graphics.RotateTransform(this.RotateAngle);
e.Graphics.DrawString(this.NewText, this.Font,b , 0f, 0f);
base.OnPaint(e);
}
}
Now drag this custom control to be used into your form.
You have to set the below properties.
newlbl.Text = "";
newlbl.AutoSize = false;
newlbl.NewText = "ravindra";
newlbl.ForeColor = Color.Green;
newlbl.RotateAngle = -90;
Change angle as required by simply changing the RotateAngle property.
So...You can do this way:
1.Download this dll file : http://www.mediafire.com/download/hc16qezty0k6qnv/RotateLabel.dll
2.Go on your Visual Studio and open your solution
3.Now you need to go on Projects tab -> Add references... -> Then browse the
file you downloaded and simply add that file
4.Next step is to right click on ToolBox
5.After you done that you need to click on Choose Items
6.Again browse your downloaded file and add VerticalLabel
7.Then you can drag VerticalLabel from the Toolbox to your form.
That is it, its simple.
Hope that helped you i just translated this answer and made it simpler :)
Good luck,
Stralz
I have a Panel filled with a lot of controls for users to fill. These include textboxes, checkboxes, radiobuttons etc. It is a long form to fill so the controls are in a scrollable panel. What I need is to save the whole panel as pdf. I think PDFsharp is a good library to be able to save any text or image as a pdf file but I don't want to write code for every single control inside the panel. I once wrote a class to create a pdf file from a Control object. It was iterating all inner controls (and their inner controls until no inner control is left) of the given control and write their Text property (yes/no for chekable controls) to pdf using their Location and Size properties. I could not find it now but I remember it was having issues with some of the DevExpress controls I use so I didn't bother writing it again. (Edit: I had to, you can find it below.) I think taking a screenshot and save that image as pdf would be nice but I couldn't find out how to achieve it. This question seems like it but there is no satisfying answer to that.
So, screenshot or not I'm open for any advice. There should be many occasions where users must fill long forms and be able to keep it as pdf. Again, any advice or workaround would be appreciated. (I think about creating the form using html, displaying it in a WebBrowser control and using an html to pdf library but I really prefer using my existent form)
Many Thanks.
Edit:
I had to write something iterates inner controls of a container control (like a panel) and writes every inner control to a pdf using their Location, Size and Font properties though, I don't recommend to use it (at least as it is) because of these:
It sets the page's size to given control's size and use only one (usually huge) pdf page. You can add a logic to split it to pages if you need to. (I didn't, but I guess you'll probably need your pdf more printer friendly).
Cheeso's method (using a FlowDocument) is a much more "legitimate" way for a task like this. I prefer using that over this but I didn't have a choice in this instance.
I used PDFsharp in this code. You can find it in it's hompage or it's CodePlex page.
PdfReport class:
private PdfDocument Document;
public Control Control { get; private set; }
public PdfReport(Control control) { Control = control; }
public PdfDocument CreatePdf(PdfDocument document = null)
{
Document = document != null ? document : new PdfDocument();
PdfPage page = Document.AddPage();
page.Height = Control.Height;
page.Width = Control.Width;
XGraphics gfx = XGraphics.FromPdfPage(page);
foreach (PdfItem item in CreatePdf(new Point(0, 0), Control.Controls))
{
XStringFormat format = item.IsContainer ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.BottomCenter ? XStringFormats.BottomCenter : item.TextAlign == ContentAlignment.TopLeft ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.TopCenter ? XStringFormats.TopCenter : XStringFormats.Center;
gfx.DrawString(item.Text, item.Font, item.Brush, new XRect(item.Location, item.Size), format);
}
return Document;
}
private IEnumerable<PdfItem> CreatePdf(Point location, Control.ControlCollection controls)
{
List<PdfItem> items = new List<PdfItem>();
foreach (Control control in controls)
{
if (control.Controls.Count > 0)
items.AddRange(CreatePdf(control.Location, control.Controls));
items.Add(new PdfItem(control, location));
}
return items;
}
public void SaveAsPdf(string path, bool open = false)
{
CreatePdf().Save(path);
if (open)
Process.Start(path);
}
PdfItem class:
public string Text { get; set; }
public Point Location { get; set; }
public Size Size { get; set; }
public Font Font { get; set; }
public bool IsContainer { get; set; }
public ContentAlignment TextAlign { get; set; }
public Color ForeColor { get; set; }
public XBrush Brush { get { return new SolidBrush(ForeColor); } }
public PdfItem() { }
public PdfItem(string text, Point location, Font font, Color foreColor, Size size, bool isContainer = false, ContentAlignment alignment = ContentAlignment.MiddleCenter)
{
Text = text;
Location = location;
Size = size;
Font = new Font(font.FontFamily, font.Size, font.Style, GraphicsUnit.World);
TextAlign = alignment;
ForeColor = foreColor;
IsContainer = isContainer;
}
public PdfItem(string text, Point location, Size size)
: this(text, location, new Font("Calibri", 12), Color.Black, size) { }
public PdfItem(Control control, Point parentLocation)
: this(control.Text, control.Location, control.Font, control.ForeColor, control.Size, control.Controls.Count > 0)
{
Location = new Point(Location.X + parentLocation.X, Location.Y + parentLocation.Y);
IEnumerable<PropertyInfo> properties = control.GetType().GetProperties();
if (properties.FirstOrDefault(p => p.Name == "TextAlign" && p.PropertyType == typeof(ContentAlignment)) != null)
TextAlign = (control as dynamic).TextAlign;
if (properties.FirstOrDefault(p => p.Name == "Checked" && p.PropertyType == typeof(bool)) != null)
{
string title = control.Text != null && control.Text.Length > 0 ? string.Format("{0}: ", control.Text) : string.Empty;
Text = string.Format("{0}{1}", title, (control as dynamic).Checked ? "Yes" : "No");
}
}
Regarding
. I think taking a screenshot and save that image as pdf would be nice but I couldn't find out how to achieve it.
There is a tool called "cropper" available on codeplex.com. It is designed to be used as a user tool that can take screenshots. It is managed code, open source.
I can imagine embedding some of the cropper magic into your app so that you could take that screenshot. I can also imagine this would be useful for collecting a diagnostic image of the screen at the time of a problem.
On the other hand... if you are interested in producing a printed form that reproduces the content on the screen, then I think you should be using WPF, in which case doing what you want is pretty easy. For example, this question describes how to do a print-preview for a FlowDocument. From that point your user can print to PDF (if he has a PDF printer installed) or print to XPS, or print to a physical output device, and so on.
I don't know if this would help you or not, but DocRaptor.com's pdf api could be built in so it would do it for you, no matter what the user inputs. It uses basic html.
As you can use the below :)
YourPanel.AutoSize = true;
int width = YourPanel.Size.Width;
int height = YourPanel.Size.Height;
Bitmap bm = new Bitmap(width, height);
YourPanel.DrawToBitmap(bm, new Rectangle(0, 0, width, height));
string outputFileName = #"C:\YourDirectory/myimage.bmp";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
{
bm.Save(memory, ImageFormat.Bmp);
Clipboard.SetImage(bm);
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
YourPanel.AutoSize = false;
The Clipboard.SetImage will send you bm to the clipboard so you can paste them to your pdf form or whatever document
This also has an example built in that saves it as a image for you if you want.
The trick here is Autosize for your panel. It needs to be set to true so the panel resizes itself as a whole area visible, then right after you do your work you can resize it to false so it uses scrollbars again for the users screen (you may see it flash for half a second, but this code does work.
Saving it in a PDF I personally just prefer to write it there as my clipboard or you can write byte. But ITextSharp is a great library for the extension to work with!
I Really hope this helps.