Laden...

Forenbeiträge von Parso Ingesamt 157 Beiträge

08.12.2014 - 14:24 Uhr

Ach die mein ich doch 😉 Du kannst nur die Koordinaten innerhalb eines Fensters oder die absoluten Koordinaten ermitteln..
Wieso verwendest du eigentlich System.Windows.Forms.Cursor.Position, sonst ist doch deine Frage eher ins Winforms forum einzuordnen ... vg

08.12.2014 - 14:16 Uhr

Hallo,

die Methoden PointToScreen und ScreenToPoint sind das was du suchst.

vg

08.12.2014 - 13:30 Uhr

Wie gesagt, was die Error-Templates angeht, verwende ich hauptsächlich ValidationRules, die können ziemlich viel behandeln. Hält dich ja niemand von ab, eigene zu schreiben. Du kannst doch im IsValid über deine Childs iterieren, hält dich auch niemand von ab.

Wenn du im Child nicht aufs Parent zugreifen willst, bleibt dir nur die auswert vom Parent aus. Ich kann dir nun aus dem Stehgreif schlecht sagen wie du dein Problem konkret lösen kannst, wenn du nix postest.

vg

08.12.2014 - 12:56 Uhr

Holla,

Optimizing Performance: Controls

IsVirtualizing dürfte dich ggf zum Ziel führen. Ein TabControl ist auch wie ein ListView oder DataGrid oder sonst was ein ItemsControl...vielleicht gibts eine Möglichkeit am Steuerelement die Eigenschaft zu setzen oder das Panel anders zu verwenden.

Du könntest auch über einen per code behind emplateSelector ein usercontrol erstellen und dann als dt zurückliefern.

Aber was ist wenn du die visuellen Werte doch mal brauchst ? Dann wäre ne Speicherstruktur mit allen Eigenschaften doch ganz hilfreich...musst ja nicht alles direkt ins vm packen.

Vielleicht hilft der Denkanstoß.

vg

08.12.2014 - 12:45 Uhr

Hallo,

ein typisches Problem deiner Modelarchitektur :

Du setzt die Eigenschaft eines Childs in Abhängikeit der Eigenschaft eines "NachbarChilds" als in der gleichen Liste.

Also du hast nun mehrere Lösungen :

  1. Dein Child löst ein Changed-Event aus, welches du am Parent abfangen und behandeln kannst.
  2. Du setzt die Eigenschaft deines Childs über seinen Parent mit einer Set-Methode, in dieser SetMethode kannst du vorher die anderen Childs überprüfen.
  3. Verwende eine abstraktere Lösung. Du kannst die Statusänderung über ein Command lösen, das zugriff auf parent und child hat. (command ans parent hängen und child über commandparameter)
  4. VisitorPattern über ein command verwenden

Manchmal ist es aber viel einfacher wenn du dem Child sein Parent bekannt machst, dort eine Eigenschaft/Methode dran hängst um die rechtlichen Childs zu prüfen und dann in Abhängigkeit des Rückgabewertes deine Statusänderungen vorzunehmen (parent.GetStateofChildsExcept(this) : status)...irgendwas in der Art. Ist zwar nicht die sauberste Lösung, aber die am wenig aufwändigsten. Architektur ist immer ein Balanceakt zwischen Pragmatismus und "Schönheit".

Kleine Nebenfrage :Wieso eigentlich "natürlich dataerrorinfo" ? Ich verwende die Dinger so gut wie nie, irgendwie fehlt mir meist der praktische Nutzen davon, der Aufwand ist weitaus größer, als eine ValidationRule zu verwenden

vg

05.12.2014 - 16:50 Uhr

Hallöchen,

Wenn du vm's verschachtelst, kannst du dem child sein parent übergeben und diesen als mittelsmann verwenden. Abhängigkeiten zum Parent selbst sind meist ungünstig -> du kannst Interfaces verwenden um die Kopplung so schwach wie möglich zu halten wäre eine Überlegung wert.

Die Lösung des Elementnamebindings ist in deinem Fall wahrscheinlich die bessere Lösung. Schwierig wird das ganze nur wenn du den selektierten Wert im ViewModel auswerten oder setzen möchtest, da wirst du ein Objekt benötigen, dass eine gemeinsame Resource für deine Sub-Vm's dient, da ist halt die Mutti...

Abhängigkeiten sind immer ein mehr an Aufwand, den man so sauber wie möglich lösen sollte, gerade wenn es um Wiederverwendung geht, wirds dann schwierig feste Kopplungen auf denen deine UI Logik aufbaut zu trennen.

Wenn man es ganz sauber machen möchte, sollte man das ganze von dem Punkt aus betrachten :

Ein UserControl ist ein eigenständiges Element, dass man unabhängig benutzen möchte. Wenn man nun Kopplungen an ein anderes Element hat, entsteht eine Abhängigkeit, die man mitschleppen muss, wenn man das UserControl wiederverwenden möchte. Wenn du also nicht von einem anderen VM als vom UserControl abhängig sein möchtest bist du wieder beim ElementBinding.

VMs sollten App-Logik enthalten, wo die Werte herkommen, ist ziemlich unwichtig. Die Trennung dient primär der Testbarkeit über Unit/Integration/...tests.

Unterm strich -> depends on

vg

03.12.2014 - 14:51 Uhr

Hallo,

vielleicht etwas spät aber egal 😉

Also wenn ihr keine zusätzliche Abstraktionschicht in eure App-Logik(VIEWMODELS) eingebaut habt, dann spricht nichts gegen ein Auditlog im ViewModel. Das ganze wird aber auf dauer etwas aufwändig, gerade wenn man viele vm's verwendet die ggf gleiche funktionale kontexte verwendet, bietet sich für solche Fälle eine zusätzliche Abstraktion an, wir verwenden sie um die Applogik etwas getrennter zu halten.

vg

03.12.2014 - 14:34 Uhr

Die Tab-Navigation ist die Hölle aber es dürfte auch ohne Code-Behind gehen:

KeyboardNavigation.TabNavigation="Continue" sollte helfen.

alternativ: setz das Focusable der Items über den Style auf False, aber das IsHitTestVisible auf True

vg

03.12.2014 - 14:29 Uhr

Hallo,

du machst es echt kompliziert 😉 Es geht auch einfach, hier mal ein kleines bisschen Theorie

  1. Die maximale Größe eines Steuerelements wird duch die Größe seines Parents begrenzt.
  2. Ist die Größe des Parents unendlich groß, kann der Inhalt auch unendlich groß sein.
  3. WPF ist Vektorkrimskrams. Heißt jedes Steuerlement hat maximal 1 als Länge/Breite. Der Gesamte Layoutmechanismus wird mit ein bisschen Zeitinvestition sehr viel leichter zu verstehen(sonst würden DataTemplate Größen gar nicht so aussehen wie man das gerne möchte)

Nun ein paar Tips:

  1. Niemals gegen Größen Binden bzw. keine Größen angeben...ausser du willst dass es nur 20 Punkte breit/hoch ist. Das ist sehr selten der Fall. Kann man bei Icons machen, weils unschön aussieht, wenn man Pixelzoom im Bild hat..
    2.Weniger ist mehr:
    Wozu brauchst du im ein Frame im Scrollviewer im Border ? Je weniger desto besser.

  2. Wenn du den Scrollviewer verwendest, und nur vertikal scrollen möchtests schalte das horizontale Scrolling ab, dann ist der Scrollviewer nur unendlich hoch und nicht noch unendlich breit

  3. VerticalAlignment/HorziontalAlignment - VerticalContentAlignment/HorizontalContentAlignment lösen eine Menge Probleme.

Die andere Ursache deines Vergrößerungsproblems is das hier :

Width="Auto" -> heißt immer : so breit wie der Inhalt -> so breit wie das größte Element im VisualTree. Allein den H-Scroll abschalten dürfte nicht gereicht haben.

Nimm mal die Zeile raus und setz HorziontalAlignment auf Stretch, dh so breit wie dein Parent.

  1. Vermeide Code-Behind. Du kommst mit Code-Behind nur bedingt weit.

Code Behind brauchst du nur wenn du eigene Controls oder Attached-Mechanismen entwickelst, alles andere dürfte hackerei sein.

Leider ist die Msdn ....sehr schwach. viel blabla und wenig konzeptionelle Tips.

vg

03.12.2014 - 13:57 Uhr

Das ist recht einfach erklärt :
Für jedes UserControl wird ein eigener Designer gestartet, der nur dein UserControl kennt. Das Teil ist auch nicht ganz fehlerfrei...

Du kannst einfach deinen Converter in das UserControl-Resourcedictionary verschieben, weil es da eigentlich auch hingehört. Wenn du dieses UserControl wiederverwenden möchtest, heißt es nicht, dass es deinen Converter schon in den Resourcen gibt, du verlässt dich nur darauf.

Resourcen ins App-Xaml auszulagern, verursacht eine Menge Probleme bei der Entwicklung. Wenn du dir den Speicher sparen möchtest, musst du dir ein eigenes ResourceDictionary implementieren, da kannst du mal nach "sharedresourcedictionary" googeln(was fürn sinnfreies verb). Nur leider wird dein Designer damit nicht ganz klarkommen 😉

Der Designer zeigt nur das an, was er fehlerfrei übersetzen kann...oder besser gesagt, was innerhalb der Fehlertoleranz liegt.

vg

29.09.2014 - 07:32 Uhr

Morgen,
setter und getter von dps werden nie aus dem xaml aufgerufen, schau mal nach der Klasse Keyboard/manager, da gibt's ne Eigenschaft mit dem Controll das den Fokus hat.

Wozu brauchst du das eigentlich? Vielleicht gibt's für deine Anforderung eine elegantere Lösung

22.09.2014 - 19:33 Uhr

Wenns um Geld geht werden Menschen zu Schweinen 😉

Der Ansatz ein ganzen Ablauf zu loggen...ist meines erachten Wahnsinn...zu mal man da innerhalb von 2 Sekunden monströse Dateien produzieren kann. Da hatte ich vor einigen Monaten wieder eine Diskussion mit einem meiner Kollegen...

Wenn man jeden Methoden Ein- und Ausgang logt, gehe ich ganz stark davon aus, dass Clean-Code Principles , UnitTests und Code-Reviews absolute Fremdbegriffe sind, da wäre man mit Refactorings und Schulungen besser dran, als nachträgliche Flickschusterei und Schuldzuweisungen über Loggings - leider ist dieser Kindergartenkram immernoch Realität - "Investmentbanker, die wissen wie mans macht" -> BWLer-Projektleiter:D...das selbe. Ist am Ende viel billiger, spart nerven und Stress und schlechte Laune bei allen Beteiligten. Ist auch verständlich, dass der Kunde sauer wird, wenn man ihn als Betatester missbraucht...

Weil:wenn mans richtig macht braucht man keine 20000 Zeilen Logs um einen Fehler zu finden. Der Stacktrace reicht meist aus.

Ich persönlich nutze den Traceschalter 😉 Der reicht meist für das was ich mache aus, jedes Logging kostet Performance, genauso wie jeder andere redundante Aufruf. Wenn Fehler auftreten brauch man meist nur die Exception und wenns richtig übel ist -> MemoryDumps...die Dinger sind Gold und Diamanten Wert.

Unterm Strich ist es meistens so, dass man monster-logs nur dann braucht, wenn man nicht weiß was los ist. Wenn man an einen Punkt angekommen ist, wo man alles erdenkliche protokolliert, nur um dann nen 2 min Bugfix auszuliefern, hat man von Anfang n bisschen unfug bei der Planung gemacht. Einigermaßen gut durchdachte Konzepte lassen Platz für ein ordentliches Exceptionhandling und entsprechende Fehlermeldungen zu.

Nachvollziehbar ist aber der rechtliche Aspekt um die "Schuldfrage" zu klären, ein heiß geliebtes Thema im Projektgeschäft. Da gilt : Absicherung vor Performance...Vertragsstrafen sind teilweise so enorm, die können ne Firma locker ruinieren.

vg

22.09.2014 - 18:41 Uhr

Die Sache mit dem Garbagecollector ist sone Sache...darauf sollte man sich nie verlassen. Der Grund wieso dein Ram voll läuft ist recht einfach erklärt : der gc räumt auf wenn er die Resourcen dafür bekommt oder er dazu gezwungen wird. Dadurch, dass du die Datei wahrscheinlich in einem Abwasch lädst, wirds natürlich Resourcentechnisch schwierig. Es gibt glaub auch sone Art Zeitstempel im GC, wann man das letzte mal ne Referenz angefasst hat, je länger das her ist, desto größer ist die Wahrscheinlichkeit, dass dein Objekt weg fliegt.

Der GC ist dafür da, dass man seinen reservierten Speicher nicht ständig freigeben muss, aber wenn man Systemresourcen belegt oder wie ein Fass ohne Boden Speicher reserviert, kann der einem auch nicht mehr helfen.

Ein GC.Collect ist ein Performancekiller schlecht hin und den sollte man nie aufrufen.

Wenn man Operationen mit großen Dateien durchführt, ist es weitaus sinnvoller in kleineren Blöcken zu arbeiten. Meist benötigt man nur einen Teil der Daten, die sich in der Datei befindet. Die Sache mit Release und Debug klappt nur solange, bis im Release ein gleichwertiger Speicherverbrauch erreicht wird.

Ein einfaches Beispiel, Videos,
Man kann für die Laden-Methoden einen Ringpuffer verwenden, dort werden so viele Bilddatenreingeschrieben (Haupt und Differenzialbilder) bis der Ringpuffer voll ist, beim Abspielen eines Frames werden die Bilddaten verworfen. Kombiniert mit einem Filepointer hat man so eine schnelle Leseoperation im RAM und einen Thread der den Ringpuffer im hintergrund mit Daten füttert.

Ich hatte vor ein paar Jahren (sind vielleicht ein paar mehr) 5mb - 5gb große Dateien aus Steuergeräten von Rennfahrzeugen, die sind in einen DX Oszilloskop in Echtzeit dargestellt worden sind. Wenn man mit solche Datenmengen arbeitet, kommt man ganz schnell an Speicher und Performancegrenzen, jenseits von gut und böse.

Man sollte auch immer beachten, dass die eigene Applikation nicht die Einzige auf dem System ist, daher immer versuchen kleine Päckchen zu schnüren...kostet am Ende auch weniger Zeit beim Debuggen, Review oder Refaktorieren 😉

PS : wer das Dispose vergisst, hat die ÄtschiBätschiException's verdient...

22.09.2014 - 18:21 Uhr

Hallo,

ich hab recht gute Erfahrungen mit dem Visitor-Pattern gemacht, wenns es darum geht Rekursionen zu vermeiden.

Vielleicht für die Pattern-Freunde eine alternative.

vg

22.09.2014 - 18:08 Uhr

Das liegt vermutlich daran, dass die Textbox selbst als größe "Auto" hat...dadurch wird in der TextBox selbst das Arrange mit der Länge des strings berechnet.

Du kannst dir das Arrange auch sparen denke ich. Du hast sicherlich ein ControlTemplate geschrieben, darin kannst du deine beiden Textboxen durch ein Grid entsprechend aufteilen.

Ich glaub du musst auch margin und padding deines Steuerlementes mit ins MeasureOverride einrechnen...

vg

19.09.2014 - 12:36 Uhr

Bau dir ne Extension oder benutz operatorenüberladungen , fluent interfaces, Specifications(ein sehr nützliches Pattern für regelbasierende system/workflows und co) oder oder oder 😉

public static bool Between(this int cmpValue, int min, int max) { return ... ; }

Elegant ist was gefällt ... ich persönlich bevorzuge den direkten weg, mein Code soll sich wie eine Geschichte lesen. Der Teufel liegt nämlich im Detail und alles was nach : "if(a<b && a>c) then do this and this else a = 2" oder ähnliche programm-code-exoten aussieht, versteht man nämlich nach spätestens 2 Monaten selbst nicht mehr, ohne nachdenken zu müssen, was man sich da mal(s) ausgedacht hat.

vg

17.09.2014 - 21:30 Uhr

Holla,

mit dem Fehler kann man nicht viel Anfangen, benutzt du eigene Serialisierungmethoden ? dann würde ich da mal reinschauen...poste mehr von deinem Problem, sonst kann man dir nicht helfen 😉

Deserialisierung von generischen Liste sollte kein Problem darstellen, es kann sein, dass du Namings geändert hast oder die Datei wurde manuelll angepasst...null ref kann alles sein...

vg

15.09.2014 - 19:46 Uhr

Ai caramba ...
als erstes mal : schmeiss dein controltemplate wech...das is nicht notwendig...wenn du jedoch drauf bestehen möchtest...mach dir dein <Border x:Name="bd"> ..ContentPresenter ... </Border> und setz beim IsMouseOver die werte des Borders ... viel spass beim exprimentieren.

Wenn du Controltemplates nicht vollständig ausprogrammierst, fehlen dir später verdammt viele eigenschaften die du noch brauchst...margin/padding/allignment-werte usw...

PS : benutz die Trigger und setz dann einen anderen Style...das ist nicht so aufwändig.

vg

15.09.2014 - 19:38 Uhr

Versuchs mal mit Width = "*"
vg

15.09.2014 - 19:36 Uhr

Ein Dispatcher Timer holt den Status der DLL ab und aktualisiert die WPF GUI. Funktioniert soweit alles problemlos.

Dispatcher.CurrentDispatcher.BeginnInvoke dürfte dir hier helfen...setzen die Priority auf ApplicationIdle ...das sollte sicher funktionieren

So Pi mal Gummibärchen:

Dispatcher.CurrentDispatcher.BeginnInvoke(DispatcherPriority.ApplicationIdle,((Action)(()=>{ .. Code... ;}));

Dadurch wird eine Threadsynchro deines Ladezyklus mit der UI erreicht...

PS: Ich find Timer doof 😉

vg

15.09.2014 - 19:29 Uhr

Ich halte persönlich nicht so viel von der Idee mit dem Konstruktor, dadurch koppelt man kleine Einheiten wie ein VM...im ungünstigsten Fall castet man noch intern auf einen expliziten Typen und dann kann man das ding gleich ins VM packen.

vg

15.09.2014 - 19:25 Uhr

Hallöchen,

vielleicht ist dein Ansatz ein falscher. Das du in einem Datatemplate nur ein Model, bzw. ViewModel bekommst ist korrekt.

Wenn du dein VM im DataTemplate hast, kannst du per die Trigger deines DataTemplates verwenden um auf Änderungen deines ViewModels/Models(Model und ViewModel sind unterschiedliche Dinge) zu reagieren.

Wenn du in deiner UI Änderungen vornimmst, speicherst du sie sicherlich irgendwo im VM...wenn nicht, mach entsprechende Eigenschaften rein. Man kommt da so schwer ran, weil es vom Frameworkdesign so gewollt ist, dass man schwer ran kommt. Ein einfaches Beispiel, wenn du in einem TreeView einen Knoten aufklappts, benötigst du im gebundenen Objekt eine Eigenschaft(sowas wie "IsExpanded") die per Binding diesen Zustand spiegelt...wenn du nun aus deinem Code heraus den Knoten auf oder zuklappen möchtest, änderst du einfach die gebundene Eigenschaft.

Wenn z.B. Visiblity aufgrund eines Boolschen Wertes im VM änder möchtest, benutz die Trigger oder einen Binding-Converter, der aus deinem Boolschen Wert einen Visibility-Wert macht.

vg

15.09.2014 - 19:04 Uhr

Hallo,

vielleicht mal ein alternativer Ansatz für die Arbeit mit Dialogen:

das MVVM hat einige Lücken(dat is nicht die eierlegende wollmichsau), weil es die Verwendung von UI im VM nicht vorsieht. Nun gibt es aber häufig Anwendungsfälle, in denen man Dialoge machen möchte. Das dumme ist, dass man zwar von der UI aus Commands des VM aufrufen kann, aber nicht umgekehrt...falls jemand das mal gelöst hat, dann immer her mit der Info...

So da doofe an der Sache ist nun : Commands sind im MVVM teil des ViewModels ODER des Models (gibt ja Menschen die behaupten ObservableCollections sind nur für WPF ... )...das bringt die Frage mit sich : wie bekomm ich dann son dusseligen Dialog auf ? die Antwort ist einfach : Ignoriere MVVM. Mach ein Command und weise es zu.

    
//ViewModelCommand  implementiert ICommand mit virtuell methoden von execute und canexecute
public class OpenFileCommand : ViewModelCommand 
    {
        public override bool CanExecute(object parameter)
        {
            return parameter is IMyViewModel;
        }

        public override void Execute(object parameter)
        {
            //immer prüfen, weil man das command auch IMMER direkt mit execute aufrufen kann
            if (!CanExecute(parameter))
            {
                return;
            }

             //den Dialog einfach als WPF-Window erstellen
             if((bool)FileDialog.ShowDialog())
             {
               (parameter as IMyViewModel).OpenFile(FileDialog.Path);
             }
        }
    }

Dann brauchst noch eine Implementierung, ähnlich dieser hier :


    public static class UICommands
    {
        public static ICommand OpenFile{ get; private set; }
        
        static UICommands()
        {
            OpenFile= new OpenFileCommand();
        }
    }

Wenn du nun in deiner Anwendung das Command verwenden möchtest kannst du per x:Static das Binding angeben.


            <Button Content="Open" Command="{x:Static myApp:UICommands.OpenFile}" CommandParameter="{Binding }"/>


Das klappt recht gut, man kann dort viele Funktionalitäten auslagern die man sonst im VM hat und eigentlich allgemeiner Natur sind. Über den Parameter kannst du eigenlich alles Binden was du gerade im Zugriff hast, auch dein VM.

Der große Vorteil dieser Verwendung : du kannst 20 mal die selbe Funktion an verschiedenen Stellen in deiner UI unterbringen(wenns dir beliebt auch auf 20 Buttons nebeneinander) und die noch in deinen VM's aufrufen.

Es gibt noch eine Alternative, diese ist im Aufwand aber viel größer. Eine Factory/Service oder ähnliche Implementierung verwenden und entsprechend den Dialog mittels MVVM - Pattern implementieren und über die Factory/Service verwenden.

Theoretisch klingts einfach (wenn man weiß was ne Factory oder n Service ist 😉 ) die Implementierung ist aber recht aufwändig. Ich benutze einen recht weit entwickelten Mechanismus aus Annotations und Services und XML-Konfig um mir meine Models, Views und ViewModels zusammen zu stellen. Dadurch hab ich mein Codingaufwand zwar stark reduziert, aber für kleine Anwendungen ist das mit Atombomben auf Spatzen schießen.

PS: das meiste ist ausm Kopf zusammen geschrieben, der Code dürfte nicht baufähig sein 😉 Copy Paste proggen is eh was für Faule, die Bugs sowas von verdient haben:P

Vielleicht hilft es dir etwas weiter...vg

05.09.2014 - 16:13 Uhr

Hallöchen,

also zuerstmal mal ein paar theoretische Infos:
Delegaten sind Funktionszeiger, jede Funktion hat eine bestimmte Addresse im Speicher.
Wenn man die Adresse der Funktion kennst, kannst du diese Aufrufen. Wenn du dich mal im Google umschaust...es gibt im Assembler einen Befehl "call"...der dafür da ist Funktionen über eine Speicheradresse aufzurufen.
Wenn man das verstanden hat, wird recht schnell klar, wie z.b. anonyme methoden oder events intern funktionieren.

Auch wenn du es nicht siehst, jede Klasse und jeder Typ den du verwendest ist ein Zeiger(speicheradresse). Da du dich in C# nicht unbedingt mit sowas rumprügeln musst, verwendest du ein Alias für diese Speicheradresse, den wir mal Delegat nennen. ...

Wenn du Eventhandler registrierst, machst du nichts anderes in eine Liste die Adresse deiner Funktion einzutragen:

Aus :

Button.Click += MyButtonClick;

Wird sowas in der Art:

public event Action<EventArgs> Click 
            add
            {
                m_Click += value;
            }
            remove
            {
                m_Click -= value;
            }
        }

Das ist das sog. Observer-Pattern... in der .net Welt Events....
Dieses String-DelegateCommand macht also nix anderes als einen Event aufzurufen, also eine Command nach Event Umleitung.

So nun zu den Commands, dafür gibt es natürlich auch ein Pattern ->Command-Pattern:

Die beiden sind sich in ihrer Funktionsweise sehr ähnlich, haben jedoch einen prägnanten Unterschied:

Bei einem Event musst du das Objekt, welches das Event bereit stellt kennen, bei Commands nicht(jedenfalls bei der WPF verwendung).

Ein Command stellt ein Signal dar, das ausgelöst wird, dieses Signal steht erstmal ganz allein im Raum. Deine CanExecute und Execute-Funktionen sind diejenigen, die sich für das Signal interessieren.

Hast du nun 2 Commands, stellen diese zwei unterschiedliche Signal dar...usw. Da von einem Steuerelement erstmal immer nur ein einziges Command ausgeführt werden kann, werden erstmal auch nur dieses eine CanExecute und Execute aufgerufen.

Damit du auf dieses Signal reagieren kannst, benötigst du natürlich trotzdem eine logische Verbindung vom Signal zum Interessenten.

Diese Aufgabe übernimmt der CommandManager über die CommandBindings. Also sind die Commandbindings sind nichts anderes als die verknüpfung zwischen einem Command und den Funktionen.

Der CommandManager ist ein ziemlich kluges Kerlchen, der nur dann ein CanExecute aufruft, wenn sich dein UI-Element im logischen Fokus(logischer Fokus ist nicht unbedingt auch der Eingabe-Fokus) befindet...sprich, alles was potentiell eine Eingabe behandeln könnte.

Wenn du deine Commands über ViewModels verwendest, stellst du diese entweder im ViewModel selbst bereit, oder "globaler".

Du kannst Commands auch komplett eigenständig implementieren, wenn du mit looser Kopplung arbeitest, sind die überaus hilfreich um Funktionen deiner Applikationsschicht in deine UI zu bringen.

Eine alternative Verwendung ist eine Implementierung ala ApplicationCommands-Klasse...das ist für die CustomControl-Implementierung sehr nützlich(.net hat die Quellcodes veröffentlicht, kann man viel nachschauen).

Was dein mehrfach-CommandBinding angeht, sollte es recht offentlichlich sein, dass du pro klasse nur ein CommandBinding für ein Command hast. Ein zweites würde auch nicht viel Sinn ergeben. Denn es stellt sich die Frage : welches soll aufgerufen werden, wenn nur eins welches, wenn beide: wieso sollte es bei einem Signal, innerhalb eines Objekts mehrere Behandlungen geben, das würde den Prinzip der Objektorientierung widersprechen.

Die Aufgabe einem Button Kontextabhängige Funktionen zu geben ist, ich sags mal direkter, dreckig. Hast du eine komplett andere Anforderung, mach eine neue View und ein neues ViewModel. Einfaches Beispiel: Speichern und Drucken- Ansicht, dort können sich ganz andere Funktionalitäten befinden

hast du mehrere artähnliche Anforderungen, benutze Trigger und Converter, aber schreib nicht in dein VM UI-Abhängige logik rein. Speichern und Speichern unter - Ansicht, dort sind meist nur minimale unterschiede in den Aufrufparametern

Das ViewModel ist dafür da das Model, also deine Datenstruktur, für die UI zugreifbar zu machen und App-Logik auszuführen, es ist nicht dafür dar um irgendwelche Hacks für die UI zu bieten.
Was zb ein böser Hack wäre : Visibility in ein ViewModel reinzumachen...es gibt einen Grund, wieso man die Sichtbarkeit eines Steuerelements ändert. Über einen Binding-Converter kannst du solche sachen aus "wenn mein enum = enum.awert, dann button unsichtbar" machen. Trigger sind für viele Fälle auch sehr gut.

vg

27.06.2014 - 18:13 Uhr

Hallo nochmal,

das "mvvm mediator pattern" ist ein Observer - Pattern...mit anderem Namen.

Also ich versuch das mal zu rekaptulieren:

Du hast ein View und ein Model für deinen Baum

Du hast ein View und ein Model für die Detailansicht

In beiden Views hast du unterschiedliche Datenquellen...Also dein TreeNode zeigt ein anderes Objekt an, als deine Detailansicht ?

ViewModel A und ViewModel B verwenden die selbe Instanz von Datenklasse:

Da stimmt was in der Implementierung deiner Daten-Objekte nicht, denn die müssten ein INotificationChanged haben und entsprechend bei der Eigenschaftenänderung das Event auslösen.
Falls du davon ausgehst dass es reicht in deinen ViewModels ein INotificationChanged zu haben...dat ist nich richtisch. Jede Eigenschaft muss seine Änderung mitteilen.

ViewModel A und ViewModel B verwenden NICHT die selbe Instanz von Datenklasse:

Dann bist du bei sowas wie Domain-Events(ist Observer, etwas erweitert) oder anderen Lösungen. Du müsstest bei der Änderung von A ein Update-Befehl schicken, den deine Viewmodels abonieren.

Ist im Endeffekt das selbe die dein MVVM-Mediator-Pattern.

DomainEvents-Link

Ich hab auch schon einige Prototypen mit einem ViewModel-Tree probiert, die solche Probleme lösen können. Einfach in die ViewModel-Basisklasse eine Child-Collection von ViewModelBase eingefügt und über TemplateSelectors die entsprechenden Views rausgesucht. Kein Mensch sagt dass n ViewModel keine ViewModel-Childs haben kann. Da eine Applikation ausgehend vom Fenster in eine Baumstruktur gepackt werden kann, tut das nicht weh und man kann beim löschen/aktualisieren von ViewModels alles in einem Aufruf erledigen

Ich würde aber an deiner Stelle, insofern du mit Plugin-Mechanismen arbeiten willst, die Domain-Events in betracht ziehen.

Noch sauberer wären DTO's, dafür müsstest du aber für alle deine Datenobjekte die du anzeigen willst jeweils eine eigene Klasse schreiben, die alle Eigenschaften enthalten die du vom Datenobjekt brauchst enthalten. Das ist ein mörderischer Aufwand, den man sich gut überlegen sollte. Änderungen an den Datenobjekten können dann per "Vergleiche mein Datenobjekt mit dem DTO"-Klasse ins Datenobjekt übertragen und dann wieder in die Datenbank geschrieben werden.

Dadurch wirst du sehr unabhängig vom verwendeten OR-Mapper...aber lohnt sich eigentlich nur wenn man vorhat die Lebenszeit eines aufwändigen Projekts über mehr als ein paar Jahre hinweg zu verlängern.

vg

27.06.2014 - 11:12 Uhr

Gumo,

hm ich glaube du hast dein einen grundgedanklichen Konstruktionsfehler. Wenn du ausgehend vom berühmt berücktigten MVVM-Pattern arbeitest, kennt das eine das andere nicht.

Der Sinn dahinter ist es, über eine Funktionalität names XAML eine Verbindung zwischen der Benutzeroberfläche und deiner Programmlogik herzustellen. XAML ist keine reine Beschreibungssprache für hübsche Oberflächen, sondern auch der "Mediator" zwischen deinem Steuerelement und deinem Code.

Wenn du z.B. ein Treeview in deiner UI hast, hast du irgendwo in einem Model (mathematische) Abbildung deines Trees in eigenen Objekten sowas wie "myNodeItem", das nur die Daten enthält die wichtig sind und mittels Binding an das Treeview geklebt wird, über Datatemplates kannst du Aussehen und Funktionen bereitstellen, die der Benutzer auslösen kann(zb einen Button in jedem Element einer Liste).

Damit dein Model mitbekommt, dass der Benutzer vorm Bildschirm auf den Button gedrückt hat gibts das sogenannte Command-Pattern. Das ist gefühlte 1000 Jahre alt, also nix neues in der Softwarewelt.

Das Prinzip ist folgendes :

Button löst Command aus ... und irgendwo innerhalb deines Kontextes interessiert sich jemand für dieses Command(CommandBinding). Der Interessent stellt Methoden zur Verfügungum zu prüfen, ob das Command ausgeführt werden kann (CanExecute) und die Ausführung selbst (Execute) und sobald das entsprechende Command ausgelöst wird, reagiert der Interessent darauf.

Es ist ähnelt dem Observer-pattern (sind die Events in .net) benötigt aber keine direkte Registrierung am Signalgeber (also die Klasse wo das Event existiert), es geht nur noch um das Signal selbst.

Die Commands sind ein sehr wichtiger Teil der WPF Architektur. Events gibts zwar auch, aber die sind nicht unbedingt dafür da, eine Verbindung zwischen UI und Modell herzustellen, die dienen mehr der Kommunikation zwischen den Steuerelementen selbst. Muss man ein wenig mit aufpassen.

Deshalb gibts Teilweise auch ganz andere als im WinForms und man vermisst ganz viele...MS hat sie nicht vergessen, sie haben das Problem UI Logik - Programmlogik anders gelöst.

vg

25.06.2014 - 20:08 Uhr

Naja, wer sagt dass du die Löschen musst, du willst die ja nur nich zeichnen...

Die Virtualisierung arbeitet so, dass wenn du viele viele Listenelemente hast, nur die gezeichnet werden die sichtbar sind.

Wenn du ne Listview verwendest, kannst du das auch so machen. Die hat eine Eigenschaft dafür oder zumindest eine angehängte.

Deine Anforderung klingt nicht nach viel, ist es aber im Endeffekt. Alles was mit Grafikk zu tun hat, hat, sagen wir mal ein anderes Kaliber als ne "doofe" Datenbank oder Web-anwendung. Dort liegen die Schwerpunkte meist in "hübscher" und/oder benutzbarer Darstellung und Datenhaltung.

Wenns um Optimierungszeugs geht, braucht man Abstraktionsvermögen und etwas Geduld, nicht jede Idee optimiert auch 😉

Dieser VirtualCanvas ist ganz gut und sollte für deine Zwecke voll ausreichen. Soweit ich das sehen arbeitet der mit einem einfachen Clippingalgorithmus über die Boundaries(das ist das rechteck das man gedanklich um ein objekt ziehen kann- also min/max koordinaten oder die größe), die werden mit der größe des Controls und der Größenabgabe berechnet (das matrizenzeugs musst du mal außer acht lassen, ist wohl nur für skalierung und verschiebung drin)

Es gibt in der VirtualCanvas Klasse eine Methode MeasureOverride() ... da sieht man recht gut wie das Measure funktioniert. Vielleicht hilf dir das etwas weiter...oder du benutzt das Teil einfach 😉
Die Klasse hat übrigens nur zufällig den gleichen Namen ... meine ist viel primitiver

vg

25.06.2014 - 17:39 Uhr

Ich zitiere mich mal selbst:

Viele Entwickler denken noch in Pixeln und Events und UI-CodeBehind..

Benutz Events nur dann wenn du sie wirklich brauchst...die sind BÖSE. Damit kannst du jegliche Form von schwer zu debuggenden Fehlern verursachen. Einer der schlimmsten is : nicht vom Event deregistriert, aber objekt wurde irgendwo "gelöscht", also soll nicht mehr verwendet werden...und irgendwer löst den event aus und der listener deines gelöschten objekts arbeitet noch munter weiter...das sind so Probleme die kein Mensch braucht.
Anderer Fall : Event A Event B Event C Event A - Ein sehr häufiges Problem.

Es ist ziemlich ungünstig dass du dein Fenster als Customcontrol betitelst 😉 denn dann ist es keins. Ein CustomControl ist ein Steuerelement, sowas wie ne Textbox oder ein Label, nur mit eigener Implementierung..ein Fenster ist ein Fenster...auch wenns die gleichen Mechanismen sind, steckt eine andere "Idee" dahinter.

Versuch mal diesen Ansatz sonst wirds noch komplizierter:


    public class VirtualCanvas : Canvas
    {

        public bool UseVirtualize
        {
            get { return (bool)GetValue(UseVirtualizeProperty); }
            set { SetValue(UseVirtualizeProperty, value); }
        }
        public static readonly DependencyProperty UseVirtualizeProperty =
            DependencyProperty.Register("UseVirtualize", typeof(bool), typeof(VirtualCanvas), new PropertyMetadata(false));

        public VirtualCanvas()
        {
        }
        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);

            UseVirtualize = true;//false
        }
    }

In dem Link den ich dir geschickt habe, ist noch aufgezeigt wie du die für solch eine Lösung ein ControlTemplate erstellst. Ohne das wirst du wirklich nicht mehr weit kommen. Dein Ansatz der Lösung ist vermutlich recht weit weg von der Objekttrennung.

Du hattest doch ganz am Anfang geschrieben(Wenn nun aber nur m angezeigt werden können (m<n) so sollen auch nur m gezeichnet werden.)..da musst du nur die Virtualiserung einschalten, dann ist das Thema durch...in so fern das dein Panel unterstützt. Virtualizing-StackPanel googeln. Ist ja nich so dass es sowas schon gibt.

Kannst mal deinen Code packen und in die "cloud" schmeißen, dann schau ich beizeiten mal drüber.

vg

25.06.2014 - 17:07 Uhr

Hallöchen,

also soweit ich weiß, kann man im enum keine Operatoren überladen.

Dein Problem kannst du in mehrfacher Weise lösen, immer davon ausgehend..

Unbequeme Lösung
Schreib dir ne Klasse für den Vergleich und verwende diese an allen Stellen wo es einen Vergleichsoperation für das Enum gibt, sonst bringts nicht viel.

oder : Nicht erfüllbare Anforderung << is immer doof

Saubere Lösung:
Sollten die Werte irgendeiner Stelle gespeichert sein(DB/Datei), aktualisiere diese Werte per Script & Co, dann kannst du dein Enum im Code erweitern. Dürfte wohl ein Problem werden, wenn es noch eine andere Komponente gibt, welche ein gleiches Enum definiert hat, das müsste mit angepasst werden..wenn dort keine Änderungsmöglichkeit besteht:

Was noch zu bedenken ist:

Das Problem dürfte eher die "andere Bibliothek" darstellen, wenn die auf einmal einen neuen Wert ihr Enum übersetzen möchte, kann es gut sein, dass dir, je nach verwendeter Sprache n böser Fehler im die Ohren fliegt, den du nicht unbedingt auflösen kannst.

Mach ein neues Enum und mappe die Werte beim Auslesen auf das Neue. Dann kannst du mit ein paar Handgriffen - insofern das ganze nicht mit Copy Paste häufiger vorkommt - auf das neue Enum umstellen, ohne das Alte anzufassen.

Kleiner Tipp : Bennene das altes Enum um, so dass es den Namen des neuen hat (über F2/oder Refactor-Name), so werden alle Verwendungsstellen gleich mit umbenannt und arbeiten mit dem Neuen. Die Stelle(n) wo der Wert in das Enum umgewandelt wird, musst du entsprechend anpassen, dass die Werte des alten entsprechend gemapped werden, beim speichern wirst du das wohl auch machen müssen.

Lass genug Platz zwischen den Werten(nicht 1,2,3 sonder 10,20,30) für genau solche Erweiterungen, da hast du das nächste mal nicht den selben Ärger.

vg

25.06.2014 - 14:05 Uhr

Grins 😄 und woher weißt du wie hoch dein Fenster ist wenn du einmal die Größe deines Childs berechnest dürfte sich das nich ändern, der arme Anwender darf das Fenster nicht ändern..its a feature not a bug 😉

Also wenn du dein Control unbedingt dazu zwingen möchtest die Virtualisierung an und ab zu schalten, sind wir wieder beim Trigger 😉

Du hast doch ein ControlTemplate gemacht oder ? ... ich hoffe es jedenfalls...

Gib deinem Control eine neue Eigenschaft : **IchZwingeDirMeinenVirtualisierungsWillenAuf **... natürlich ein DependecyProperty (niemals INotficationChanged in CustomControls verwenden...dat gibt nur probleme beim Binden an das Control später)

Dann machst du dir nen Trigger gegen diese Eigenschaft und (de)aktivierst die Virtualisierung aufgrund dieser Eigenschaft.

So dann suchst du dir folgendes : OnRenderSizeChanged/OnRender oder irgendeine passende Methode die du überschreiben kannst.

Gibt viele OnMethoden die man verwenden kann...

        
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);

            IchZwingeDirMeinenVirtualisierungsWillenAuf = JAWOLL/NEIIIN; --!! true/ false geht natürlich auch !!--
        }


Das wird aufgerufen wenn sich die Bounds deines Containers (Canvas) geändert haben. Von dort aus kannst du auf DesiredSize deines Childs zugreifen...ist aber n hack...

Dann setzte du **IchZwingeDirMeinenVirtualisierungsWillenAuf ** auf deinen Wert...

Und dieses

25.06.2014 - 12:25 Uhr

Ok nochmal, vielleicht ist es so verständlicher :

    foreach (RowItem rowItem in matrixInfo.RowItems.OrderBy(x => x.Index))
    {
        RowControl rowControl = new RowControl(rowItem);
        grid.Children.Add(rowControl);
        rowControl.ApplyTemplate();
        rowControl.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
    }

Du möchtest die größe deiner Elemente berechnen, das brauchst du nicht!

rowControl.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); << normalerweise kommen dort die abmessungen des Containers hinein...also deines Canvas. Da der sich aber immer verändern kann, kommen am Ende auch andere Werte raus, je nach dem wie du deine Allignments angegeben hast(stretch/left/right) die wieder von den Abmessungen des Parents abhängig sind.

Das ist etwas das dir das WPF abnimmt. Wichtig ist dabei, dass du verstehst wie dieses Teil arbeitet. Sonst machst du "Workarounds" --- Euphemismus für Hacks..

Wir gehen mal von folgendem theoretischem Fall aus :

Du hast eine Liste von Elementen. Diese Elemente sollen in einem Container dargestellt werden.

Für die Darstellung verwenden wir ein ListView und deine Elemente sind einfache Strings.
Wenn du dir nun ein Control schreibst, wirst du die Liste als ItemsSource angeben und für die Darstellung wirst du ein DataTemplate verwenden.

Nun wird das DataTemplate auf eine Element der Liste angewendet, dadurch ergeben sich bestimmte Boundaries - diese sind die Größe deiner Elemente in der Visualisierung. Das ListView weis somit auch erst nach der Anwendung des DataTemplates die Größe seines Childs-Darsteller...und das auch erst wenn es sich selbst zeichnet. Vorher wird es nämlich nicht gebraucht.

Die Anordnung deiner Elemente wird vom Listview vorgenommen, da dort der Layout - Mechanismus bekannt ist, so sind die Koordinaten und Abmessungen nächsten Zeichnen wieder andere.

Die Architektur von WPF-UI unterscheidet sich enorm zu der Winforms - Architektur. In WPF ist alles etwas abstrakter gehalten, du musst dort keine Pixelschieberei betreiben. Du möchtest ja im Endeffekt nur, dass deine Elemente an einer bestimmten Stelle stehen, also versuch einfach zu überlegen : was muss ich dafür tun und was nicht.

Wenn man das so salopp ausdrückt:
Viele Entwickler denken noch in Pixeln und Events und UI-CodeBehind...das ist der Grund wieso die Lernkurve von WPF so riesig ist(was der Bauer nicht kennt, frisst er nicht - der menschliche Verstand mag keine Veränderung 😉 ). Es funktioniert nur noch zum Teil. Wie Entwickler von Mickrigweich haben da echt ein kleines Meisterstück der Softwarearchitektur hingelegt, die sind leider nur sehr Gewöhnungsbedürftig.

Schau mal hier : http://www.wpftutorial.net/HowToCreateACustomControl.html

Dort findest du die Grundlagen für die Erstellung von CustomControls...

Die MSDN Hilfe wirst du auch benötigen. Ist leider immer nicht immer so gut nachzuvollziehen, aber dort befinden sich alle Informationen die du brauchst.

23.06.2014 - 13:19 Uhr

Hallöchen,

FrameworkPropertyMetadataOptions metadata =    FrameworkPropertyMetadataOptions.AffectsMeasure |    FrameworkPropertyMetadataOptions.AffectsArrange;

Soweit ich mich erinnere sorgen die Flags dafür dass nach dem Aufruf deiner Changed-Methode die entsprechenden Neuberechnungen automatisch aufgerufen werden.

Wenn du die Virtualisierung nur über eine einfache Anzahl von Textboxen aktivieren möchtest, verwende einfach einen Trigger, das ist weitaus einfacher.

Aber ich glaub du kannst dir den Aufwand sparen, dort die Childs von den Canvas zu befüllen.

Ich vermute mal du hast ein ControlTemplate für dein MatrixControl geschrieben ?!...dort müsstest du den Canvas per TemplateBinding befüllen können, alternativ mit einem RelativeSourceBinding (gibts auch für TemplatedParent - TemplateBinding ist nur die "Kurzversion" davon).

Soweit ich das erkenne, befüllst du aus der MatrixInfo-Klasse die Child-Collection, in dem du dort für eine Daten-Implementierung einen fest vergibst. Dadurch verhinderst du aber dass du eigene DataTemplates verwenden kannst. So bist du darauf angewiesen immer wieder dein Custom-Control anzupassen wenn du eine Erweiterung machen möchtest.

Ein kleiner Hinweis noch: wenn sich deine Infoklasse ändert, fügst du alle Elemente der Infoklasse zum Canvas hinzu, löschst die Alten aber nicht. Würde bedeuten, wenn du die Eigenschaft erneut setzt, bleiben alle Objekte vom alten bestehen.

Die Entwicklung von eigenen Steuerelementen ist verzwickter, als ein einfaches UserControl, da es dort weitaus mehr Regeln zu beachten gibt. Es gibt einige Annotations für die Entwicklung von CustomControls(Defaults von Binding für Collections) und Namenskonventionen (PART_), sowie die Verwendung von externen Data/Style-Templates und CommandBindings.

Alternativ kannst du dein Matrixzeugs auch über ein AttachedProperty an einen Canvas hängen, da musst du dir nich das Control schreiben und über einen Trigger auf die Anzahl deine Textboxen kannst du die Virtualisierung an/aus schalten.

Wiki mal Decorator pattern, falls du das noch nicht kennst...es hilft beim verständnis der WPF UI Architektur.

vg

23.06.2014 - 12:49 Uhr

Hallöchen,

also erstmal :

  1. werden in deinem Listview die Elemente korrekt angezeigt, die aus deiner Datenquellen kommen ?
  2. Wenn ja...probier doch mal SelectedValue ... SelectedItem kann auch ein Frameworkelement sein, dass dein Element visualisiert(ListViewItem). Da klappt dann naturlich auch das Binding gegen eine gesuchte Objekteigenschaft nicht ...

Wenn mal ein Binding-Fehler auftritt, schau in die Ausgabe, da stehen meist Fehlermeldungen mit denen man mehr anfangen kann als mit :"klappt nicht" 😃

Bei Binding-Fehler irgendwas mit :error 40 : Binding Path not found... da steht dann auch in der langen Fehlermeldung der Typ gegen den du das Binding gesetzt hast.

Wenn das nicht hilft müsstest du etwas mehr posten, da ich so nur raten kann.

vg

18.06.2014 - 13:12 Uhr

Das ist wahrscheinlich der Kompressionsalgo der so lang dauert. 6k x 4k sind viele viele bytes die kopiert und komprimiert werden müssen, das kostet nun mal zeit. da wahrscheinlich die implementierte kompression auf singlecore läuft, bringen dir deine restlichen kerne auch nicht mehr viel, da dort die taktfrequenz des einzelnen kerns zählt und nicht die summe aller, das dürfte nur was bringen, wenn du mehrere bilder gleichzeit speichern möchtest.

ganz entscheidend bei arbeiten mit dem grafikspeicher ist die graka. eine onboard aufm lappi bringt da schmerzlich wenig performance, da nießt der prozessor kurz während der arbeitsspeicher fleißig hin und her kopiert.

16.06.2014 - 12:44 Uhr

Er schrieb doch :

Da wir die Einstellungen also nicht im Assembly selber vornehmen können, muss der Server entsprechend unsicher Konfiguriert werden. Das ist kein grosses Problem, da auf dem vServer sonst nichts läuft.

Das spielt doch keine Rolle, ob man das NIEMALS machen darf, wenn man so erstmal schauen kann ob eine Berechtigungsänderung im Verzeichnis das Problem an sich löst.

Wenn man weißt was in der Funktion roos_partner.globalProperties.getIcon16Class(String FileName) gemacht wird, kannst man davon ausgehen, dass die methode vielleicht noch was schreibt.

16.06.2014 - 12:28 Uhr

Soweit ich das sehe, fehlt deiner App das Zugriffsrecht aufs Dateisystem(der versucht Icon-Dateien aus irgendeinem unterordner zu lesen)
->roos_partner.globalProperties.getIcon16Class(String FileName) +67

Falls ihr das noch nicht Probiert habt:
Such dir das Verzeichnis in dem eure App liegt, und setze den Vollzugriff auf dieses Verzeichnis mit dem IIS_USR (das ist das Benutzerprofil vom IIS-Server unter dem alle gehosteten Web-Apps standardmäßig laufen) mit der angabe, dass die unterverzeichnisse die berechtigungen erben,

alternativ, falls das Programm andere credentials verwendet, entsprechend für diesen Benutzer setzen.

Da der IIS keinen regulären Zugriff auf das Dateisystem hat, ist das eins der üblichen Schwierigkeiten einer Web-App.

vg

13.06.2014 - 10:08 Uhr

Hallöchen,

ich hatte vor kurzem etwas Ähnliches gemacht, dabei habe ich eine Liste von Objekten aufgebaut und diese dann über Datatemplates in entsprechende Grafikobjekte umgewandelt.

  • Mein Canvas ist eine Ableitung der Canvas-Klasse das mit einem virtuellen Koordinatensystem arbeitet. Die Transformation des Koordinatensystem wird über die RenderTransform-Eigenschaft berechnet, damit kannst du dir die Umwandlung deiner Koordinaten deiner ganzen Objekthierarchie sparen
  • Die Basis meiner Objekthierarchie ist das hier :
  public class CanvasGeometry
    {
        public double Top { get; set; }
        public double Left { get; set; }
        public double Width { get; set; }
        public double Height { get; set; }

        public Brush BorderBrush { get; set; }
        public double BorderThickness { get; set; }
        public Brush Fill { get; set; }

        public Coordinates Coordinates { get; internal set; }
    }

Da hab ich zb ein RectangleObject

 public class CanvasRectangle : CanvasGeometry
    {
        public CanvasRectangle(Rect r, double borderThickness = 1, Brush borderBrush = null)
            : this(r.Left, r.Top, r.Width, r.Height, borderThickness, borderBrush)
        {
        }

        public CanvasRectangle(double left, double top, double width, double height, double borderThickness = 1, Brush borderBrush = null)
        {
            Top = top;
            Left = left;
            Width = width;
            Height = height;
            BorderBrush = borderBrush ?? Brushes.Black;
            BorderThickness = borderThickness;
        }
    }

Hier das DataTemplate

  <DataTemplate DataType="{x:Type visualizer:GraphicVisualizer}" x:Key="RectangleVisualizerTemplate">
        <controls:VirtualCanvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Coordinates="{Binding Path=Coordinates.Coordinates}">
            <Rectangle Width="{Binding Size.Width}" Height="{Binding Size.Height}"
                       StrokeThickness="{Binding LineThickness}" Stroke="{Binding BorderBrush}"
                       Canvas.Left="{Binding Position.X}" Canvas.Top="{Binding Position.Y}" />
        </controls:VirtualCanvas>
    </DataTemplate>

Diese kannst du dann über DataTemplates für den Canvas in ein Rectangle unwandeln...

Bei mir ist kein Notification-Changed implementiert, da ich nur statische Werte benutzt habe.

Vielleicht hilfts dir, vg

12.06.2014 - 11:00 Uhr

Schau mal hier :

#pragma warning (C#-Referenz)

Wie gesagt, dann wirst du mit dem #pragma disable warning weiter kommen

sollte auch ein enable dafür geben - schalte sie wieder ein, wenn du wieder haben willst.

Die AssemblyInfo.cs befindet sich im Projekt unter dem Knoten Properties (in deiner Wrapper-dll)

vg

12.06.2014 - 10:08 Uhr

Hallöchen,

schalte die Warnung einfach ab 😉 Das kannst du über die Visual Studio Einstellungen /Projekteinstellungen machen.

"#pragma disable warning" wäre das code-äquivalent - die kannst du auch in deine AssemblyInfo.cs schreiben, damit sollten die für die Projektdatei nicht mehr berücksichtigt werden (musst du aber mal selbst nachprüfen ob es geht)

Mehrfachladen einer Assembly geht immer. Es wird nur sehr schwer einen Typcast vorzunehmen.

Wenn du ein Interface ITest hast, das in einer Interface.DLL enthalten ist und diese assembly zb via Assembly.Load ein zweites mal geladen wird, wird dir ein solcher Aufruf:

ITest myTestInstance = new Test();

  • eine invalidcastexception um die ohren schmeißen, da dein ITest möglicherweise aus dem 2. Load aufruf verwendet wird und dein class Test : ITest aus dem ersten load stammt.

Grund dafür ist einfach, die Typen sind nicht die selben - kann man über type.guid prüfen.

Solche Fehler bekommt man erst wenn man Mechanismen verwendet, die stark das Reflection verwenden - falls man nämlich mal die Muße hat, nen IOC/DI-Mini-Framework zu coden.

In deinem Fall würde ich dir empfehlen einen auf einen Plugin-Mechanismus zurück zu greifen, diese lassen sich mit IOC-Frameworks sehr einfach implementieren.

Du hast dadurch die Möglichkeit Implementierungen auszutauschen ohne Code zu ändern.

vg

11.06.2014 - 16:40 Uhr

Du quälst dich wohl selbst gern 😃

kannst du das verhalten genauer beschreiben, da deine fehlerbeschreibung sehr unpräzise ist. Werden die Einträge gar nicht erstellt oder nur die Änderungen nicht gespeichert ?

Ich glaube eher dass dein Problem in der übertragung der Werte aus deinen Formularen liegt, als dass diese nicht gespeichert werden.

Prüfe nach welche Werte beim Speichern vorhanden sind und dann mach dich auf die Suche nach dem bösen Buben der dir das Leben schwer macht, ist meist der Mensch der n halben Meter vom Monitor entfernt sitzt 😉

Vielleicht kommt einfach die Änderung deiner Textbox durch irgendeine Bedingung oder einem Copy-Paste fehler nicht da an wo du es erwartest. Computer machen nie das was sie sollen, nur das was man ihnen befiehlt, - hüte dich vor deinen befehlen, sie könnten in erfüllung gehen -

vg

11.06.2014 - 16:10 Uhr

Hm da fehlt irgendwie was...der owner.

Du kannst solche Eigenschaften über Code-Snippets erstellen lassen...falls du es nicht kennst :

schreibe "propdp"
drücken tab

Da hast du alles was du brauchst, musst nur noch die richtigen Werte ausfüllen.

        public int MyProperty
        {
            get { return (int)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

Dein Xaml kannst du um einiges lesbarer und vor allem wartbarer machen in dem du KEIN RelativeSource verwendest. Das solltest du nur im Notfall verwenden - und den gibt es eigentlich, wenn du komplexere Datatemplates verwendest.

 UsePeriod="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.UsePeriods, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}"

--> Die Verwendung ElementName ist viel bequemer, perfomanter und einfacher zu lesen

UsePeriod="{Binding ElementName=userControl, Path=DataContext.UsePeriods, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}"

ElementName=userControl -> das ist der Name eines Objekts eines Steuerelements deines Xaml
Du kannst unter den NameSpace-Definitionen deine UserControls einen Namen für dein UserControl setzen mit

x:Name = "userControl" 

Ein TwoWayBinding ist bei DP normalerweise nicht notwendig, da dies für DP standard ist.

NotifyOnSourceUpdated
NotifyOnTargetUpdated
kannst du wahrscheinlich genauso weg lassen

Damit solltest du auch IntelliSense für deinen Path haben.

Die Lösung dieses Rätsels ist nicht so schwierig :

{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}

Das sucht nacht dem ersten Parent das vom Typ UserControl ist, oder davon erbt...
DataGridTextColumn wird wahrscheinlich davon erben...
DataGridFlexibleColumn << det wes ick nicht...

Es gibt die Klassen VisualTreeHelper und LogicalTreeHelper, damit lassen sich die sachen mit ein wenig Kenntnis der Architektur sehr gut analysieren.

vg

03.06.2014 - 15:32 Uhr

Heißt doch so schön, Humor ist's, wenn man trotzdem lacht...Das klingt ja fast nach einer Drohung.

Du hast wohl den Sarkasmus dieses Textes nicht ganz verinnerlicht. Nicht dass ich dich irgendwie zurückbelehren möchte. Ihr solltet vielleicht irgendwo vermerken was bei euch politisch korrekt ist und was nicht, damit man sich drauf einstellen kann, ist schließlich euer Forum.

Du kannst ja beim nächsten mal einfach ne Nachricht schreiben, wäre sicherlich sozial Kompetenter, als solch eine lächerliche Drohung zu schreiben.

vg und lass dich nicht ärgern

02.06.2014 - 15:23 Uhr

In solchen Fällen hol ich immer was zum draufhauen raus...alternativ kannst du es auch mit Kaffeeentzug, Rauchverbot oder nackt durchs dorf peitschen versuchen...

Eine kleine Anekdote:

Ich hatte mal zwei Katzen, ausm Tierheim. Die waren noch sehr jung, aber total scheu. Nach dem sie sich 3 Wochen lang unterm Sofa versteckt haben, wollt ich natürlich auch mal was anderes von denen sehen und hören als nur die Fressorgien in der Nacht weil sie Tagsüber nich rausgekommen sind. Die hatten echt schiss.

Irgendwann hatte ich die Schnauze echt voll und hab den Fressnapf in den Kühlschrank gestellt und alle paar Stunden wieder rausgenommen. Dann hab ich mich daneben gesetzt und gewartet.
Tag 1:

Die kleinen drecksviecher sind erst gar nicht rausgekommen

Tag 2:

Die kleinen Drecksviecher hockten unterm Küchentisch und wir haben "wer zuerst blinzelt" gespielt...ich hab verloren.

Tag 3:

Ich hatte 2 Tage lang gegessen....die nicht 😄 .... Irgendwann war der Hunger größer als die Angst.

Tag 4 - Jahr 4

Die kleinen Drecksviecher haben mich zum persönlichen Dosenöffner, Türaufmachen, Kloreiniger, Kraulmaschine und Gesprächspartner gemacht...sie waren verdammt anhänglich.

Musste sie leider abgeben....

So nun die Moral von der Geschichte:

Mit Geduld und ein bisschen diplomatischem Gefühl(Diplomatie ist nicht immer nett - siehe oben) kommt man immer weiter. Letztendlich ist der Kollege erstmal nicht dein Problem, denn dein Arbeitgeber kann schlecht von dir verlangen, dass du seine Lebeneinstellung änderst. Du solltest nur eins nicht tun : setzt dich nicht selbst unter druck, wenn der nicht will...und du ihn nicht 3 tage lang hungern lassen kannst...wird das auch nix, du kannst nix dafür wenn son sturer mensch sein gehalt nicht verdienen will. vor allem solltest du ganz offen kommunizieren, sonst fällt alles auf dich zurück. die tücke der menschlichen psychologie 😉

vg

02.06.2014 - 15:02 Uhr

Immer diese doofen Dreher ... War nur ein Test ob jemand aufgepasst hat 😄

02.06.2014 - 14:42 Uhr

Das liegt einfach daran, dass ihr das Steuerelement ständig neuzeichnet. Jede Änderung bedeutet ein Invalidate-Aufruf und das heiß wiederum -> repaint.

Und in OnPaint bedeutet es -> paint -> change -> paint -> change ->paint -> change .... den rest könnt ihr euch denken, heißt da sollte man das ganz und gar nicht rein tun. Der pixelt sich einfach zu tode.

So nu ein kleiner Rat wie man das beseitigen kann :

Das Flackern kommt daher, dass ständig alles neu gezeichnet wird und nicht nur der Teil der sich ändert.
Vererben und das Zeichnen überschreiben, das ist aber recht aufwändig und ihr müsst euch erstmal in die gdi einarbeiten.
Grafik in WinForms ist echt n eigenes Kapitel für sich.

Nun ein böser Hack :

Speichert beim Ändern der Werte den Zeitstempel, setzt ein suspendlayout und dann ein resumelayout wenn eure zeitspanne zb 250 hinter sich hat. sowas sind richtig böse hacks, die man nie machen sollte...ich schrieb ja: Timer sind häßliche kleine dinger, weil man meist für einen noch zwei weitere braucht oder irgendwelchen dussel mit denen anstellt.

Also vg und viel spass beim tüfteln

02.06.2014 - 14:22 Uhr

Hallöchen,

solche Programme sind ganz gut um sich in neue Themen einzuarbeiten. Wenn das noch aktuell ist und für dich interessant, versuch mal deinen Code selbst zu überarbeiten. Du kannst vieles in kleine Methoden auslagern (zum beispiel die Konsolenausgabe der Exceptions). Dadurch dürfte dein gesamter Quelltext auf ca 20 % des jetzigen schrumpfen.

Was die Benennung angeht...solange man weiß wofür etwas da ist reichts immer. Du musst selbst deinen Quellcode nach nem Jahr noch ohne Nachdenken lesen können, das machts auch viel leichter für andere, wenn die nicht raten müssen.

So als kleiner Anreiz für eine zusätzliche Optimierung:

Für die Kommandozeilen kannst du dir sowas wie nen "Wrapper" schreiben. Eine Klasse die in etwa so aussieht:

class CommandLineParams
{
    public CommandLineParams(string[] args)
    {
       // das hier schreibt man etwas eleganter aber sollte für dich verständlich sein
        foreach (var argument in args)
           ShowHelp = argument.ToLower() == "/?"; // hier kommt true oder false raus

    }
    //Gegen diese Eigenschaft kannst du dann prüfen ob du eine Hilfe anzeigen möchtest
    public bool ShowHelp {get; protected set;}
}

Nun kannst du direkt nach dem Programmstart deine args aus Main() parsen (->übersetzen) und dein Programm wird später viel einfach zu verstehen sein:

var commandLineParams = new CommandLineParams(args);
if (commandLineParams.ShowHelp)
{
   ShowHelp();
}

So hast du deinen Papyrusrollenquellcode (weils so schön untereinandersteht) viel leichter im Griff, wenn jemand nähmlich mal ein Programm wiederverwendet wird er oder sie vielleicht nach einer Methode suchen, die ShowHelp entspricht.

vg

22.05.2014 - 15:56 Uhr

Versuch mal SelectedValue auf null zu setzen...

Aber mal eine Frage am Rande, wie holst du dir denn das ausgewählte Element 😃 Versuch dir mal ein kleines Modell (MVVM Pattern mit WPF ) anzulegen. Damit kannst du recht bequem deine Daten bereit stellen und von der UI auch reinschreiben lassen (Binding von SelectedValue auf eine Eigenschaft in deinem Model)

Gibt millionen und abermillionen tuts dafür im internet.

viel erfolg

21.05.2014 - 13:52 Uhr

Regulärer Effekt der Win-Api...liegt in der Konfiguration des Fensters in dem du zeichnest. Ohne Quellcodes wirds mit'm Helfen schwer.

21.05.2014 - 13:49 Uhr

InitializeComponent(); << baut die UI auf...die controls werden erstellt und "irgendwann" vom UI-Thread gezeichnet.

Da wird dann auch das Binding (eigener Thread) initialisiert und da alles so schön asynchron läuft weißt du nicht ob dein DataContext zum Zeitpunkt der Bindings von Quelle zu UI schon hat oder nicht. Sogar wenn dein Binding im Xaml korrekt ist, kann der Datacontext zum zeitpunkt des Bindings = NULL sein.

Dispatcher.Invoke(action, Dispatcherpriorty.appidle) << damit kannst du das lösen...lean and mean.

Die Lernkurve bei WPF ist ziemlich gewaltig und am Anfang echt sehr nervig, da vieles vom Code-Aufbau zwar wie WinForms aussieht aber zum großen Teil komplett anders funktioniert. Wenn man mehr als ein Hello-World in WPF machen will, wirst du um ein MVVM nicht drum rum kommen. Für Kleinigkeit reichts zu wissen, wie das System arbeitet...aber bis du das alles durch hast, hast du auch MVVM gelernt 😛

Auf dieser Seite findest du ganz viele kleine Anleitungen für WPF und man bekommt einen schnellen Überblick für viele kleine Problemchen die man vielleicht noch bekommen wird, falls man sie noch nicht hat.

http://www.wpftutorial.net/

vg

21.05.2014 - 13:30 Uhr

Eigentlich reagiert das System nur auf Änderungen von Eigenschaften einer Resource und nicht auf die Änderung der Resource selbst. Versuch mal einfach dein vorhandenes Brush anzupassen anstatt es zu überschreiben. Ich vermute mal dass kein CollectoinChanged vom Resourcedictionary abonniert wird.

vg