views:

168

answers:

1

Hi everyone,

I have a COM server app which uses some WinForms controls which, frankly, do not seem to be redrawing when their properties (Text, BackColor, etc) are changed.

I have tried invoking txtControlName.Invalidate() as well as .Update() and neither seem to be affecting anything.

It is a business requirement that we stick to .Net 2.0 and provide a visually responsive and interesting UI, and while I realize I can probably force redraws with WinAPI SendMessage() I would much rather have .Net handle all this stuff -- there are enough hacks in place and we don't want to add any more.

Finally, I wanted to note that the .Net COM server is hosted inside an unmanaged application.

Thanks!

Tom

Addendum:

At this time, the actual updating code looks like:

public void UpdateSt(int? seq, string text) {

 Control.CheckForIllegalCrossThreadCalls = true;

 if (this.lblText.InvokeRequired) {

  this.lblText.Invoke(new MethodInvoker(() => {

   UpdateSt(seq, text);

  }));

 } else {

  if (text != String.Empty) {

   lblText.Text = text;
   //WinAPI.InvalidateRect(lblText, true);
   lblText.Refresh();
   //WinAPI.SendMessage(lblText.Handle, (uint)WinAPI.WM.SETTEXT, 0, new StringBuilder(text));
   DebugTrace("lblText says '" + lblText.Text + "', is supposed to say '" + text + "'.");
  }

  if (imgSeq.HasValue) {

   // not implemented yet

  }

 }

}

Addendum #2:

Spy++ reports that the WM_SETTEXT call originating from .Net's Text setter is failing, much like my own WM_SETTEXT calls.

Addendum #3: SOLVED

Turns out the problem was a combination of a broken message pump and some P/Invoke calls that did more harm than good. As the library was started via COM there was no .Net message pump and adding Application.Run() inside a new thread allows everything to respond the way it should. It seems to be a good idea to make sure all forms-based interactions start from a thread with that call.

+1  A: 

The generic diagnostic is that there is something wrong with the message pump. You are not complaining that the controls do not paint themselves at all so it seems unlikely that this is completely broken. If this is an occasional painting problem then the diagnostic is that you've got a threading problem. In other words, you are updating the control properties, or calling Invalidate/Update, from the wrong thread.

Windows Forms has built-in diagnostics for this, active when a debugger is attached. Make sure you don't set the Control.CheckForIllegalCrossThreadCalls to false.

Next place to look is the message pump itself. When you display the forms with their Show() method instead of ShowDialog() then your unmanaged message pump will be dispatching messages. That has some undesirable side-effects by itself, keyboard accelerators won't work anymore, nor does tabbing. Check if the problem disappears if you use ShowDialog().


Your comment provides another hint at what might the problem. If you get False from InvokeRequired when you know that you're calling from another thread and you don't see any visible sign of your update then you are using the wrong Form object reference. Possibly one you created with the new operator. Make sure you use the existing one, Application.OpenForms[] can give you a reference if you have trouble getting one.

Hans Passant
Curiously, Control.InvokeRequired is consistently returning false. I've taken to manualling Invoke()ing the commands to update the controls and still, nothing is happening. Knowing this, is it safe to rule out any blatantly obvious threading issues?
Tom the Junglist
I updated my answer, below the line.
Hans Passant
To verify that, you could use IWin32WindowHandle to retrievce the HWND of the item you want to update, and compare that to the one found with Spy++.
peterchen
@nobugz -- Interesting, I was under the impression (and have done so without issue in the past) that one changed control properties by changing the instantiated form/control. If that is not the case then my mind may be temporarily blown.To be honest, I haven't *confirmed* that I am calling from another thread, almost hte entire addon runs from event handlers and they have always been in seperate threads in the past.
Tom the Junglist
@nobugz You also suggested that the message pump might be broken. I am unsure of how to verify this as I thought it was generally handled within the framework innards. How does one go about checking that?
Tom the Junglist
Not so sure, I like the "wrong form reference" explanation a lot better. It would however typically be the message pump in your unmanaged code that does the dispatching. How did the ShowDialog() angle work out?
Hans Passant
@nobugz No change with ShowDialog()
Tom the Junglist
@peterchen The handle that my program sees is equal to the correct item as shown in Spy++
Tom the Junglist
@nobugz -- the form hosting lblText was previously instantiated in the COM server's constructor and stored in a class-wide local var. I refactored that so that the class-wide var simply returns the results from `Application.OpenForm[]` and the original form object (with the `new FrmForm()`) is discarded. Updates to controls are still not appearing... oddly, this same behavior is showing up even in a seperate debugging app (written in .Net) that references the same COM server DLL.
Tom the Junglist