Bug in der Windows UI: SetRedraw verändert WS_VISIBLE Stil in einem RTF Control

Ich habe eine relativ komplexe UI, die auch dynamisch Controls erzeugt. In diese Controls werden auch zum Teil Massen an Daten hineingeschoben. Damit alle Controls zeitgleich erst die Daten präsentieren verwende ich eine einfache Methode, die aus alten Windows Tagen stammt: CWnd::SetRedraw/WM_SETREDRAW. Man verwendet diese Nachricht zum Beispiel um das Flackern von Listboxen und Comboboxen zu verhindern, wenn man viele Daten einfügt.
Diese Nachricht wird von allen Fenstern unterstützt oder sollte unterstützt werden 😉

Meine Software macht nun folgendes:

  • Zuerst hat meine Ladeprozedur für die Daten, zuerst alle Controls erzeugt, oder überflüssige vernichtet und positioniert, oder evtl. nur ausgeblendet (ShowWindow(SW_HIDE). D.h. nach dem ersten Laden der Daten ändert sich am Layout evtl. nichts mehr.
  • Anschließend wurde an alle Controls CWnd::SetRedraw/WM_SETREDRAW mit FALSE gesendet.
  • Dann die Daten geladen.
  • Nach dem Laden wird einfach wieder CWnd::SetRedraw/WM_SETREDRAW mit TRUE gesendet und ein Invalidate durchgeführt.

Das funktioniert für alle Controls, mit einer Ausnahme: Das RTF Control. Wenn man WM_SETREDRAW TRUE an ein RTF Control sendet, das nicht sichtbar ist, dann wird dieses sichtbar. Der Stil WS_VISIBLE wird also verändert. 😮

Um das Problem zu isolieren habe ich hier ein kleines Testprogramm geschrieben. Der kritische Code sieht so aus. Das gesamte Projekt kann man hier auch herunterladen: Demoprojekt.

void CTestRTFSetRedrawDlg::OnBnClickedBtDoit()
{
 bool bWasVisible = (m_wndEdRTF.GetStyle() & WS_VISIBLE)!=0;
 m_wndEdRTF.SetRedraw(FALSE);
 m_wndEdRTF.SetWindowText(_T("Line 1\r\nLine 2\r\nLine 3\r\nLine 4"));
 m_wndEdRTF.SetSel(0,0);
 m_wndEdRTF.SetRedraw(TRUE);
 m_wndEdRTF.Invalidate();
 bool bIsVisible = (m_wndEdRTF.GetStyle() & WS_VISIBLE)!=0;

 // Check if the visible state changed
  if (bIsVisible!=bWasVisible)
  AfxMessageBox(_T("The visible state of the RTF control changed!"));
}

Nachtrag 16.01.2010 (Danke Sven für Deinen produktiven Kommentar):
Auch andere Controls wie Button-, Static– und Edit-Controls verändern den Visible Status wenn WM_SETREDRAW angewendet wird. Einzig Listbox– und Combobox-Controls behalten den Visiblestatus korrekt bei ❗

5 Gedanken zu „Bug in der Windows UI: SetRedraw verändert WS_VISIBLE Stil in einem RTF Control“

  1. Das scheint ein Standardverhalten in der DefWndProc zu sein. Ich habe in Deinen Testdialog mal ein CWnd eingebaut, das in OnInitDialog erstellt wird. Mit diesem passiert das gleiche wie mit dem RTF-Control.

  2. Mein Code nutzt Statics, einige von CWnd abgeleitete Klassen. List-Views, Edit Controls, Buttons, Comboboxen, also eigentlich fast alles was was es so gibt.
    Das Verhalten hat sich bei mir nur in dem RTF Control gezeigt.

    Aber zumindest ist es strange…

  3. Hi Martin,
    bei Deinen Stichworten „komplexe UI“ + „Daten in die Controls laden“ + „WM_SETREDRAW“ denke ich unweigerlich an den von Microsoft bestätigten Bug zumindestens im TreeView-Control:
    Microsoft KB 130611: „BUG: Using WM_SETREDRAW w/ TreeView Control Gives Odd Results“
    http://support.microsoft.com/kb/130611

    Vielleicht kannst Du da einen Zusammenhang mit Deinem beobachteten Problem knüpfen?
    Jedenfalls hoffe ich Dir, dadurch einen Hinweis geben zu können, um „under the hood“ zumindestens einen potentiellen Zusammenhang mit den identischen bzw. ähnlichen Windows-internen Vorgängen aufzuspüren.
    Betrachte meinen Beitrag bitte nur als einen Hinweis aus der Sicht eines „Laien“. Ich denke, als MVP hast Du da sicherlich mehr Möglichkeiten als meiner einer, um an die nötigen Hintergrundinformationen ranzukommen.

    Gruß
    Martin

  4. Hast Du testweise einmal ein einfaches CWnd in den Dialog Deines Testprogramms eingebaut? Möglicherweise überschreiben die Standardcontrols WM_SETREDRAW und behandeln die Nachricht selbst.

    Warum schickst Du WM_SETREDRAW an alle untergeordneten Controls? Es reicht doch auch, das gemeinsame Elternfenster mit SetRedraw zu schützen. Alle untergeordneten Fenster werden dann automatisch von der Aktualisierung ausgenommen. Nach dem zweiten SetRedraw kommt dann ein RedrawWindow mit RDW_ALLCHILDREN.

  5. 2x ja Sven!

    1. Tatsächlich ändert sich auch für ein Edit Control oder ein Static Control der WS_VISIBLE Status! ListBox und ComboBox verhalten sich korrekt.
    2. An WM_SETREDRAW für das Parent habe ich nicht gedacht.
    Stimmt. Das ist die eleganteste Form.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.