Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Message Routing in Winform-Applikationen

22 views
Skip to first unread message

Paul Werkowitz

unread,
Jul 30, 2007, 5:48:27 AM7/30/07
to
Hallo Netizens,

gibt es in bei WinForms eine Möglichkeit, eigene Nachrichten "von oben nach
unten" durchlaufen lassen zu können? Ich möchte nach Möglichkeit nicht
jeden Container mit Code zur Weiterleitung ausstatten. Ich denke mal, für
Cmd-Messages sollte es sowas ja bereits geben.

Ich brauche sowas, um den Aktivierungsstatus von globalen Kommandos (z.B.
cut/copy/paste) im Editmenü bestimmen zu können.

Greetz
Paule

Elmar Boye

unread,
Jul 30, 2007, 10:08:06 AM7/30/07
to
Hallo Paul,

Paul Werkowitz <newsg...@primaprogramm.de> schrieb ...

Schau Dir mal in Input und Command Konzepte der WPF an:
http://msdn2.microsoft.com/en-us/library/ms751593.aspx

die sollten sich - ansatzweise - auf die Windows Forms übertragen lassen.
Und kennen Konzepte wie "bubbling" und "tunneling" von Befehlen.

Gruss
Elmar


Frank Dzaebel

unread,
Jul 30, 2007, 2:52:19 PM7/30/07
to
Hallo Paul,

Für .NET 2.0 WinForms-Controls ist das *direkt* nicht vorgesehen. Es gibt
aber die Möglichkeit über IMessageFilter, wodurch Du alle Events aller
Controls - egal, ob in Containern - in einem PreFilterMessage-Handler
behandeln könntest:

// program.cs:

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 form1 = new Form1();
Application.AddMessageFilter(form1);
Application.Run(form1);
}

// form1.cs:

public partial class Form1 : Form, IMessageFilter
{
const bool messageNotHandled = false;
const bool messageHandled = true;

public bool PreFilterMessage(ref Message m)
{
Debug.WriteLine(m.ToString());
return messageNotHandled;
}
// ...
}

//==============

Als Idee ginge auch der ElementHost, durch den Du eine WPF-Page in einer
Windows Form anzeigen könntest (WPF kann ja u.a. beide Event-Richtungen).


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET

Paul Werkowitz

unread,
Jul 31, 2007, 3:08:02 AM7/31/07
to
Am Mon, 30 Jul 2007 20:52:19 +0200 schrieb Frank Dzaebel:

> Hallo Paul,
>
>> gibt es in bei WinForms eine Möglichkeit, eigene Nachrichten "von oben
>> nach
>> unten" durchlaufen lassen zu können? Ich möchte nach Möglichkeit nicht
>> jeden Container mit Code zur Weiterleitung ausstatten. Ich denke mal, für
>> Cmd-Messages sollte es sowas ja bereits geben.
>> Ich brauche sowas, um den Aktivierungsstatus von globalen Kommandos (z.B.
>> cut/copy/paste) im Editmenü bestimmen zu können.
>
> Für .NET 2.0 WinForms-Controls ist das *direkt* nicht vorgesehen. Es gibt
> aber die Möglichkeit über IMessageFilter, wodurch Du alle Events aller
> Controls - egal, ob in Containern - in einem PreFilterMessage-Handler
> behandeln könntest:

Hallo Frank,

vielen Dank für die Antwort. IMessageFiler + PreFilterMessage war mir schon
bekannt - wo ist denn der Unterschied zum direkten Überladen von WndProc?
In beiden Fällen habe ich Zugriff auf das gesamte Dispatching.

Wie ist denn das Verteilen von Messages im Framework? Ich möchte "oben" was
einfüllen (eine Msg mit Daten dran) und dann erreichen, dass diese durch
alle GUI-Elemente "fließt", d.h. von jedem GUI-Element "gesehen" wird. Aus
Performance-Gründen sollte dasjenige mit Focus zuerst drankommen, dann aber
auch die anderen. Wenn ein Control meint, dass die Msg von ihm zu
bearbneiten ist, soll das "Fließen" gestoppt werden können, da das Ergebnis
bereits feststeht.

Als Alternative habe ich mir überlegt, manuell folgenden Prozess zu
implementieren: Ich stelle das Control mit Focus fest, und frage dieses, ob
es die Anfrage beantworten möchte (dazu muss ich natürlich ein geeigentes
Interface machen). Wenn ja (Normalfall), stoppt der Prozess. Wenn nein,
gehe ich zum Parent, und mache dort das Gleiche (sofern dieser das
Interface implementiert). Danach frage ich alle Members des Containers (was
einen rekursiven Abstieg bedeuten kann), etc, etc. Im Prinzip ist es also
die Aufgabe, von einem Blatt eines Baumes aus den gesamten Baum zu
iterieren, bis irgendeiner "fertig" sagt.

Diese zweite Lösung wird sicher funktionieren, bedarf jedoch einiges an
manuellem Aufwand. Schön wäre es eben, wenn man eingebaute Mechanismen
verewnden könnte, wie z.B. das eingebaute Dispatching von Windows-Messages.
Ich bräuchte dann nur eine eigene Message zu definieren und das Framerwork
würde diese durch den Baum verteilen.

Das Ganze brauche ich, um z.B. den Zustand (enabled/disabled) von Menüitems
berechnen zu können (im OnIdle). Das hängt in erster Linie vom Control mit
dem Focus ab, aber nicht nur.

Grüße
Paule

Paul Werkowitz

unread,
Jul 31, 2007, 5:17:22 AM7/31/07
to
Am Mon, 30 Jul 2007 16:08:06 +0200 schrieb Elmar Boye:

> Hallo Paul,


>
>
> Schau Dir mal in Input und Command Konzepte der WPF an:
> http://msdn2.microsoft.com/en-us/library/ms751593.aspx
>
> die sollten sich - ansatzweise - auf die Windows Forms übertragen lassen.
> Und kennen Konzepte wie "bubbling" und "tunneling" von Befehlen.
>

Hallo Elmar,

die MSDN-Artikel über WPF habe ich schon mal gelesen. Alles höchst
interessant. Bevor jedoch nicht ein vernünftiger Editor für XAML zur
Verfügung steht, kann man das wohl noch nicht praktisch einsetzen.
Silverlight / VS2008 braucht noch ein bisschen.

Hinzu kommt das Problem, dass wir ja bereits eine größere Applikation mit
WinForms haben. Wenn ich auf WPF umstelle (was sicher irgendwann kommen
wird), dann nutze ich nicht nur ICommand & friends, sondern auch die
anderen Verbesserungen. Das ist aber ein größerer Schritt, und muss später
irgendwann kommen. Muss ja auch irgendwie bezahlt werden, und geht
praktisch also nur bei einer Komplettumstellung des Systems. Im Moment
brauche ich eine Lösung, die relativ einfach für WinForms zu implementieren
ist. Ich denke auch, ich weiß wie ichs machen werde, (siehe mein anderees
Posting)

Grüße
Paule

Elmar Boye

unread,
Jul 31, 2007, 9:49:14 AM7/31/07
to
Hallo Paul,

Paul Werkowitz <newsg...@primaprogramm.de> schrieb ...

> Am Mon, 30 Jul 2007 16:08:06 +0200 schrieb Elmar Boye:
>> Schau Dir mal in Input und Command Konzepte der WPF an:
>> http://msdn2.microsoft.com/en-us/library/ms751593.aspx
>>
>> die sollten sich - ansatzweise - auf die Windows Forms übertragen lassen.
>> Und kennen Konzepte wie "bubbling" und "tunneling" von Befehlen.

> die MSDN-Artikel über WPF habe ich schon mal gelesen. Alles höchst
> interessant. Bevor jedoch nicht ein vernünftiger Editor für XAML zur
> Verfügung steht, kann man das wohl noch nicht praktisch einsetzen.
> Silverlight / VS2008 braucht noch ein bisschen.

Ich meinte damit nicht den Einsatz von WPF selbst.

> Hinzu kommt das Problem, dass wir ja bereits eine größere Applikation mit
> WinForms haben.

Sondern sich die dortigen Konzepte zu eigen zu machen und
in den eigenen Code zu übernehmen.

Mit abgeleiteten Controls oder Extended Providern sollte sich
das Schema auch auf WinForms anwenden lassen.

Gruss
Elmar

Frank Dzaebel

unread,
Jul 31, 2007, 1:43:42 PM7/31/07
to
Hallo Paul,

>> Für .NET 2.0 WinForms-Controls ist das *direkt* nicht vorgesehen. Es gibt
>> aber die Möglichkeit über IMessageFilter, wodurch Du alle Events aller
>> Controls - egal, ob in Containern - in einem PreFilterMessage-Handler
>> behandeln könntest:
>
> Hallo Frank,
> vielen Dank für die Antwort. IMessageFiler + PreFilterMessage war mir
> schon
> bekannt - wo ist denn der Unterschied zum direkten Überladen von WndProc?
> In beiden Fällen habe ich Zugriff auf das gesamte Dispatching.

Nein. Wenn Du z.B. die WndProc einer Form überschreibst, werden die
Nachrichten der Container und Controls in den Containern normal nicht über
diese WndProc verarbeitet. Bei der IMessage-Schnittstelle funktioniert das!

Vielleicht meinst Du auch etwas anderes. Vielleicht zunächst das
abschliessen, um dann über Deine weiteren Anmerkungen zu diskutieren. Da WPF
nun demnächst wichtiger wird, ja auch eine passende aktuelle Frage.

Paul Werkowitz

unread,
Jul 31, 2007, 3:44:22 PM7/31/07
to
Am Tue, 31 Jul 2007 19:43:42 +0200 schrieb Frank Dzaebel:

>> Hallo Frank,
>> vielen Dank für die Antwort. IMessageFiler + PreFilterMessage war mir
>> schon
>> bekannt - wo ist denn der Unterschied zum direkten Überladen von WndProc?
>> In beiden Fällen habe ich Zugriff auf das gesamte Dispatching.
>
> Nein. Wenn Du z.B. die WndProc einer Form überschreibst, werden die
> Nachrichten der Container und Controls in den Containern normal nicht über
> diese WndProc verarbeitet. Bei der IMessage-Schnittstelle funktioniert das!
>

Ja natürlich .... hatte ich glatt übersehen (müsste ich natürlich wissen).


> Vielleicht meinst Du auch etwas anderes. Vielleicht zunächst das
> abschliessen, um dann über Deine weiteren Anmerkungen zu diskutieren. Da WPF
> nun demnächst wichtiger wird, ja auch eine passende aktuelle Frage.
>

Jo, dann sag mal was dazu. Wie Elmar ja bereits empfohlen hatte, könnte man
die WPF-Mechanik in WinForms nachbilden - da hätte man eine sehr schöne,
allgemeine Lösung. Nur lohnt sich das IMHO nicht... später steigen wir ja
doch um, und dann ist der Aufwand umsonst.

Das Problem lässt sich allgemein so fassen: Der Zustand (enabled/disabled)
eines Commands hängt natürlich in erster Linie davon ab, wer den Focus hat
- schließlich beziehen sich Kommandos idR auf das fokusierte GUI-Element.
Leider gibt es ein paar Nebenwirkungen: auch andere Stellen der Software
(z.B. Background-Worker der übergeordneten Form) können Einfluss haben. Wir
brauchen also einen Mechanismus, der neben dem fokussiereten Element auch
die "offensichtlichen" weiteren Kandidaten berücksichtigt, was im
allgemeinen Falle nur ein Traversieren des Baumes bedeuten kann.

Zweite Aufgabe: Die Logik der Kommandos (Statusbestimmung, Ausführung) soll
an beliebiger Stelle stehen können. Zunächst (und im Normalfall) bei den
Controls selber, aber eben auch bei den anderen, "offensichtlichen"
Kandidaten (i.e. Form). In der WPF ganz hervorragend gelöst. Wie mach ichs
aber am Besten unter WinForms?

Grüße
Paule

Frank Dzaebel

unread,
Jul 31, 2007, 4:40:26 PM7/31/07
to
Hallo Paul,

>>> vielen Dank für die Antwort. IMessageFiler + PreFilterMessage war mir
>>> schon bekannt - wo ist denn der Unterschied zum direkten Überladen von
>>> WndProc?
>>> In beiden Fällen habe ich Zugriff auf das gesamte Dispatching.
>>
>> Nein. Wenn Du z.B. die WndProc einer Form überschreibst, werden die
>> Nachrichten der Container und Controls in den Containern normal nicht
>> über
>> diese WndProc verarbeitet. Bei der IMessage-Schnittstelle funktioniert
>> das!
>>
> Ja natürlich .... hatte ich glatt übersehen (müsste ich natürlich wissen).

Schön, dann können wir mal zum Event Routing übergehen.

> Jo, dann sag mal was dazu. Wie Elmar ja bereits empfohlen hatte, könnte
> man
> die WPF-Mechanik in WinForms nachbilden - da hätte man eine sehr schöne,
> allgemeine Lösung. Nur lohnt sich das IMHO nicht... später steigen wir ja
> doch um, und dann ist der Aufwand umsonst.

Tja, das sehe ich ähnlich. Man möchte und sollte das Rad nicht doppelt
erfinden. Zwar lässt sich WPF innerhalb von WinForms und WinForms innerhalb
von WPF ausführen, aber man muss da ganz vorsichtig mit den Erwartungen sein
und gründlich in den Details vorchecken, bevor man Hybrid-Lösungen angeht.
Das würde ich nicht machen, wenn es um einheitliches globales Event-Routing
geht.

Ich denke, eine Nachbildung wäre zwar das sauberste, aber pragmatisch
solltest Du eine Zwischenlösung benutzen. Dazu gleich.

> Das Problem lässt sich allgemein so fassen: Der Zustand (enabled/disabled)
> eines Commands hängt natürlich in erster Linie davon ab, wer den Focus hat
> - schließlich beziehen sich Kommandos idR auf das fokusierte GUI-Element.

Aha, also wenn z.B. der Focus wechselt, dann soll ein Control auch einen
anderen Zustand bekommen.
Ich würde aus Einfachheit zu einem globalen Handler tendieren. Also
eigentlich dem IMessageFilter.
Ich muss aber sagen, dass ich evtl. von Deinem App-Kontext noch zu wenig
weiss, um eine seriöse gute Antwort geben zu können. Nichts desto trotz,
i'll try.

> Leider gibt es ein paar Nebenwirkungen: auch andere Stellen der Software
> (z.B. Background-Worker der übergeordneten Form) können Einfluss haben.
> Wir
> brauchen also einen Mechanismus, der neben dem fokussiereten Element auch
> die "offensichtlichen" weiteren Kandidaten berücksichtigt, was im
> allgemeinen Falle nur ein Traversieren des Baumes bedeuten kann.

Naja, Du brauchst evtl. keinen Baum, wenn Du den Control-Container-Baum
meinst. Ich sehe momentan keine Probleme beim BackgroundWorker. Bei
Modifikationen aus anderen Threads nutzt Du ja sicher Control.Invoke und
hast so keine Einschränkungen.

> Zweite Aufgabe: Die Logik der Kommandos (Statusbestimmung, Ausführung)
> soll
> an beliebiger Stelle stehen können. Zunächst (und im Normalfall) bei den
> Controls selber, aber eben auch bei den anderen, "offensichtlichen"
> Kandidaten (i.e. Form). In der WPF ganz hervorragend gelöst. Wie mach ichs
> aber am Besten unter WinForms?

Seh ich eigentlich auch keine Probleme. Ggf. mit einem eigenen
ExtenderProvider.
Wenn es alles Deine eigenen Control-Klassen sind, eher über normale
Eigenschaften.

Paul Werkowitz

unread,
Aug 1, 2007, 3:49:07 AM8/1/07
to
Am Tue, 31 Jul 2007 22:40:26 +0200 schrieb Frank Dzaebel:

>> Wie Elmar ja bereits empfohlen hatte, könnte
>> man
>> die WPF-Mechanik in WinForms nachbilden - da hätte man eine sehr schöne,
>> allgemeine Lösung. Nur lohnt sich das IMHO nicht... später steigen wir ja
>> doch um, und dann ist der Aufwand umsonst.
>
> Tja, das sehe ich ähnlich. Man möchte und sollte das Rad nicht doppelt
> erfinden. Zwar lässt sich WPF innerhalb von WinForms und WinForms innerhalb
> von WPF ausführen, aber man muss da ganz vorsichtig mit den Erwartungen sein
> und gründlich in den Details vorchecken, bevor man Hybrid-Lösungen angeht.
> Das würde ich nicht machen, wenn es um einheitliches globales Event-Routing
> geht.
>

Der Hauptpunkt ist doch aber wohl, dass man Oberflächen mangels
vernünftigem XAML-Designer derzeit eher mühsam machen muss. Das wird sicher
noch .... dauert aber auch noch.


>
>> Das Problem lässt sich allgemein so fassen: Der Zustand (enabled/disabled)
>> eines Commands hängt natürlich in erster Linie davon ab, wer den Focus hat
>> - schließlich beziehen sich Kommandos idR auf das fokusierte GUI-Element.
>
> Aha, also wenn z.B. der Focus wechselt, dann soll ein Control auch einen
> anderen Zustand bekommen.

Nein, nicht das Control. Das (globale) Kommando "Ausschneiden" zum
Beispiel.
- Ausschneiden geht nicht immer
- und wenn, tuts evtl. bei jedem Control was anderes...

> Ich würde aus Einfachheit zu einem globalen Handler tendieren. Also
> eigentlich dem IMessageFilter.
> Ich muss aber sagen, dass ich evtl. von Deinem App-Kontext noch zu wenig
> weiss, um eine seriöse gute Antwort geben zu können. Nichts desto trotz,
> i'll try.

ist mir nicht ganz klar. Gehen wir mal davon aus, dass wir eine Klasse
"Command" haben, die
- eine canExecute Methode hat. Diese wird im OnIdle aufgerufen und muss
bestimmen, ob das Kommando im derzeitigen Kontext ausführbar ist.
- eine execute Methode hat, die die Funktion tatsächlich ausführt.

Natürlich hat Command die Möglichkeit, GUI-Elemente wir z.B. Menüitems,
ToolbarItems, Buttons etc. registrieren zu können: ein Command kann
mehrere GUI Elemente zugeordnet haben.

Code für execute soll möglichst nah am Ziel (also idR beim Control)
angeordnet werden können (also nicht unbedingt global). Es brings nichts,
wenn ich ich einem 100.000 Zeilen-Programm eine zentrale Stelle habe, wo
ich den Code für "Ausschneiden" hinschreiben muss.

Code für canExecute prinzipiell ebenfalls. Hier kommt jedoch erschwerend
hinzu, dass evtl. auch andere Programmteile "mitreden" wollen - s.u.

>> Leider gibt es ein paar Nebenwirkungen: auch andere Stellen der Software
>> (z.B. Background-Worker der übergeordneten Form) können Einfluss haben.
>> Wir
>> brauchen also einen Mechanismus, der neben dem fokussiereten Element auch
>> die "offensichtlichen" weiteren Kandidaten berücksichtigt, was im
>> allgemeinen Falle nur ein Traversieren des Baumes bedeuten kann.
>
> Naja, Du brauchst evtl. keinen Baum, wenn Du den Control-Container-Baum
> meinst. Ich sehe momentan keine Probleme beim BackgroundWorker. Bei
> Modifikationen aus anderen Threads nutzt Du ja sicher Control.Invoke und
> hast so keine Einschränkungen.
>

Es geht mir nicht um Threads. Ich wollte damit nur sagen, dass der Zustrand
eines Kommandos wie z.B. "Abbrechen" (oder "Drucken") durchaus auch von
anderen Agenten abhängen kann, nicht nur vom fokussierten Control: Solange
der Background-Worker läuft, kann man eben nicht drucken. Die
Command.canExecute muss daher die Möglichkeit bieten, vom fokussierten
Control zu abstrahieren und auch andere Programmteile befragen können.

Dazu muss man eine Logik haben, um vom fokussierten Control zu diesen
"anderen Programmteilen" zu kommen. Ich sehe da eigentlich als generische
Lösung nur das Traversieren des Baumes. Denkbar wäre natürlich auch die
Forderung nach manueller Registrierung aller Agenten, die bei
Command.canFocus mitmischen wollen, beim Command. Das wäre aber
ERHEBNLICHER Aufwand, den ich eigentlich vermeiden möchte.

[snip]


>
>> Zweite Aufgabe: Die Logik der Kommandos (Statusbestimmung, Ausführung)
>> soll
>> an beliebiger Stelle stehen können. Zunächst (und im Normalfall) bei den
>> Controls selber, aber eben auch bei den anderen, "offensichtlichen"
>> Kandidaten (i.e. Form). In der WPF ganz hervorragend gelöst. Wie mach ichs
>> aber am Besten unter WinForms?
>
> Seh ich eigentlich auch keine Probleme. Ggf. mit einem eigenen
> ExtenderProvider.
> Wenn es alles Deine eigenen Control-Klassen sind, eher über normale
> Eigenschaften.
>

Habe eigene Controlklassen abgeleitet, brauche ich aus anderen Gründen
sowieso.

Greetz
Paule

0 new messages