Saturday, November 23, 2019

Missing A Project/Assembly Reference

I have several solutions that need to reference assemblies in another solution. For many years, I used assembly/DLL references, because the other solution is our "Core Framework" solution, and I didn't want other programmers messing around with our framework classes. At another company that I worked at, that happened all too often. Then I'd have to go back in and fix all the bugs they put into *my* Framework classes!!! Without the projects being in the solution, it wasn't as likely that someone would go in and mess around with those classes.

But assembly references can cause issues too (such as outdated assemblies and problems with automated Builds, to name just two). So I finally relented and started adding the necessary Core Framework projects to each new solution and then any new projects for that solution could have project references to the Framework instead of assembly references. All was well for a while, but then several months ago, the problems began.

In a new solution that included Framework projects, I started getting "red squigglies" everywhere for one particular Framework project, showing the usual error when I moused over: "blah blah blah does not exist (are you missing an assembly reference?)". None of the other Framework projects have had problems, only this one. The using directive for that project's namespace had the squiggly and any class that was in that namespace also had squigglies. And Intellisense wouldn't work for any method calls, etc. And here's the weird part: with all those errors, everything still built fine on my computer (and on the Build server)!!

I'm using Visual Studio 2017. I was at version 15.8.something, which was not the latest version. So, I thought that I needed to update (because nobody else had the problem), so I'm now at the latest version (15.9.17) and it did NOT help! =0(

Googling for "Visual Studio 2017 missing project references" offered all kinds of suggestions. Clean the solution and build. Clean the solution and rebuild. Delete the obj folder and reopen VS. Add dependency references. Remove the project and add it back in. Remove the reference and add it back in. And many more that I don't even remember right now ... none worked!!!

I would periodically, drop my search for a fix and continue working for a while. Until the fact that I had no Intellisense started driving me nuts again and I'd search some more. This went on like this for months. Until I finally found something that worked!!!

Someone on some Forum (StackOverflow, I think) suggested unloading the problem project. The red squigglies suddenly disappeared! And Intellisense was back! The project reference is still there (because unloading doesn't affect the .sln at all, which is nice because I don't have to worry about checking it into source control, since there's no change, and it won't affect any of the other developers).

All you have to do is right-click the offending project in the Solution Explorer and choose "Unload Project" (it's down towards the bottom of the menu). The project remains displayed in the solution, but is marked as "(unavailable)". If you need to, you can always right-click it again and choose "Reload Project".

I have no idea why this works (I tried to Google to find an answer to that question), but after trying off-and-on for months to find a solution to my problem, I'm not going to "look a gift horse in the mouth", as they say!

Now I can continue with my Happy Coding! =0)

Sunday, November 03, 2019

Printing Your WinForm

Many developers still use WinForms (Windows Forms), and I frequently see questions on the Forums about how to print your Forms. I think this type of question might be asked by developers wanting to have a printed version of their forms, with data filled in, to maybe show to a prospective customer. Or, maybe the application contains an Invoice Form with customer data to be printed to include with a customer purchase. I'm sure there are lots of reasons for wanting this functionality.

I have seen many examples of how to do this when Googling, and some work better than others. Some of the issues I've seen include not taking into account multiple monitors ... it only works with your main laptop/tablet screen, or the image ends up spanning your laptop screen plus all of your monitors (I use two external monitors ... imagine three screens crammed onto one page)! Not exactly what you're looking for, is it?

After a bit of trial-and-error, cobbling together bits and pieces from various other sources that I've found with Google, I've come up with this. It works pretty well, I think. Call this PrintForm() method from a button click:

public void PrintScreen()
System.Drawing.Printing.PrintDocument oDoc = new System.Drawing.Printing.PrintDocument();
oDoc.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(oDoc_PrintPage);
PrintDialog pd = new PrintDialog();
pd.PrinterSettings = new System.Drawing.Printing.PrinterSettings();
pd.Document = oDoc;
oDoc.DefaultPageSettings.Landscape = true;

// print like this:
//if (pd.ShowDialog() == DialogResult.OK)
// oDoc.Print();

// for now, let's just use the print preview to demonstrate
PrintPreviewDialog ppd = new PrintPreviewDialog();
ppd.Document = oDoc;

The PrintPage EventHandler gets called for both the Print and the PrintPreviewDialog, so all the necessary code is in this event handler:

private void oDoc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
// First, let's get the image of this running Form that we want to print
Bitmap formBitmap = new Bitmap(this.Width, this.Height);
this.DrawToBitmap(formBitmap, new Rectangle(0, 0, this.Width, this.Height));

// for clearer images
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

// this bit of code is needed to figure out if we need to scale the bitmap to fit the page
// (there is an implicit conversion from int to float)
float neededWidth = this.Width;
float neededHeight = this.Height;

float availableWidth = e.PageBounds.Width;
float availableHeight = e.PageBounds.Height;

double neededRatio = neededWidth / neededHeight;
double availableRatio = availableWidth / availableHeight;
float fitRatio = 1;

if (neededRatio >= availableRatio)
fitRatio = availableWidth / neededWidth;
fitRatio = availableHeight / neededHeight;

// The Min size needs to be converted to int for the DrawImage() method.
int newWidth = Convert.ToInt32(Math.Min(neededWidth * fitRatio, availableWidth));
int newHeight = Convert.ToInt32(Math.Min(neededHeight * fitRatio, availableHeight));

e.Graphics.DrawImage(formBitmap, 0, 0, newWidth, newHeight);

Have fun and Happy Coding! =0)