Laden...

Forenbeiträge von Satty67 Ingesamt 11 Beiträge

05.09.2010 - 22:16 Uhr

Hallo,

ich habe eine Druckseitenbeschreibung, in der die Objekt.Bounds in 1/10mm gespeichert sind. Bei einem ersten Versuch habe ich einfach 1/10mm nach 1/100 inch umgerechnet und damit gedruckt (funktioniert!)

Dann wollte ich sehen, ob ich es einfacher gestalten kann, wenn ich Graphics (im PrintPage-Event) entsprechend anpasse:

e.Graphics.PageUnit = GraphicsUnit.Millimeter;
e.Graphics.PageScale = 0.1f;

Jetzt muss ich allerdings MarginBounds und Font.Height von inch in Millimeter umrechen. Bei ersterem gibt der pageSetupDialog immer Werte in inch zurück und beim Font mach es nur mit inch Sinn (die Font-Größen sind im Dokument in dpi gespeichert).

So wie ich das sehe, komme ich bei Quell-Koordinaten in Millimeter mit keinem Ansatz um eine Umrechnung an irgend einer Stelle herum.

Ich frage mich ob ich etwas übersehen habe oder ob meine Einschätzung soweit richtig ist?

€: Da sich PrintDocument, PageSetupDialog etc. scheinbar nicht auf Millimeter umschalten lassen, sondern immer mit 1/100 inch Werten arbeiten, bleibt mir nur meine erste Variante.

PageSetupDialog ist dabei besondern sparsam bei der Millimeter-Unterstützung. EnableMetric = true arbeitet mit unschönen Rundungsfehlern. 10mm wird nach Umrechnung in inch und danach erneuter Anzeige zu 9,91mm. Klasse.. da freut man sich als Anwender doch sicher über die Kreativität eines Programmes.

Ich verzichte auf die Angebotene Rundung durch EnableMetric und runde selbst. Verliere dadurch zwar Nachkommastellen der Millimeter-Werte, was aber trotzdem ein logischeres Bild für den Anwender abgibt.

Kann mir nicht vorstellen, dass das hier jeder so löst, aber ich kein beim Besten Willen keinen anderen Weg finden:


		private int GetMarginForMM(int inchValue)
		{
			return (int)Math.Round(inchValue * 0.254, 
			                       MidpointRounding.AwayFromZero) * 10;
		}
		
		void MenuItemPageSetupClick(object sender, EventArgs e)
		{
			// Selbst gerundete mm-Werte anzeigen
			pageSetupDialog1.EnableMetric = false;
			
			pageSetupDialog1.PageSettings.Margins.Top =
				GetMarginForMM(pageSetupDialog1.PageSettings.Margins.Top);
			pageSetupDialog1.PageSettings.Margins.Left =
				GetMarginForMM(pageSetupDialog1.PageSettings.Margins.Left);
			pageSetupDialog1.PageSettings.Margins.Right =
				GetMarginForMM(pageSetupDialog1.PageSettings.Margins.Right);
			pageSetupDialog1.PageSettings.Margins.Bottom =
				GetMarginForMM(pageSetupDialog1.PageSettings.Margins.Bottom);
			
			pageSetupDialog1.ShowDialog();
		}
31.08.2010 - 13:03 Uhr

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.

22.08.2010 - 22:09 Uhr

WinApi ShowCursor(bool) wirkt sich wirklich nur auf Fenster im gleichen Prozess aus, was ab Windows NT eigentlich fast immer nur das eigene Programm ist.

Die Funktionalität ist auch nicht abgeschaltet, man kann quasi blind weiterhin klicken.

Im angegeben Link wird auch geschrieben, dass es identisch mit Cursor.Hide() / Cursor.Show() ist.

PS: Da Du schon vom Austausch mit einem "leeren Cursor" geschrieben hast. Mit der WinAPI Funktion SetSystemCursor könntest Du die rund 15 Standard Sysmbole systemweit austauschen, was wohl früher auch für Cursor-Themes genutzt wurde.

21.08.2010 - 12:44 Uhr

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;
		}

21.08.2010 - 11:10 Uhr

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 😉

21.08.2010 - 08:35 Uhr

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 😉

20.08.2010 - 12:34 Uhr

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.

20.08.2010 - 10:13 Uhr

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.

19.08.2010 - 22:34 Uhr

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!?

19.08.2010 - 22:09 Uhr

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.

19.08.2010 - 19:19 Uhr

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.