You are on page 1of 25

.

NET Windows Development


Everyday Tips, Tricks & Optimization

Alberto Población
Notice of Liability
The author and publisher have made every effort to ensure the accuracy of the
information herein. However, the information contained in this book is sold without
warranty, either express or implied. Neither the authors and Krasis Consulting S.L.,
nor its dealers or distributors, will be held liable for any damages to be caused either
directly or indirectly by the instructions contained in this book, or by the software or
hardware products described herein.

Trademark Notice
Rather than indicating every occurrence of a trademarked name as such, this
book uses the names only in an editorial fashion and to the benefit of the trademark
owner with no intention of infringement of the trademark.

.NET WINDOWS DEVELOPMENT - EVERYDAY TIPS, TRICKS &


OPTIMIZATION

© KRASIS CO NSULTING S.L. 2 0 1 0


www.krasis.com

ALL RIGHTS RESERVED. NO PART O F THIS BO O K MAY BE REPRO DUCED, IN ANY


FO RM O R BY AN Y MEANS, WITHO UT PERMISSIO N IN WRITING FRO M THE
PUBLISHER.

ISBN: 978-84-935489-3-3
PRINTED IN THE UNITED STATES O F AMERICA
Acknowledgments
First, I would like to give my thanks to Jose Manuel Alarcón, who suggested that I should
write this book, followed its progress, and encouraged me to go on until it was complete.
Before I started writing this book, I had in my hands a lot of short texts and coding examples
that I had created while answering questions in public forums or presenting examples in
training courses. I give my thanks to the people who asked the questions that prompted me to
create these examples and those who participated in the discussions that allowed me to refine
them.

Some of those pieces have served me as inspiration more than once, since I based on them a
few examples presented in this book and they also supported a few short articles that I
published in the magazine “dotNetMania.” I would like to give a special thank-you to its
editor, Paco Marín, who gave me permission to reuse here some of those ideas.
And last, but not least, I thank my wife, who encouraged me while I was writing and helped
me find the time to write when other obligations seemed to always keep me away from this
task.
Contents

FOREWORD ........................................................................................................... ix
PREFACE ................................................................................................................ xi

ACCESSING DATA ...................................................................................................... 1


1.- introduction .................................................................................................................................. 1
2.- Using Multiple Active Record Sets .......................................................................................... 1
2.1.- The problem...................................................................................................................... 2
2.2.- First solution ..................................................................................................................... 3
2.3.- Another solution .............................................................................................................. 4
2.4.- The MARS solution ......................................................................................................... 5
3.- Using Asynchronous server calls ............................................................................................. 6
3.1.- The problem...................................................................................................................... 7
3.2.- First solution: Launch a Thread .................................................................................... 8
3.3.- An alternative: Use the ThreadPool ......................................................................... 12
3.4.- Another alternative: Asynchronous delegates ....................................................... 13
3.5.- At last: Asynchronous ADO.NET methods............................................................ 15
3.6.- A further alternative: BackgroundWorker ............................................................. 17
3.7.- And yet another one: The Task class ....................................................................... 18
4.- Importing large amounts of data ............................................................................................ 19
4.1.- The problem.................................................................................................................... 20
4.2.- The solution .................................................................................................................... 21
4.3.- Further improvement ................................................................................................... 22
4.4.- Mapping columns ............................................................................................................ 22
5.- Efficiently accessing Large Objects in the database ........................................................... 23
5.1.- Reading LOBs from a database .................................................................................. 24
5.2.- Writing LOBs to a database ........................................................................................ 27
6.- Beware of SQL Injection attacks ........................................................................................... 29

NEW FEATURES IN WINDOWS 7 .............................................................................. 35


1.- introduction ................................................................................................................................ 35
2.- The Windows API Code Pack................................................................................................ 35
3.- Controlling the new Taskbar .................................................................................................. 37
3.1.- Examining the taskbar ................................................................................................... 38
3.2.- Configuring the AppId .................................................................................................. 38
3.3.- The Jump List .................................................................................................................. 39
3.3.1.- Associating the file types with the application ............................................... 41
3.3.2.- Programming the Jump List ................................................................................ 42
3.3.3.- Tasks in the Jump List .......................................................................................... 43
3.4.- Icon overlays ................................................................................................................... 45
3.5.- Displaying progress in a taskbar button ................................................................... 45
vi .NET Windows Development Everyday Tips, Tricks & Optimization

4.- Using Libraries ............................................................................................................................ 47


4.1.- Using the Common File Dialogs ................................................................................ 49
4.2.- Consuming the contents of libraries ........................................................................ 51
4.3.- Manipulating libraries .................................................................................................... 52
5.- Elevating privileges .................................................................................................................... 55
6.- Enabling Federated search on your application data ........................................................ 62
6.1.- The search handler ........................................................................................................ 64
6.2.- The image handler ......................................................................................................... 68
6.3.- The information page.................................................................................................... 69
6.4.- The search connector .................................................................................................. 71

USING GRAPHICS ..................................................................................................... 73


1.- introduction to GDI+ ............................................................................................................... 73
1.1.- First example .................................................................................................................. 73
1.2.- The System.Drawing namespace ............................................................................... 74
1.3.- The Graphics Object .................................................................................................... 75
1.4.- Pens ................................................................................................................................... 76
1.5.- Brushes ............................................................................................................................. 77
1.6.- Fonts ................................................................................................................................. 78
1.7.- Colors ............................................................................................................................... 79
1.8.- The Point and Size Structures .................................................................................... 79
1.9.- The Rectangle Structure .............................................................................................. 80
1.10.- Drawing Lines ................................................................................................................ 81
1.11.- Drawing Rectangles and other shapes .................................................................... 82
1.12.- Drawing Pie Charts ...................................................................................................... 84
1.13.- Drawing Bitmaps and Icons ....................................................................................... 85
1.14.- Summary ......................................................................................................................... 85
2.- Displaying Graphics on Windows Forms ............................................................................ 85
3.- Creating a Control that paints itself ..................................................................................... 89
4.- Using Owner-Draw mode in existing controls ................................................................. 93
5.- Custom Printing with GDI+ ................................................................................................... 99
6.- Dynamic graphics in a Web Page ........................................................................................ 109

CREATIVE USE OF SERIALIZATON ........................................................................... 115


1.- General introduction to Serialization ................................................................................. 115
1.1.- Runtime Serialization .................................................................................................. 116
1.2.- XML Serialization ......................................................................................................... 119
2.- Using Serialization to save the state of a form ................................................................ 123
2.1.- A simple approach ....................................................................................................... 124
2.2.- A more general solution ............................................................................................ 126
3.- Cloning objects by means of Serialization ......................................................................... 132
3.1.- Copying Reference-types........................................................................................... 132
3.2.- Cloning ........................................................................................................................... 133
3.3.- Applying Serialization .................................................................................................. 137
4.- Using XML Serialization to read and write XML files that conform to a schema .. 137
5.- Storing XML in SQL Server .................................................................................................. 143

SOME USES FOR REFLECTION ................................................................................. 151


Contents vii

1.- General introduction to Reflection ..................................................................................... 151


1.1.- Metadata ......................................................................................................................... 151
1.2.- Assemblies ..................................................................................................................... 155
1.3.- Types in an Assembly.................................................................................................. 155
1.4.- Examining Types ........................................................................................................... 156
1.5.- Obtaining a System.Type............................................................................................ 158
1.6.- Creating an instance of a Type ................................................................................. 158
1.7.- Binding and Invocation ................................................................................................ 161
2.- Solving Assembly Load Problems ........................................................................................ 163
2.1.- The Fusion process ..................................................................................................... 163
2.2.- Locating a Referenced assembly .............................................................................. 164
2.3.- Adding a Strong Name ............................................................................................... 166
2.4.- Other options in the configuration file .................................................................. 168
2.5.- Debugging the Fusion process .................................................................................. 169
3.- Loading and invoking Assemblies dynamically .................................................................. 171
4.- Compiling expressions on-the-fly ........................................................................................ 177
5.- Loading and Unloading Assemblies with AppDomains .................................................. 181
6.- Instancing classes from a string ............................................................................................ 184

VARIOUS TECHNIQUES THAT SHOULD BE WELL-KNOWN, BUT SOMETIMES AREN’T.... 189


1.- Reading and writing text files with non-English characters........................................... 189
1.1.- Finding out the problem ............................................................................................. 189
1.2.- What is going on? ........................................................................................................ 190
1.3.- Using Encodings ............................................................................................................ 192
1.4.- Performing the conversions explicitly .................................................................... 195
2.- Ensuring that a form is only opened once ......................................................................... 195
3.- Communicating from a control to its container ............................................................. 198
3.1.- The wrong approach ................................................................................................... 199
3.2.- The right way to do it ................................................................................................. 200
4.- Locking records in a database .............................................................................................. 205
5.- Never forget to close your open connections ................................................................ 209

A FEW TIPS FOR WINDOWS FORMS APPLICATIONS ............................................... 213


1.- Displaying an icon in the Notification Area ...................................................................... 213
1.1.- The NotifyIcon component ....................................................................................... 213
1.2.- Balloon Tips ................................................................................................................... 215
1.3.- Context Menu .............................................................................................................. 216
2.- Complex Resizable Forms ..................................................................................................... 218
2.1.- Using code to rearrange the contents of the form ............................................. 219
2.2.- The Anchor and Dock properties ........................................................................... 220
2.3.- The TableLayoutPanel control ................................................................................. 222
2.4.- The FlowLayoutPanel control ................................................................................... 225
3.- Using Dialogs ............................................................................................................................ 227
3.1.- Traditional dialogs........................................................................................................ 227
3.2.- Task dialogs in Windows 7........................................................................................ 232
4.- Non-Rectangular Forms ......................................................................................................... 236
5.- Processing all the Controls in a Form ................................................................................ 240
5.1.- Implementing a first example .................................................................................... 241
viii .NET Windows Development Everyday Tips, Tricks & Optimization

5.2.- A more complex example ......................................................................................... 244


INDEX .................................................................................................................. 247
Foreword

The Microsoft .NET Framework contains thousands of libraries to help with just
about any modern development task. You can use the .NET Framework to access data,
create a user interface, run algorithms or consume a service. The challenge as a .NET
developer becomes to learn what types of libraries are available, how to use them and
when. This book helps guide Windows® developers through common questions for a
variety of common .NET development scenarios.
Why focus on Windows development? With more than a billion clients in the world
today, Windows is by far the largest available platform. Windows 7, in particular, has
been the fastest growing and most popular release of Windows yet. See the Windows 7
chapter in this book to learn how your applications can light up on this huge, growing
platform. A number of the tips in this book are also relevant to other .NET application
types.
As a Visual Studio community program manager, I have the opportunity to interact
with many customers, including the Microsoft Most Valuable Professional (MVP)
program. The MVP Program honors Microsoft technology experts for their impact in
the community. Alberto Población has been awarded as a C# MVP because of his great
impact teaching others about .NET development, both as a trainer as well as a top
answerer in the newsgroups and forums.
Tips and tricks content is always incredibly popular because of its immediate
practical use. It is also very personal, based on the author‟s experience and
prioritization. Alberto‟s selections in this book draw on nearly 30 years of development
and reflect the top questions raised in his interactions with .NET users. Reading this
book will help you expand your toolset, complete tasks more quickly, and write better
and more resilient code. Alberto‟s writing style is easy to follow and engaging to read.
You can keep it on your shelf as a reference or read it cover to cover as a novel.
So, pack this guide with you, give it a read and add some new tricks to your
toolbox!

Lisa Feigenbaum
Community Program Manager
Microsoft Visual Studio

ix
Preface

ABOUT THIS BOOK

Most software developers typically spend their time developing line-of-business


(LOB) applications, such as accounting, inventory or payroll. These applications
usually involve presenting forms that capture data, save the data to a database, and
recover and display the data, as well as generating lots of reports.
Occasionally, a different feature will be needed in a program. It may be necessary to
display a custom graphic, or to generate a specialized report that cannot be generated
by the standard reporting engine of choice. Perhaps we require a universal mechanism
for saving the state of all our forms, or we might wish to enhance our user interface by
means of some of the features in the latest version of Windows. Any of the required
techniques would probably be straightforward for someone who specializes in such
tasks, but may not be evident for a developer of LOB applications who spends most of
his or her time developing code to display forms that access a database.
In this book, we intend to present a few useful techniques for performing various
tasks that in many cases will not be familiar to our intended audience, but may come
handy when some new feature or enhancement is needed in a LOB application.
Some of our chapters present features of the Framework that are not widely known
due to their very specialized use. In other cases, we present features that in our opinion
should be well known; however, when examining the questions that are frequently
repeated in developer forums, and the small number of replies that such questions
receive, we conclude that these topics are not as well known as they deserve.
Therefore, we have chosen to also cover some of these “simpler” topics.

ABOUT THE CODING EXAMPLES

Most of our examples are based on Windows Forms applications, although we do


include a few tips that are specific to Web Forms. However, the majority of the topics
that we present refer to the usage of the Framework Class Libraries (FCL). These
libraries operate in the same way regardless of the technology that is used for the user
interface, and therefore the techniques that we present can be used not only in
Windows Forms, but also in Web Forms, ASP.NET MVC, Windows Presentation
Foundation, or even Silverlight applications.
The examples in this book are written in C#. However, since they deal with the
FCL, most of the concepts that we present are the same regardless of the language that

xi
xii .NET Windows Development Everyday Tips, Tricks & Optimization

you use to access them. Therefore, they should be useful for developers writing code in
VB.NET or any other .Net language.
If you wish to run the examples without having to type them from the book, you can
download the completed sample projects from the website of the publisher.
Most of the projects were done with Visual Studio 2008, but will migrate without
effort to Visual Studio 2010. A small number of the samples, mainly those that present
some of the latest features in Windows or in Visual Studio, were done with Visual
Studio 2010, and are not directly compatible with earlier versions.

And now, with no further ado, let‟s go ahead into the first chapter.
6
CHAPTER

Various techniques that


should be well-known,
but sometimes aren’t

This chapter deals with some miscellaneous subjects that, supposedly,


should be familiar to all developers. However, after having examined lots of
code and answered questions from many developers, we have concluded that,
unfortunately, these matters are not as obvious or as well-known as would be
expected.

1.- READING AND WRITING TEXT FILES WITH NON-


ENGLISH CHARACTERS

Many developers whose native language is English never deal with text that
contains characters other than those in the ASCII table. This encoding uses 7 bits to
codify a set of symbols, numbers and all the letters that are used in English. However,
other languages use additional characters such as letters with diacritical marks (áèü) or
additional symbols (åøñ). And we are not even speaking about non-Latin alphabets
such as Greek, Russian or Chinese.

1.1.- Finding out the problem

A typical way to read a text file uses a StreamReader, in a way that is similar to
what we see in the following code fragment:

using System.IO;
...
using (StreamReader sr = new StreamReader(filename))
{
string s = sr.ReadLine();
textBox1.Text = s;
}
189
190 .NET Windows Development - Everyday Tips, Tricks & Optimization

This will work well with a file that only contains ASCII characters. But let‟s do an
experiment. Open Notepad, type-in a sentence that contains some foreign characters,
and save it into a file on disk. For instance:

Figure 1.- Non-English text in Notepad

Note: if you are using a U.S. keyboard, it does not directly generate any of these
special characters. You can insert them from the “Character Map” utility that is usually
found under Start -> All Programs -> Accessories -> System Tools. You can also press
the Alt key, enter the numeric code for the character on the numeric keypad, and
release the Alt key. For instance, the “é” character that we used can be entered as
Alt+130.
Now, run the piece of code above, so that it reads the file that was saved in
Notepad. You will note that the non-ASCII characters come out wrong:

Figure 2.- The text that is read from the file

As you can see, the textbox shows small rectangles in place of the “special”
characters.

1.2.- What is going on?

Notepad saved the file using an encoding named “ANSI” (the “Save As...” dialog
offers other choices, but ANSI is selected by default). This encoding uses 8 bits to
encode each character. The first 127 characters match the ASCII table, so there is no
problem when reading pure English text. But the “top” 128 characters represent various
symbols such as the “é” and “¿” that we typed in our sample. The specific mapping of
characters depends on the regional version of Windows. The example above was run
Various techniques that should be well-known, but sometimes aren’t 191

on an operating system that was using the “Western European” character set, also
known as “Windows-1252”.
If you wish to see the actual codes that were written into the file, you can open it
with a binary editor. You can do this from Visual Studio if you add the file into the
project. Then, right-click on it in Solution Explorer and select Open With -> Binary
Editor.

Figure 3.- Hexadecimal content of the ANSI text file

Here you can see, for instance, that the “é” was encoded as 0xE9.
All Strings in .Net are Unicode, and they are stored in memory using the UTF-16
encoding, which encodes each character in 16 bits (well, this is only true for characters
in the Basic Multilingual Plane, but in practice it is unlikely that you will be using any
other of the Unicode planes). This is enough for encoding most of the characters in all
languages, so you do not have to worry about encodings when manipulating character
strings inside your programs.
At some point, when you are reading the 8-bit data from the file into the Unicode
String in memory, there has to be a conversion that “translates” the characters from one
encoding into the other. When reading the file with a piece of code like the one in our
first example, the StreamReader itself performs the conversion. By default, it tries to
interpret the file as UTF-8. This is an encoding that stores Unicode characters using 8-
bit data. The first 127 characters are stored in a single byte, and they match the ASCII
table, so this process is not apparent when reading English text. However, “special”
characters are stored using more than one byte. The first byte serves as a sort of
“prefix” to indicate that something special comes next. This is what our file would
contain if saved as UTF-8:

Figure 4.- Hexadecimal content of the UTF8 text file

As you can see, the “é” is now encoded as 0xC3 0xA9, which is quite different from
its ANSI encoding.
When the StreamReader attempts to interpret the file according to the UTF-8
encoding, but instead finds characters encoded as ANSI, it fails to interpret them
correctly. Sometimes, a character is misinterpreted so that a different character is
shown instead. In other cases, the specific combination of bits may not even
correspond to any character in the target encoding, so a small rectangle is displayed (as
we saw earlier) to indicate the error.
192 .NET Windows Development - Everyday Tips, Tricks & Optimization

1.3.- Using Encodings

In order to interpret correctly the contents of the file, we need to tell the
StreamReader which is the encoding for the file that it is reading. This can be done by
means of a parameter of type System.Text.Encoding that can be passed to the
constructor:

using System.Text;
...
Encoding encoding = Encoding.GetEncoding(1252);
using (StreamReader sr = new StreamReader(filename, encoding))
{
string s = sr.ReadLine();
textBox1.Text = s;
}

The preceding code would display properly the content of the file, if it was written
using the Western European (“Windows-1252”) character set.
The Encoding class has static methods that provide the most usual encodings, such
as Encoding.UTF8 or Encoding.ASCII. It also has a GetEncoding method that can
obtain an encoding from its number (as shown above) or from its name, such as
Encoding.GetEncoding("Windows-1252").
There is no reliable way to determine the encoding that was used to create a file.
You need to be familiar with the application that created the file, and the platform on
which it was run, so that you can deduce the Encoding that you need to specify when
opening the file. If you use the wrong encoding, the characters will come out all wrong.
For instance, if you thought that our example ANSI file was created on a Greek version
of Windows, you might try to open it using encoding “Windows-1253” (which is the
Greek encoding). The code would be exactly the same except that you would change
the number for the encoding:

Encoding enc = Encoding.GetEncoding(1253);

When running the program, the wrong characters would be displayed:

Figure 5.- Wrong text read from the file


Various techniques that should be well-known, but sometimes aren’t 193

The same binary codes in the file on disk have now been interpreted as Greek
characters, since we told the StreamReader to use that encoding; this is why we are
seeing an omega and an iota instead of the original characters that we wrote into the
file. Of course, an analogous failure when reading the text would occur if the file had
actually been written in Greek (1253) and we tried to interpret it as Western European
(1252). So it is important to specify the correct encoding when accessing the file.
Up to this point, we have talked about using .Net code for reading a file that was
created with an external tool, but the same applies when writing a file that needs to be
in a specific format so that it can be read properly by a different application. Normally,
you would write a text file with a StreamWriter:

using (StreamWriter sw = new StreamWriter(filename))


{
sw.WriteLine(textBox1.Text);
}

By default, the StreamWriter translates the Unicode string in memory (represented


by textBox1.Text in the preceding example) into an equivalent UTF-8 sequence of
bytes that is written into the file. If we run this code and then open the file with
Notepad, the text will display correctly because the file contains a prefix at the
beginning that specifies the way in which it is encoded (you can see the prefix in the
hexadecimal dump in figure 4). Notepad recognizes this prefix and interprets the file
accordingly. However, if the file needs to be open by an application that does not
understand this convention and tries to interpret the file according to a pure 8-bit
encoding, the result will be all wrong. For instance, this is what you see if you display
the file in a Command Prompt window:

Figure 6.- UTF8 file in a Command Prompt window

Notice how the file prefix is displayed as a sequence of strange characters and the
pair of bytes that encodes each of our special characters is interpreted as a pair of
different characters.
If we want the file to display correctly, we need to save it using the same encoding
that is expected by the program that is going to interpret it later. In this case, this could
be, for instance, encoding 850 (“Western European DOS”) or 437 (“OEM United
States”), depending of the version of Windows. The encoding is passed in the
constructor of the StreamWriter, similarly to what we did for the StreamReader:
194 .NET Windows Development - Everyday Tips, Tricks & Optimization

Encoding enc = Encoding.GetEncoding(850);


using (StreamWriter sw = new StreamWriter(filename, false, enc))
{
sw.WriteLine(textBox1.Text);
}

If we write the file using this code, it will display correctly in the Command Prompt
window, as shown in the following figure:

Figure 7.- Displaying a correctly encoded file

Of course, now it will fail to display correctly if it is opened with Notepad, since
this program expects a different encoding. Once more, we are reminded that it is
important to know which program is going to use our file, so that it can be saved with
the expected encoding.
So, which encoding should you use if you are writing a program that needs to save
text files and is later going to read them?
If you are in control of both the reading and writing processes, you can use any
encoding as long as it is the same one for reading and for writing. If you wish to
support the whole range of Unicode characters without loss of information, you should
use one of the encodings that support Unicode. If your text contains mostly ASCII
characters, UTF-8 is probably the best choice, since it will encode the majority of
characters using only one byte. It also happens to be the default if you do not specify
any encoding for the StreamReader and StreamWriter.
If the program needs to be compatible with legacy Windows applications, you
probably want to use one of the ANSI encodings. As we saw earlier, there are several
variations depending on the locale of the operating system. In most cases, you will
want your program to use the default encoding, so that the files are compatible with
other locally installed applications. You can get the operating system's current ANSI
code page by means of System.Text.Encoding.Default.

Encoding enc = Encoding.Default;


StreamWriter sw = new StreamWriter(filename, false, enc);
...

The documentation for the System.Text.Encoding class lists the available


encodings.
Various techniques that should be well-known, but sometimes aren’t 195

1.4.- Performing the conversions explicitly

In all the preceding explanations, we just passed an Encoding to the StreamReader


or StreamWriter and we let these classes perform all the character encoding and
decoding. If you are familiar with the operation of the classes in the System.IO
namespace, you know the StreamReader and StreamWriter merely serve as
intermediaries between your code and a Stream. The Stream reads and writes arrays of
bytes, and the StreamReader and StreamWriter translate those bytes into or from
Strings, applying the adequate encoding in the process.
Those arrays of bytes need not always come from a file. Maybe you are receiving
them from a TCP socket, or you need to save them into a BLOB in a database. In any
case, it is conceivable that you may need to write the code that transforms a String into
an array of bytes with a specific encoding, or vice versa. This can be done by means of
the methods GetBytes and GetString of the Encoding class.

Encoding enc = Encoding.UTF8;

//Convert string to byte[]


string theText = ...;
byte[] converted = enc.GetBytes(theText);

//Convert byte[] to string:


byte[] b = ...;
string s = enc.GetString(b);

To summarize: the next time that you write an application that might need to handle
international text, keep in mind that not necessarily every character maps to one byte,
and even if you can fit the whole character set that the application uses into 8 bits, there
are different ways to perform this mapping. Therefore, you will need to devote a little
bit of time to consider the Encoding that you will use, and apply it at the appropriate
places.

2.- ENSURING THAT A FORM IS ONLY OPENED ONCE

This is quite simple, but you would be surprised at the number of times that this
question appears in programming forums, even from people who are reasonably
experienced at developing windows forms applications.
Typically, the author of the question is writing an application that has a main form,
possibly an MDI container, which has a button or menu option for opening a second
form (usually, but not necessarily, an MDI child). Of course, opening the secondary
form is quite easy:

private void ShowNewForm(object sender, EventArgs e)


{
Form1 childForm = new Form1();
childForm.MdiParent = this;
childForm.Text = "Window " + childFormNumber++;
childForm.Show();
}
196 .NET Windows Development - Everyday Tips, Tricks & Optimization

By the way, this is (almost) the default code provided by Visual Studio when you
add a new MDI form to a Windows Forms project.
However, if you press repeatedly the button to open a new form, several copies of
the form will be opened, because each time a new instance of Form1 is created and
shown.
A first approach to avoid this situation is to keep a reference to the child form in a
class variable and verify whether it is initialized before opening a second copy of the
form:

private Form1 childForm = null;


private void ShowNewForm(object sender, EventArgs e)
{
if (childForm == null)
{
childForm = new Form1();
childForm.MdiParent = this;
childForm.Show();
}
else
{
childForm.Show();
if (childForm.WindowState == FormWindowState.Minimized)
childForm.WindowState = FormWindowState.Normal;
childForm.BringToFront();
}
}

If the reference is null, we create a new form and show it. If it is not null, we take
whatever actions are needed to make it visible (for instance, it could be minimized or
hidden beneath other forms). This works, but it has a drawback: What if the user closes
the form? The reference will still be not null, so the program will not open the form
again.
There are several possible solutions to this situation. The first is to check not only
whether the reference to the child form is null, but also if it has been disposed:

if (childForm == null || childForm.IsDisposed)


{
childForm = new Form1();
childForm.MdiParent = this;
childForm.Show();
}
else ...

This works, in general, but is not completely reliable because it depends on the form
being disposed when it is closed. However, there are some circumstances when this
does not happen. For instance, the documentation asserts that if an MDI form is hidden
and then its Close method is called, the form is not disposed.
Another alternative is to connect a handler to the FormClosed event, and use this to
clear the reference to the form:

private Form1 childForm = null;


private void ShowNewForm(object sender, EventArgs e)
Various techniques that should be well-known, but sometimes aren’t 197

{
if (childForm == null)
{
childForm = new Form1();
childForm.MdiParent = this;
childForm.FormClosed +=
new FormClosedEventHandler(childForm_FormClosed);
childForm.Show();
}
else
{
childForm.Show();
if (childForm.WindowState == FormWindowState.Minimized)
childForm.WindowState = FormWindowState.Normal;
childForm.BringToFront();
}
}
void childForm_FormClosed(object sender, FormClosedEventArgs e)
{
childForm = null;
}

Yet another option is to loop over all the child forms trying to find the one that we
wish to show. If found, we make it visible; otherwise, a new form is created:

private void ShowNewForm(object sender, EventArgs e)


{
foreach (Form f in this.MdiChildren)
{
if (f is Form1)
{
f.Show();
if (f.WindowState == FormWindowState.Minimized)
f.WindowState = FormWindowState.Normal;
f.BringToFront();
return;
}
}
Form1 childForm = new Form1();
childForm.MdiParent = this;
childForm.Show();
}

This is perhaps easier to understand, since it does not require connecting an event,
but it may be a little less efficient due to the need for looping over all the forms. Note
that this last method only works for MDI forms, while the previous ones would be
valid for any kind of form. If you want to use this method with any arbitrary forms, you
can substitute Application.OpenForms for this.MdiChildren.
It wasn‟t that difficult, was it? So, why are there so many developers who ask about
this subject? Well, a possible reason is that many of them are programmers who
previously developed applications with VB6 (or earlier), and have migrated to .Net.
Old VB6 had a feature that is no longer present in .Net: For every form in the project,
the compiler automatically issued a statement like this one:

Public Form1 As New Form1


198 .NET Windows Development - Everyday Tips, Tricks & Optimization

This led many developers to not make the distinction between the class and the
instance, because they always had a reference to an instance with the same name as the
class. Also, declaring something “...As New type” had an implication that no longer
holds true in .Net, namely, “every time the object is accessed, verify that it is not
Nothing, and if it is, automatically create a new instance”. Therefore, it was easy to
simply do “Form1.Show” at any time, and it always showed the same form. This will
not work in .Net, and it causes much confusion for VB6 programmers, including their
failure to understand why Form1.TextBox1 doesn‟t work. So the important lesson for
developers who have migrated in this way is that they need to understand the difference
between a class and an instance of that class, and that the name of a form cannot be
used implicitly to reference an instance of that form.

3.- COMMUNICATING FROM A CONTROL TO ITS


CONTAINER
This is another task that should be simple, but some developers try to solve it in
ways that are convoluted or inelegant. For illustration, we are going to work on an
example that consists of a Windows form that contains a user control, but the same
principles would also apply to a web page that contains user control or a server control,
or to a master page that hosts a content page.
Consider a form that contains a user control. The developer wants to perform some
action on the form when something happens on the control. For example, the control
contains a button, and the goal is to present some text in a textbox contained in the
form (outside of the user control) when a button in the user control is clicked.

Figure 8.- Example application