My blog has moved! Redirecting…

You should be automatically redirected. If not, visit http://www.digitalfugu.com/blog/ and update your bookmarks.

Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Friday, October 12, 2007

Update: Showing Form Without Stealing Focus

Remember I was using an API call to ShowWindow to show our custom window w/o stealing focus but had to set the visibility property programatically to get the form working properly? No? That was what we were doing but we've found something much cleaner.

More digging through MSDN showed that you can override a form's ShowWithoutActivation property to always return true to get the same effect. This allows you to use the normal Form.Show() method and all it's automatic goodness. However, this is only available in .Net Framework 2.0 or later. If you are in the 1.0 or 1.1 world, you'll need to do the P/Invoke to call the ShowWindow API.

The code looks like this:
protected override bool ShowWithoutActivation
{
get
{
return true;
}
}
It is like magic. I'm liking it a lot.

Calling ShowWindow from WinForm causes form loading to stall

In our quest to draw custom windows for our application we are using the WebBrowser control provided by .Net Framework 2.0, to display messages with rich formatting. However, our custom form drawing requires that we know the form dimensions before the form is initialized which presented us a problem because our messages can be user defined and allow line breaks, as well as some limited formatting options such as bold and italics and as such will vary in height when it is rendered in the WebBrowser control. So the problem became: How can I pre-render out the html and measure how tall it will be so that we can have the required window size in the constructor before form initialization?

We discovered that we could use a scheme of "splitting" up the constructor of our form so that we could load up the web browser control, then handle the DocumentComplete event for the "second" part of the constructor which includes the normal call to InitializeComponent(). If we don't wait for DocumentComplete() the Document associated with the browser control isn't null, but also doesn't have correct size information because it hasn't rendered yet. That was great. We could use WebBrowser.Document.Body.ScrollRectangle.Height.

An additional complication was that we didn't want the form to steal focus when it showed up and were using the ShowWindow api call with the SW_SHOWNOACTIVATE parameter. This meant that to keep the parent thread running we needed to use Application.Run() with no params instead of the normal Application.Run(FormObject) and we called the ShowWindow api from part two of the form constructor to prevent strange window ghosting. This caused a strange side effect that the form would load up part way and then not seem to respond. This was a mystery.

Strangely enough since we could see the form we assumed that it was fine, but why wasn't it responding to inputs and coding properly? Thank goodness for MSDN. After some research into the normal Form.Show() method call we found out that if we didn't set the form visibility to true, then some windows events wouldn't get processed. So as soon as we added this.Visible = true to the end of our "2 part constructor" every thing seems to run like it is supposed to.

Thursday, October 11, 2007

Culture, Globalization and Headaches: International Application Development

If you will never develop software that is to be used in any location except North America, then no need to read this post. However the reality of the software industry is that the global economy rules and with the internet, even free software has become a global product. Though you may not have immediate plans for releasing your software to other countries that your own, it is prudent to consider globalization in all phases of development.

The application that we are currently working passes XML messages back and forth between processes, computers and servers. When we lined up a beta customer from Germany, we discovered that while we had thought about globalization we didn't realize how deep the hole really was. We always thought that we'd just go back and add other language translations. But it is never that simple is it?

As we've discovered, all communications that can cross language barriers must be done in invariant culture. This is especially important for our product because XML is essentially a big string and strings can be interpreted in different ways in different cultures. By keeping all communication invariant you ensure that things like numbers and dates are interpreted properly when they get to their destinations.

Take the invariant representation of the number 1234.998 for example. In the US english (en-US) culture the numer would be displayed as 1,234.998. In german from Germany (de-DE) the number would be displayed as 1.234,998 (the germans use a "," for the decimal separator and a "." as the thousands separator). Interesting isn't it? One can see how globalization and localization could cause some very serious and big problems. If the number was stored in the en-US culture and then intrepreted in the de-DE culture, the number would possibly become 1.234998 in invariant culture.

Consider globalization and localization at every stage of development. Some c# methods that we've found useful are listed below.

Going from invariant string representation to numbers

public static decimal ParseDecimalInvariant(string value)
{
return decimal.Parse(value, System.Globalization.NumberStyles.Number, System.Globalizaton.CultureInfo.InvariantCulture.NumberFormat);
}
public static double ParseDoubleInvariant(string value)
{
return double.Parse(value, System.Globalization.NumberStyles.Number, System.Globalizaton.CultureInfo.InvariantCulture.NumberFormat);
}
public static int ParseIntInvariant(string value)
{
return int.Parse(value, System.Globalization.NumberStyles.Number, System.Globalizaton.CultureInfo.InvariantCulture.NumberFormat);
}
public static long ParseLongInvariant(string value)
{
return long.Parse(value, System.Globalization.NumberStyles.Number, System.Globalizaton.CultureInfo.InvariantCulture.NumberFormat);
}

Going from numbers to invariant culture string representation

public static GetInvariantString(byte value)
{
return value.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
}
public static GetInvariantString(intvalue)
{
return value.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
}
public static GetInvariantString(long value)
{
return value.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
}
public static GetInvariantString(decimal value)
{
return value.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
}
public static GetInvariantString(double value)
{
return value.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
}
Also check out MSDN for info on globalization classes (System.Globalization Namespace) and articles such as this one CLR Inside Out: Windows Vista Globalization Features

Wow. That was long.

Just remember: "You can pay now or you can pay later, but sooner or later you still have to pay."

Tuesday, October 02, 2007

Install A .Net Service Via Command Line

I've been working with .Net services for the last year or so and it is often convenient for debugging to be able to install your debug version. For us we have a number of services that interact with each other. So we install the service we aren't working on, but that the one we are working depends on, and debug using the IDE. Since it is a major pain to build an installer every time we want to test, we used the command line utility InstallUtil.exe that comes with the .Net Framework to install and uninstall our services in batch files.

If you've installed .Net in the default location InstallUtil.exe lives in C:\Windows\Microsoft.Net\Framework\v2.0.50727\InstallUtil.exe
in the respective framework version directory.

Below is an example batch file.
C:\Windows\Microsoft.Net\Framework\v2.0.50727\InstallUtil.exe C:\Dev\Server\SourceCode\bin\Debug\server_service.exe
pause
and to uninstall, use the /u flag
C:\Windows\Microsoft.Net\Framework\v2.0.50727\InstallUtil.exe /u C:\Dev\Server\SourceCode\bin\Debug\server_service.exe
pause
Using these batch files we just create short cuts to them and can double click the short cut to install or uninstall. Of course you can install/uninstall more than one service in the batch file.

Be sure to also check out the other flags available for InstallUtil.

Monday, October 01, 2007

Detect Desktop DPI Settings

Our application was working great. We were using GDI+ to draw custom windows. I've now fought more with GDI+ and half know more than I ever wanted to know about window drawing X_x. I had thought that I would never have to touch that code again for a long time. Things were great until our awesome QA Army of One decided to change from their default resolution of 96 dpi to 120 dpi and then to old school 72 dpi settings "just to see" and did we ever see! There were big black holes all over the place, text was getting cut off and our image banners were all the wrong size plus they were not crisp and clear.

We needed to check for dpi settings and initialize our custom window drawing routines accordingly. I was hoping to heaven that someone at the Microsoft .NET Framework development team had thought of something for that. My prayers were answered by the DpiX and DpiY properties of the System.Drawing.Graphics object. The following is a quick C# example for using these properties. What it does is check to see what resolution the desktop is set at and retrieves an image at that resolution with a method called GetImage. And in the special case that the desktop is running some really strange dip setting we do the else case which takes the 120 dpi image and scales it appropriately.
System.Drawing.Bitmap bmp;
System.Drawing.Graphics g = this.CreateGraphics();

if ((g.DpiX = 96) && (g.DpiY = 96)) // this is now the defacto resolution for most systems
{
bmp = (Bitmap)GetImage("banner(96dpi)");
}
if ((g.DpiX = 72) && (g.DpiY = 72))
{
bmp = (Bitmap)GetImage("banner(72dpi)");
}
else if ((g.DpiX = 120) && (g.DpiY = 120))
{
bmp = (Bitmap)GetImage("banner(72dpi)");
}
else // this is where we take care of the really strange stuff
{
System.Drawing.Image img = GetImage("banner(120dpi)");
Size sz = new Size((int)(img.Width * (g.DpiX / img.HorizontalResolution)),
(int)(img.Width * (g.Dpiy / img.VerticalResolution)));
bmp = new Bitmap(img, sz);
}

So ends another adventure... except we need to apply this to all our forms that have image banners. Yay.