I'm trying to add printing support to a C# WPF application I'm writing and I'm tearing my hair out over this. I'm trying to print a single image from a window in a WPF application. The image is a shipping label and the printer is a thermal printer loaded with 4"x6" shipping label stock. The code to print is as follows:
PrintDialog pd = new PrintDialog();
if (pd.ShowDialog() == true)
{
Image tmpImage = new Image();
tmpImage.Stretch = Stretch.Uniform;
tmpImage.Width = pd.PrintableAreaWidth;
tmpImage.Source = this.img_label.Source;
tmpImage.Measure(new Size(pd.PrintableAreaWidth, pd.PrintableAreaHeight));
tmpImage.Arrange(new Rect(new Point(0, 0), tmpImage.DesiredSize));
pd.PrintVisual(tmpImage, "Shipping Label");
}
This code works in that it will display the print dialog, I can choose my printer, configure it to use the correct label stock, and print the label. However, as other posts have indicated, it does not save the settings I have selected. So, if I choose to print the same image again without closing the application in between, it reverts to the default printer and, even when I choose the correct printer, defaults that printer to the default settings, which includes using the wrong size label stock. So every time I print I have to select the printer and configure it to use the correct stock. This is simply not acceptable in real world use.
After much searching online I have found numerous posts about this but all of them talk about saving the PrintDialog.PrinterSettings object and then using that to initialize the next PrintDialog instance. However, in WPF, there is no PrinterSettings member of the PrintDialog class. That is a Win Forms object. Why the Win Forms and WPF PrintDialog objects are different is beyond me, but that's likely a question that won't get answered. The real question is what I do now. I can, if necessary, re-invent the entire wheel and have my own printer selector and printer configuration pages and print the image using a PrintDocument object and bypass the PrintDialog entirely. I'd rather not do this unless it is entirely necessary. Displaying the PrintDialog is nice, it's what people are used to, and it already has all the ability to configure the printer built right in. But how can I initialize the PrintDialog in WPF to select the proper printer and use the proper printer settings? If only I were using Windows Forms this would be built in. What's the WPF equivalent?
The secondary question is, if there is no WPF equivalent, what is the recommended way to handle this? I don't really need to give the user the ability to configure the printer within my application. All I want it to do is remember the previous settings they chose the next time they go to print, just like every single other PC application that has ever been written. How can this be so hard?
Any help anyone can provide would be greatly appreciated. In the mean time I'm going down the path of re-inventing the proverbial wheel. I hope to get an easier answer soon.
Thanks!
WPF has PrintTicket and PrintQueue classes (and PrintDialog has corresponding properties, which can be initialized with your saved settings).
For the simplicity, you can consider the first one as the paper settings, and the second one - as the printer settings (selected printer).
Related
How can I print a visual as grayscale without actually showing the PrintDialog, e.g.
PrintDialog dialog = new Dialog();
dialog.PrintQueue = new PrintQueue(new PrintServer(), printerNameAsString);
dialog.PrintTicket.InputBin = InputBin.AutoSelect;
// Further settings, e.g. PageMediaSize and scaling the visual.
dialog.PrintVisual(myVisual, "myDescription");
Can I somehow get the PrinterDialog to print the visual in grayscale? Or is there a completely other way to achieve a grayscale printout of my visual?
Edit: myVisual (the Visual I want to print) is a Grid, so it inherits from UIElement.
Edit 2: If possible I would prefer not to use any external libraries (because of company policies).
In the PrintDialog the OutputColor can be set:
myPrintDialog.PrintTicket.OutputColor = OutputColor.Grayscale;
Also, the PrintCapabilities make it possible to actually check which OutputColors are possible:
PrintCapabilities capabilities = myPrintDialog.PrintQueue.GetPrintCapabilities(myPrintDialog.PrintTicket);
ReadOnlyCollection<OutputColor> possibleColors = capabilities.OutputColorCapability;
On the hardware available to me this works fine.
You can try using the standard lib in Microsoft.Expression.Effects assembly. The effect here is MonochromeEffect. Just apply this effect before printing your visual:
myVisual.Effect = new MonochromeEffect();//make grayscale
dialog.PrintVisual(myVisual, "myDescription");
myVisual.Effect = null; //turn it off
You have to import the library I mentioned above and add this using instruction:
using Microsoft.Expression.Media.Effects;
I ran into a similar problem where DrawString would always print solid black--even though the Brushes specified a lighter shade. I finally discovered that by placing an image onto the graphics object, it would print lighter shades that were in the image. It feels like a hack, but it works! (The printdialog solution might have worked too--I don't know since I'm printing automatically from a service where there is no printdialog.)
I need to create a windows store application with e reader functionality. I have to provide a fluid reading experience. Currently, my knowledge of displaying text consists of dropping textblock or textbox on the UI. I do not know how to display large amount of text to create the E Reader experience i am looking for. Which control or combination of controls should i use?
Your text rendering options are
XAML controls
TextBlock
RichTextBlock
WebView + WebViewBrush
DirectWrite - what all the above ultimately use. You can use it with C# through SharpDX.
The controls might be a bit cumbersome to use at times if you need to do measurements etc. and might not give you as much power or performance as DirectWrite does, but will give you support for text selection, copying to clipboard etc. (make sure IsTextSelectionEnabled is set to true).
For measuring text dimensions for paged display in a TextBlock - create a TextBlock in code behind and call Measure() and Arrange(), then get ActualWidth/ActualHeight to get the measurement.
Read Charles Petzold's Principles of Pagination article.
Consider Rapid Serial Visual Presentation (RSVP) speed-reading method used in the ReadQuick app.
I am using VS C# 2008 Express.
I am adding the capability to store and retrieve a user's font selection to my app using the standard FontDialog control.
During my testing I have noticed that certain fonts do not initialize the dialog fully -- the name does not appear in the "Font" box, and the sample is blank.
These are fonts in which part of the name appears in the "Font Style" list on the dialog: "Arial Black", "Segoe UI Light", "Segoe UI Semibold", etc. (Although, interestingly, "Arial Narrow" does work as expected.)
I see the same problem whether I initialize the dialog's font in code or if I set the properties of the FontDialog control to one of the above problem fonts in the IDE.
It is easy to reproduce by just creating a simple form, adding a FontDialog control launched by a button, and setting its font property to "Arial Black" and size to 16.
Any way to resolve this?
Added information:
This problem exhibits on a Windows 7 64-bit system. On Vista and XP, Arial Black and Arial Narrow are listed as separate fonts, rather than just Arial with Black and Narrow styles. So it seems Microsoft is doing something different with the common font dialog in Win7.
I can confirm that behavior, you should wait and see if someone offers a definitive explanation for why this is correct, and if nobody offers one, report it through Microsoft Connect.
Here's how to reproduce the problem, use LINQPad and execute the following program:
void Main()
{
using (var dlg = new FontDialog())
{
dlg.Font = new Font("Arial Black", 16);
dlg.Font.Dump("before");
dlg.ShowDialog();
dlg.Font.Dump("between");
dlg.ShowDialog();
dlg.Font.Dump("after");
}
}
use F4 to add references to System.Drawing and System.Windows.Forms, and hit Ctrl+. on Font and FontDialog to add the required using clauses
Then observe that:
Arial Black is not chosen the first time it is shown
If you, during that first time, pick Arial, and then pick Black in the font style picker, click OK, the dialog comes back up with no font selected.
The only thing different between the 3 dumps of the font (provided you picked Arial, Black, 16 both times) is that the size is slightly off after the dialogs (15.75pt vs. 16pt). Also, the OriginalFontName is left as null after the dialogs. The font name, however, is the same, "Arial Black".
The reason I feel this is a bug is that if the same dialog pops up twice, with the second time showing the results of picking values the first time, it should show the same information.
I need a quick way of forcing my C# Windows Forms application to not scale fonts when a user choose a larger or smaller percentage in the OS settings.
Is this even possible?
Here is what worked for me...
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Font = new System.Drawing.Font("Arial", 14F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Pixel, ((byte)(0)));
The above two lines are copied from my BaseForm.Designer.cs file, but basically I found two easy steps to get "no font scaling":
Set AutoScaleMode to None.
Use "Pixel" as the Unit Type for all Fonts, instead of the default Point value.
As far as if you should let Windows scale your fonts or not, that's up to you. I for one don't like the design, so if I feel my application needs to be scaled, I'll do it myself, with my own options and design.
Over the years of speaking with actual end-users, I've also found that most of them have no idea about DPI settings, and if they have anything other than the default set, it wasn't because they wanted it that way... and they just never noticed because all they use is the web browser and maybe Excel and Microsoft Word (which use whatever font they set it to).
If my application had respected the system font settings, they wouldn't have liked it as much == less sales, because it would have had this huge ugly font like the system dialogs do (and they don't know how to change it, but they don't care about system dialogs they never use).
The problem is that the Font property of a Form or a control specifies the font size in Points. That's a measurement that affect the height of the letters when the DPI setting changes. One point is 1/72 inches. The default DPI, 96 dots per inch and a font size of 9 points yields a letter that is 9 / 72 x 96 = 12 pixels high.
When the user bumps up the DPI setting to, say, 120 DPI (125%) then the letter becomes 9 / 72 x 120 = 15 pixels high. If you don't let the control get larger then the text won't fit in the control anymore. Very ugly to look at.
The Form.AutoScaleMode property solves this problem. It checks at which size the form was designed and compares it against the DPI on the machine on which it runs. And resizes and relocates the controls to ensure this kind of clipping won't happen. Very useful, it is completely automatic without you having to do anything about it.
The typical problem is the "relocates" bit in the previous paragraph. If you give controls their own font size instead of inheriting the size of the form or if the automatic layout of the form isn't kosher then controls may end up in the wrong spot, destroying the organized look of the form.
You need to fix that, it isn't clear from your question what the source of the problem might be. Trying to prevent this auto-scaling from doing its job is not sustainable. You'll have to iterate all of the controls in the form and change their Font, picking a smaller font size. This is however going to get you into trouble a couple of years from now, if not already. Your user is going to complain about having to work with a postage stamp.
The easiest way to debug the layout problem, avoiding the pain of constantly changing the DPI size, is to temporarily paste this code into your form class:
protected override void OnLoad(EventArgs e) {
this.Font = new Font(this.Font.FontFamily, this.Font.SizeInPoints * 125 / 96);
base.OnLoad(e);
}
I found a pretty easy workaround.
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
This will make the text the same size regardless of the autoscale size.
Going against the user wishes like this is not something MS eagerly accommodates.
Rather than fixing the symptom (your app not scaling correctly), would it not be probably just as quick to fix the problem? When a user selects larger fonts, it's usually because they need the larger letters to be able to read them; so scaling correctly is more important than not scaling and remaining illegible to the user.
No. GDI and Windows Forms are resolution dependent. The best option is to design your layouts in a way that they scale appropriately when a window is resized. This tends to allow font size scaling to work correctly, as well, as the layouts will adjust as needed.
This, by the way, is one of the big improvements in WPF - it's designed to be resolution independent.
I suspect you already tried
yourform.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
in your forms, which prevents your forms from scaling, but not your fonts. So what you have to do (even if it is not recommended, see the other posts) is setting the font size for all controls in your form to a fixed size:
Here is a code snippet to grab all controls of a Form:
public List<Control> FindAllControls(Control container)
{
List<Control> controlList = new List<Control>();
FindAllControls(container, controlList);
return controlList;
}
private void FindAllControls(Control container, IList<Control> ctrlList)
{
foreach (Control ctrl in container.Controls)
{
if (ctrl.Controls.Count == 0)
ctrlList.Add(ctrl);
else
FindAllControls(ctrl, ctrlList);
}
}
Under http://www.csharp411.com/change-font-size/ you will find a link how to change the font size of a control, especially when you want to use fixed pixel sized. I think with these tools you can wire it together on your own.
EDIT: Don't forget to say: if you are finally going not to ignore the OS font size and to use AutoScaleMode as intended: this has some quirks, see my previous post here on SO.
In Windows 8.0, even if you set the Font Unit as Pixel, with the AutoScaling set to None, it is auto scaling.
In Windows 7, the same application is not auto scaling.
So therefore, the only way to not to let your text auto scale on Windows 7 is to set the Font Unit as Pixel and the Window's AutoScaling property as None.
And in Windows 8.0, I guess you have no other choice but to set the dpi to the dpi of the developer machine and ask the user to never change it!
i will need to print an "x" according to the coordinates given to me from one of the tables in my database. im probably gonig to use c# to connect to mysql.
i will probably have a winform that is 8.5 x 11 inches (The size of a regular sheet of paper) and i will populate the entire thing with labels of "x" and they will be invisible.
each individual table record will have the coordinates of those labels which should NOT be invisible
the form for every record will show and will print. the printing will be on top of a paper that is actually a physical application itself.
the problem:
how to fill out a physical application using data from a mysql database. (dont tell me that i should be printing the entire app from scratch, the reason this is not possible is because the form is actually TRIPLE paper width (white, yellow, and pink copy), so i cannot print the entire app from scratch, i have to print on top of it.
the question: how do i print "x" at specified regions? is my solution the best way to go or is there a smarter approach?
in case you have no idea what i am talking about, here are some related questions:
ms-access: designing a report: printing text on specific x,y coordinates
Conditional formatting in Access
While labels would offer you the ability to make an X show up I don't feel that having a bunch of hidden labels is the best way.
Does the "application" represent some kind of form? Are you looking to "check-off" boxes using x's and then print this?
I may suggest using GDI+ (drawing) vs using labels.
Consider the following:
Locate the coordinates for your boxes. Then use the drawstring method within an overridden onPaint event-handler for your form or for the panel which may represent your form's canvas.
This article talks about GDI+ and how to draw text as graphics.
http://www.functionx.com/vb/gdi+/objects/fonts.htm