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

Thursday, March 10, 2011

Updating the MDI parent menu icon for maximized child form

While working on a C# .NET program recently, I ran up against an infamous (and another apparently long-standing) bug in Microsoft's Multiple Document Interface (MDI) architecture. When a child form has been maximized, and its window icon is updated, the icon that is merged into the MDI parent's menu strip is not updated to reflect the change.

I was using the child window icon to identify the document type being displayed and to mark when it had been modified. Everything worked fine when the window was open in Normal mode, but the icon stopped being updated when the child was maximized to fill its parent's client area.

After literally days of searching the web to see if anybody else solved the problem, I found answers ranging from "impossible" to "keep track of your own windows" (a.k.a. the old roll-your-own interface standby). Digging through the layers of classes with the debugger, I figured out a hack that seems to work.

Here's the basis of my solution: when a child window is maximized, the controls on its title bar are merged with its parent window. The parent menu inserts an image as its first element that is a copy of the child window's icon. The image (a Bitmap image) is copied only at the time the child is maximized, and is never updated afterward, regardless of calling Refresh(), Update(), Invalidate(), etc. So, to get around this missing functionality, my solution was to check the parent menu when I changed the icon of the child window; if it had an image in its first element, I converted the Icon into a Bitmap and replaced the image. It may sound hokey, but it worked.

These are the steps I used for updating the icon. I raised an event to update the parent form.


// Change the child icon according to the document state.
// Called from a method in the child form.
int newState = this.GetChildState();
this.Icon = (newState == 0) ?
myProject.Properties.Resources.savedicon :
myProject.Properties.Resources.modifiedicon;

// ...raise event to notify the parent. sender is the child form.

// Change the parent menu icon when child is maximized.
// Called from an event handler in the parent form.
if (MainMenuStrip.Items[0].Image != null)
{
// Copy the new state icon from the child window.
MainMenuStrip.Items[0].Image = ((Form)sender).Icon.ToBitmap();
}


The MDI architecture has its quirks. This is just a way to get around one of them. I think this is an inelegant solution to an inelegant problem, but sometimes you just have to be practical.

Monday, November 15, 2010

Differences between enum in C# and C/C++


Recently I encountered a confusing situation that took me by surprise while porting some old, well-proven C++ code to C#: the languages treat enumerated types differently. In C and C++, an enum defines a value type that represents a restricted set of values. An enum type can be declared just about anywhere before it is used, including statement blocks.

In C#,  an enum defines a class (i.e. types are classes), meaning it can be declared only at the namespace or class level; it cannot be defined in a statement block within a method as it can in C++. If you try, Visual Studio issues an error not about the enum definition itself, but instead at the opening brace of the method, stating '} expected' rather than something useful like 'enum cannot be defined at statement level' or even 'invalid statement'. Another unintuitive error message brought to you courtesy of Microsoft.

So if you're a C++ programmer learning C#, beware this trap. It's easily fixed by moving (and naming or renaming) your enumeration to a less narrowly scoped portion of your module, but it's annoying and unwelcome, nonetheless.

Thursday, June 10, 2010

Error: ShowDialog exits immediately

I was trying to implement a WinForm login class called from a menu navigation class, but when I called ShowDialog(), it was exiting immediately without displaying the window or setting the DialogResult code. It was working the first time through the loop, but subsequent calls always failed. I searched all over the 'net for answers without success; in the end, I had to trace through my program a few lines at a time in the debugger until I found the culprit.

Watching the behavior of the preparation call to Login.ShowDialog() resulted in everything appearing normal the first time. Later, the Owner property would not allow assignment - no error, but the value stayed null - and calling ShowDialog() would return immediately with the default value of DialogResult.None. My Load event was even being called as expected. To me, this appeared to be working incorrectly. It turned out to be not incorrect, but undefined behavior I was seeing.

As the stereotypical engineer, I nearly outsmarted myself by designing a library of classes that would handle the details of performing their appropriate menu operations and allow me to define the menus declaratively. One of the classes was responsible for calling a named method and instantiating its type object through reflection if needed. To allow a ShowDialog() method call, the .NET Framework requires the object to be manually disposed afterward. I got fancy and added code to call Dispose() on the object automatically if it was a Form. But...some of my called methods were part of my active menu navigation class, which was itself a Form. When the menu operation was executed, my navigation class got destroyed, so I was clobbering myself.

The resolution was to be careful to not dispose of the class I was actively using. Assigning a disposed Form as the owner of another Form is undefined, pointless, and creates bizarre results, as I so painfully found. Fixing this issue solved the inconsistent behavior. I'm deservedly embarrassed by my mistake, but wiser for it.

If this saves somebody else the inconvenience I experienced to track down this problem, then it will have been worth my time to share this story. Leave me a comment if this helped you in any way.