Laden...

[erledigt] Application.Idle zum Update der WinControls

Erstellt von Satty67 vor 13 Jahren Letzter Beitrag vor 13 Jahren 4.812 Views
S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren
[erledigt] Application.Idle zum Update der WinControls

Hallo,

ich komme aus der Delphi-Welt (schon wieder einer) und arbeite mich gerade in C# ein. Fast alle Fragen liesen sich einfach durch suchen nach verwanten Themen beantworten.

Als Ersatz für die Delphi-ActionList gibt es ja einiges, konnte auch soweit herausfinden, dass zum auslösen des Update-Ereignis Application.Idle verwendet wird. Da ich erstmal nur Menüeinträge und Toolbuttons de/aktivieren will, habe ich mir selber eine kleine Lösung gebastelt:

// im Construktor
Application.Idle += new EventHandler(Application_Idle);

// im Form.Closing
Application.Idle -= new EventHandler(Application_Idle);

void Application_Idle(object sender, EventArgs e)
{
    // Hier die WinControl Updates
}

Entgegen der Warnungen, liegt meine Anwendung damit nicht bei 100% CPU Last, sondern bei < 2%. Trotzdem bin ich mir nicht sicher, ob das so ein richtiger Weg ist.

Ab .NET 3.5 gibt es nicht zufällig eine ActionList ähnliche Standard-Lösung, die ich nur wegen falscher Begriffe nicht finden konnte?

€: Delphi ActionList

In der ActionList werden Actions definiert, die u.a. Control.Text, Icon, Enabled/Diasabled und Click-Ereignis definiert. Jedem WinControl kann dann ein Action zugewiesen werden und übernimmt die Einstellungen aus dem Action. damit lassen sich z.B. Menu-Eintrag und ToolButton mit gleichen Funktionen zentral verwalten. ActionList hat das Ereignis Update, dass aufgerufen wird, wenn eine Anpassung der Controls nötig sein könnte.

Ich selbst benötige für mein "Übungs-Projekt" nur das Update-Ereignis, in dem ich prüfe ob ein Control enabled/disabled werden soll. Mir würde als Teilanwort auch schon reichen, ob mein obiger Ansatz grundsätzlich für die Aufgabe richtig ist.

1.457 Beiträge seit 2004
vor 13 Jahren

Hallo Satty67,

Da ich nicht weiß was die ActionList macht bzw. in deinem Fall machen soll, kann ich dir im Moment nicht weiterhelfen. Kannst du mir vielleicht verraten wofür die ActionList ist und was genau in deiner Anwendung diese machen soll?

Gelöschter Account
vor 13 Jahren

In der ActionList werden Actions definiert, die u.a. Control.Text, Icon, Enabled/Diasabled und Click-Ereignis definiert. Jedem WinControl kann dann ein Action zugewiesen werden und übernimmt die Einstellungen aus dem Action. damit lassen sich z.B. Menu-Eintrag und ToolButton mit gleichen Funktionen zentral verwalten. ActionList hat das Ereignis Update, dass aufgerufen wird, wenn eine Anpassung der Controls nötig sein könnte.

Ich selbst benötige für mein "Übungs-Projekt" nur das Update-Ereignis, in dem ich prüfe ob ein Control enabled/disabled werden soll. Mir würde als Teilanwort auch schon reichen, ob mein obiger Ansatz grundsätzlich für die Aufgabe richtig ist.

das konzept ist mir völlig fremd und ich erkenne hier keinerlei vorteile.

In .net werden Controls dann verändert, wenn die Information da ist. Daher frage ich mich, warum man das irgendwie in einer liste buffern sollte.....

Es wäre Hilfreich, wenn du beschreibst, was du eigentlich erreichen möchtest.

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

Ok,

zuerst vieleicht ein Link zu einer kompletten C# Umsetzung: ActionList for C#

Beispiel:

Eine Aktion ist z.B. "Datei speichern". In einem Delphi TAction wird nun u.a. Text "Datei speichern", ein Icon (Diskette), die Information Enabled/Disabled, Hint etc. und ein Click-Ereignis-Methode gespeichert. Nennen wir es saveFileAction

Dieses saveFileAction kann man nun einem MenuItem und einem ToolButton zuweisen. Beide Controls übernehmen die Werte aus TAction, ohne das man 2x die Eigenschaften bearbeiten muss. Änderungen funktionieren auch zur Laufzeit.

Man kann also zur Laufzeit saveFileAction.Enabled = False setzen und es werden beide Controls disabled. Das geht auch für andere Standard-Eigenschaften von Controls. Ebenso wird bei einem Click-Ereignis der beiden Controls nur das saveFileAction-Click-Ereignis aufgerufen.

Eine TActionList verwaltet mehrer TAction. Über Kategorie oder Tag lassen sich so einfach Gruppen de/aktivieren oder anpassen. TActionList hat das Ereignis Update, in dem man prüfen kann, ob man z.B. saveFileAction (und damit die verknüpften Controls) enablen will, weil es eine Textänderung gab.

Ganz allgemein dienen Actions dazu, mehrer Controls, welche die gleiche Funktion haben, zusammenzufassen und eine zentralle Stelle zur Manipulation zu bieten.

Ich möchte nun das Ereignis Update nachbilden. Mein Code aus Post #1 funktioniert. Das quasi Update-Ereigniss reicht mir für mein Übungsprojekt erst einmal. Bin mir nur nicht sicher, ob dabei etwas grundlegendes falsch mache.

6.862 Beiträge seit 2003
vor 13 Jahren

Hallo,

Windows Forms kennt sowas nicht, aber WPF hat was ganz ähnliches in Form von Commands.

Baka wa shinanakya naoranai.

Mein XING Profil.

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

Gut, das hatte ich schon vermutet (weil ich es wohl sonst in meiner langen Suche gefunden hätte). Bei meinen ersten Schritten in C# werde ich bei WindowsForms bleiben und WPF-Anwendungen später angehen.

Ob mein Code oben soweit OK ist, um Control-Eigenschaften zentral zu manipulieren, hat jetzt irgendwie keiner beantworten wollen. Denke aber, wird schon passen, sonst hätte schon einer was dazu geschrieben!?

Gelöschter Account
vor 13 Jahren

In WPF kannst du einzelne Eigenschaften von Controls an Eigenschaften von Klassen binden, wobei du natürlich dann auch mehrere Controls an eine Klasse binden kannst.

Ob mein Code oben soweit OK ist, um Control-Eigenschaften zentral zu manipulieren, hat jetzt irgendwie keiner beantworten wollen.

Doch. Das macht man so nicht in C# und Winforms.

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

In WPF kannst du einzelne Eigenschaften von Controls an Eigenschaften von Klassen binden, wobei du natürlich dann auch mehrere Controls an eine Klasse binden kannst.

Gut, das WPF hier mehr bietet, die Bindung an Eigenschaften klingt mir ideal. Nützt mir unter WindowsForms aber nichts.

Aufgabe ist ja, z.B. in einem Editor die Controls (Menü, Toolbutton) die Einfügen, Kopieren, Ausschneiden anbieten, entsprechend der aktuellen Situation zu schalten.

Natürlich kann ich jetzt die Controls in entsprechenden Ereignissen schalten, also z.B. Kopieren enablen, wenn richText SelectionChange auslöst und SelectionLength > 0 ist.

Jetzt wollte ich aber alle Menü- und Toolstrip-Einträge in einer Methode freischalten/sperren. Dazu könnte ich diese zentrale Methode bei SelectionChange aufrufen bzw. bei jedem anderen Ereignis, dass eine Anderung der Control-Eigenschaften erfordert.

Das ergibt dann aber viel mehr Code, da ich alle relevanten Ereignisse behandeln muss, statt nur in einem Ereignis -> Application.Idle, die nur die Eigenschaften zu prüfen und entsprechend die Controls zu schalten.

6.862 Beiträge seit 2003
vor 13 Jahren

Hallo,

dein Codeproject Link machts ja genauso, daher würde ich schon sagen das es okay ist es zu umzusetzen, auch wenn es wie schon gesagt wurde in Windows Forms ungewöhnlich ist, weil die Controls das Vorgehen eigentlich gar nicht unterstützen.

Baka wa shinanakya naoranai.

Mein XING Profil.

203 Beiträge seit 2006
vor 13 Jahren

weiß nicht, ob du das so meinst?


menuItemForSave.Click += new EventHandler(this.OnSaveSomething);
buttonForSave.Click += new EventHandler(this.OnSaveSomething);

protected virtual void OnSaveSomething(object sender, EventArgs ea)
{
}

das hat jetzt nix mit idle zu tun. sowas macht man normalerweise im construktor bzw. in der InitializeComponent Funktion (wird meistens vom Designer anglegt)

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

weiß nicht, ob du das so meinst?

Ja, das ist der übliche Weg, den ich umschiffen wollte. Also für jedes mögliche Ereignis, das relevante Eigenschaftsänderungen anzeigt, einen Handler schreiben.

Bei meinem Ansatz (Nachbildung einer Delphi Funktionalität) wird nicht bei Bedarf geprüft, sondern immer wenn Bedarf sein könnte.

Nachteil: Es wird öfter geprüft, als es nötig ist (deshalb wird auch in Delphi empfohlen, in der Methode möglichst keine komplexen Aufgaben zu lösen... nur boolsche Vergleiche, was aber auch reicht)

Vorteil: Ich muss weder alle Ereignisse kennen, die relevante Eigenschaftsänderungen melden, noch muss ich den Code dafür schreiben.

Ich werde wohl das Verhalten der Anwendung beobachten müssen. Wenn es keine Nachteile zeigt, behalte ich es bei, ansonsten gehe ich den klassischen Weg.

Als Beispiel (Freihand, nicht geprüft):

void Application_Idle(object sender, EventArgs e)
{
    bool textSelected = richText1.SelectionLength > 0;

    toolButtonKopieren.Enabled = textSelected;
    menuItemKopieren.Enabled = textSelected;
}

Ich muss dabei garnicht wissen, welches Ereignis von richText1 mir eine Änderung von SelectionLength meldet. Je mehr Eigenschaften von unterschiedlichen Elementen geprüft werden muss, desto übersichtlicher wird mein Ansatz und umso weniger Aufwand, da relevante Ereignisse nicht zu behandeln sind.

5.299 Beiträge seit 2008
vor 13 Jahren

Ja, das ist der übliche Weg, den ich umschiffen wollte. Also für jedes mögliche Ereignis, das relevante Eigenschaftsänderungen anzeigt, einen Handler schreiben.

macht er doch gar nicht. Er hat einen Handler, und weist die Click-Events zweier Controls dem zu.
Der Save-Code wird also durch 2 Controls ausgelöst.

Der frühe Apfel fängt den Wurm.

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

Ja Moment... (Click-Event != ChangeEvent)

die Click-Aktionen sind klar. Logisch, dass ich die einfach dem jeweiligen gleichen Handler zuweise. Save mischt man auch nicht mit Load oder sonstigen Aktionen, dass wird in je einer eigenen Methode behandelt. Das wäre auch keine Arbeit. ActionList sorgt aber auch dafür, das Text,Icon,Checked etc. auch gleich gesetzt werden.

Aber es geht im ersten Schritt um Change-Ereignisse, die MenuItems/Buttons ein/ausschalten sollen. Also TextSelected, TextModified, ClipboardFilled, ItemSelected usw. die den Menü-Eintrag und Button jeweils ein/ausschalten.

Ich kann mir nicht vorstellen, dass man das auf zig Methoden verteilt. Das wird doch immer in einer Methode verarbeitet und in C# dann eben die Methode durch verschiedene Ereignisse aufgerufen.

Ich versuche nur, durch Application_Idle diese verschiedenen Change-Ereignisse auf eines zu reduzieren. Denn ich kenn 100% die zu prüfenden Eigenschaften für ein Enable/Disable, aber u.U. nicht alle Ereignisse, die ein Change der Eigenschaft melden.

In einer Anwendung hat man mit der Zeit 10+ Change-Ereignisse, die nur MenuItem/Toolbuttons ein/ausschalten. Mit Application_Idle nur eines...

Mein Ansatz scheint auf jeden Fall etwas völlig ungewöhnliches in C# zu sein, ich weis auch nicht mehr, wie ich es genauer erklären soll 😉

F
10.010 Beiträge seit 2004
vor 13 Jahren

Mein Ansatz scheint auf jeden Fall etwas völlig ungewöhnliches in C# zu sein, ich weis auch nicht mehr, wie ich es genauer erklären soll 😉

Ja, es ist vollkommen unüblich in WindowsForms.
Und wenn du auch den Rest von Delphi immer und immer wieder nach .NET retten willst, wirst Du es Dir nur schwerer machen als es ohnehin schon ist.

Sobald du aufhörst in Delphi zu denken und mal modernere Herangehensweisen wie MVP/MVC/MVVM und co anschaust, oder evtl mal nach WPF schaust wirst Du feststellen, das Actionlisten in .NET nicht nötig sind.

Wären sie es, wären sie in jedem grösserem Projekt schon drin.

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

ActionListen und deren Funktionalität ist auch in Delphi nicht nötig.

Die Idee dahinter ist, die Gestaltung und Verwaltung des UI zu vereinfachen. Das es in WindowsForms nichts vergleichbares gibt, bedeutet garnichts. In WPF wird mit Commands ja bereits ein Ansatz in die Richtung geboten.

Ganz angesehen davon wollte ich nur die Update-Funktionalität in C# umsetzen und hatte gedacht mir die Erklärung mit der Erwähnung ActionList zu vereinfachen. Da hatte ich falsch angesetzt. Wollte ich alles, hätte ich einfach eine der kompletten C# Umsetzungen verwendet.

Ohne eine Grundsatz-Diskussion losschlagen zu wollen, ich werde selbstverständlich in Delphi gelernte Techniken auch in C# anwenden, wenn sie die Entwicklung für mich vereinfachen. C# ist für mich eine Erweiterung meines Sprachschatzes, kein kompletter Umstieg. Ich glaube nicht, dass eine Sprache perfekt ist und man nicht durch Techniken einer anderen auch etwas gewinnen könnte.

Wo kann man das Thema als erledigt markieren? Ich denke die letzten Post's drehen sich im Kreis 😉

F
240 Beiträge seit 2006
vor 13 Jahren

Ich glaube nicht, dass eine Sprache perfekt ist und man nicht durch Techniken einer anderen auch etwas gewinnen könnte.

Genauso sollte man kritisch herangehen und überlegen, ob die Übernahme einer Technik überhaupt Sinn macht, vorallem wenn sie fundamental in die Architektur eingreift und in der anderen Sprache nur mit vielen Umwegen zu implementieren ist.

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

Der Ansatz greift nicht fundamental in die Architektur ein, es wird ein Standard-Event-Handler eingerichtet. Es ist nicht durch viele Umwege zu implementieren, sondern erheblich einfacher, als die Standard-Alternative. Wäre letzteres nicht der Fall, würde das ganze kein Sinn machen.

Es entsteht bei mir etwas der Eindruck, als ob Gegenargumente kommen, ohne die Idee dahinter überhaupt überprüft/getestet zu haben. Allerdings will ich auch von niemandem verlangen, es zu Testen, wenn er keine Vorteile sieht.

Niemand muss es so machen, ich wollte nur wissen, ob ich es so machen kann. Meine Frage, ob es korrekt und unkritisch implementiert wurde, ist beantwortet. Drei im Netz gefundene Beispiele betägigen es, ebenso die Funktionalität in meinem Projekt.

Ganz einfaches Test-Projekt (in 2 Minuten nachgebaut):

mit 3x MenuItem, 3x ToolButton und 3x Checkboxen.

Die CheckBoxen symbolisieren nur, ob eine Funktionalität anhand Daten- oder Objekt-Eigenschaften verfügbar ist.

	public partial class MainForm : Form
	{
		public MainForm()
		{
			//
			// The InitializeComponent() call is required for Windows Forms designer support.
			//
			InitializeComponent();
			
			//
			// TODO: Add constructor code after the InitializeComponent() call.
			//
			Application.Idle += new EventHandler(Application_Idle);
		}
		
		void MainFormFormClosing(object sender, FormClosingEventArgs e)
		{
			Application.Idle -= new EventHandler(Application_Idle);
		}
		
		void Application_Idle(Object sender, EventArgs e) 
		{
			bool actionOneAvail = checkBox1.Checked;
			bool actionTwoAvail = checkBox2.Checked;
			bool actionThreeAvail = checkBox3.Checked;
			
			actionOneToolStripMenuItem.Enabled = actionOneAvail;
			toolStripButton1.Enabled = actionOneAvail;

			actionTwoToolStripMenuItem.Enabled = actionTwoAvail;
			toolStripButton2.Enabled = actionTwoAvail;

			actionThreeToolStripMenuItem.Enabled = actionThreeAvail;
			toolStripButton3.Enabled = actionThreeAvail;
		}

5.299 Beiträge seit 2008
vor 13 Jahren

Ja, das ist schon eine gute (ich würde sogar sagen: "die richtige") Idee, den Code an einer Stelle zu versammeln, der das Gui-Aussehen an den aktuellen Zustand des Progs anpasst.
Nur App-Idle ist nicht soo geeignet, weil es zB. auch bei jedem Zeichnungs-Vorgang gefeuert wird, zB beim Vergrößern des Forms zig oder auch hundertmal.
Ich schreib für sowas immer eine Methode UpdateGui(), und die mussich dann bei relevanten Aktionen eben aufrufen.

Der frühe Apfel fängt den Wurm.

S
Satty67 Themenstarter:in
11 Beiträge seit 2010
vor 13 Jahren

Ja, in Application_Idle hatte ich auch nur den Aufruf einer entsprechenden Methode drin.

Den Aufruf in alle "relevanten" Aktionen zu verlegen wollte ich ja gerade vermeiden, man vergisst was und hinterlässt dem Benutzer einen Menüeintrag, der in der aktuellen Situation garnicht ausgeführt werden dürfte.

Jetzt haben wir in der Firma rund 15 Rechner, teilweise unterschiedlich ausgestattet. Auf einigen Rechnern erzeugt der ständige Aufruf von Application_Idle eine höhere Last. Die Verzögerung durch das Message-System reicht da nicht... leider nicht akzeptabel.

Bleibt mir wohl nichts anderes übrig, als jede Funktion zu prüfen und notfalls einmal zuviel ein "UpdateUI" zu machen.

****

Nach rund 3 Wochen C# muss ich doch sagen, dass zwar die Programmierung richtig flüssig läuft, aber vieles selbst erledigt werden muss. Es fehlt doch einiges an vorbereiteten Objekt-Eigenschaften gegenüber Delphi. Von Komponenten wie ActionList oder StringGrid will ich garnicht mehr anfangen.

Bleibe aber wohl trotzdem bei C#, irgendwann hat man ja eine erweiterte Komponenten-Sammlung und fehlendes durch eigene Ableitungen erweitert.