Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Portal
  • |
  • Mitglieder
Beiträge von Master15
Thema: CommandTarget bei MVVM und Commands in mehreren ViewModels
Am im Forum: GUI: WPF und XAML

Aber damit ziehst du doch das HelloCommand aus dem EmployeeViewModel raus und definierst es im MainViewModel.
Letztlich würde das dazu führen, dass man das für jedes Command so machen müsste und dann per C#-Code das weiterreicht.
Das ergibt für mich keinen Sinn oder ich verstehe dich nicht richtig.

Thema: CommandTarget bei MVVM und Commands in mehreren ViewModels
Am im Forum: GUI: WPF und XAML

Zitat
Wenn du in deinem ersten Beispiel ein CommandParameter verwendest, und diesen in CanExecute abfragst, dann funktioniert es wie gewünscht.
Unter "3. Commands" wird eine CommandParameter-Eigenschaft erwähnt und in einem Beispiel mit einem ItemsControl veranschaulicht.

Im Beispielprojekt (MVVMTestProject.zip) gibt es eine ListView für die Mitarbeiter.
Da kann ich natürlich einen Button einfügen und auch das HelloCommand aufrufen:
<ListView ItemsSource="{Binding Employees}" SelectedItem="{Binding SelectedEmployee}">
	<ListView.ItemTemplate>
		<DataTemplate DataType="{x:Type myApp:EmployeeViewModel}">
			<StackPanel Orientation="Horizontal">
				<TextBlock>
					<Run Text="{Binding FirstName}" />
					<Run Text="{Binding LastName}" />
					<Run Text=" - " />
					<Run Text="{Binding Team.Name}" />
				</TextBlock>
				<Button Content="Hallo" Command="{Binding Path=HelloCommand}" />
			</StackPanel>
		</DataTemplate>
	</ListView.ItemTemplate>
</ListView>

Wofür ich die CommandParameter-Eigenschaft brauche, ist mir nicht klar.
Der Button ruft das HelloCommand auch so korrekt auf und wird auch ausgegraut, wenn CanExecute false entspricht.

Mir geht es z.B. um Buttons, die nicht in der ListView stecken, aber dennoch Commands eines "Sub-ViewModels" aufrufen.
<!-- Show Buttons databound to the ViewModel's Commands -->
<StackPanel Margin="0,5,0,0" Orientation="Horizontal">
	<Button Content="Neuen Mitarbeiter hinzufügen" 
			Command="{Binding AddNewEmployeeCommand}" 
			Style="{StaticResource GreenButton}" />
	<Button Content="Ausgewählten Mitarbeiter löschen" 
			Command="{Binding RemoveEmployeeCommand}" 
			CommandParameter="{Binding SelectedEmployee}" 
			Style="{StaticResource RedButton}"
			Margin="5,0,0,0" />
	<Button Content="Hallo Mitarbeiter" 
			Command="{Binding Path=SelectedEmployee.HelloCommand}"
			Margin="5,0,0,0" />
</StackPanel>

Ist SelectedEmployee gleich null, dann wird der Button "Hallo Mitarbeiter" nicht ausgegraut.
Wie JimStark und MarsStein schreiben, bleibt einen scheinbar nicht anderes übrig, in dem Fall zusätzlich über IsEnabled zu gehen.
<Button Content="Hallo Mitarbeiter" 
		Command="{Binding Path=SelectedEmployee.HelloCommand}"
		IsEnabled="{Binding Path=SelectedEmployee, Converter={StaticResource NullToBooleanConverter}}"
		Margin="5,0,0,0" />

Alternativ kann man natürlich auch im MainViewModel ein IsEmployeeSelected-Property (return SelectedEmployee != null) einbauen, was im Beispielprojekt schon enthalten ist:
<Button Content="Hallo Mitarbeiter" 
		Command="{Binding Path=SelectedEmployee.HelloCommand}"
		IsEnabled="{Binding Path=IsEmployeeSelected}"
		Margin="5,0,0,0" />

Deinen Satz, dass es mit einem CommandParameter wie gewünscht funktioniert, ist mir nicht klar.
Kannst du das bitte etwas genauer erklären, wie das mit einem CommandParameter funktioniert, aber ohne IsEnabled?

Thema: CommandTarget bei MVVM und Commands in mehreren ViewModels
Am im Forum: GUI: WPF und XAML

OK, danke für die Info.

Ich hatte angenommen, dass das Binding eines Commands intelligenter sei und prüft, ob das Objekt bzw. Command evt. null ist und dass dann generell als CanExecute=false angenommen wird.
Da bleibt dann vermutlich doch nur der Weg über IsEnabled und einem NullToBooleanConverter. Hatte gehofft das zu vermeiden.

Damit erübrigt sich dann auch meine Frage mit dem Fokus. Da muss ich mir im MainViewModel selbst merken, welches Dokument/Panel gerade aktiv ist und entsprechend das Command daran binden und ebenfalls mit IsEnabled arbeiten, falls gerade gar nichts aktiv ist.

Thema: CommandTarget bei MVVM und Commands in mehreren ViewModels
Am im Forum: GUI: WPF und XAML

Hallo zusammen,

ich habe vor über 10 Jahren eine private WPF-Anwendung entwickelt, in der ich eine Art MVC-Pattern nutze, wobei ich damals schon (Data)Bindings eingesetzt habe.

Mein Core-Projekt (Model-Schicht mit Anwendungslogik, Datenhaltungsschicht) habe ich mittlerweile unter .NET Core 3.1 laufen.
Die WPF-Oberfläche wollte ich aus diversen Gründen mal neu machen und MVVM eine Chance geben.

Ich habe früher auf RoutedCommand gesetzt und mit InputBindings bzw. CommandBindings gearbeitet.

Beim MVVM Pattern nutzt man ja z.B. DelegateCommand/RelayCommand und legt die jeweiligen Commands im ViewModel an.
So sind z.B. im MVVM-Beispiel hier aus dem Forum folgende Zeilen im MainViewModel enthalten:

public ICommand AddNewEmployeeCommand { get; set; }
public ICommand RemoveEmployeeCommand { get; set; }

Angenommen man bräuchte im EmployeeViewModell aber auch noch Commands, hier mal als primitives Beispiel einfach ein HelloCommand:

public ICommand HelloCommand { get; set; }

public EmployeeViewModel()
{
	HelloCommand = new RelayCommand(HelloCommand_Execute);
}

private void HelloCommand_Execute(object obj)
{
	System.Diagnostics.Debug.WriteLine($"HelloCommand_Execute: Hello {FullName}");
}

In MVVM-Beispiel würde ich das aus dem MainWindow gerne per Button auslösen:
<Button Content="Hallo Mitarbeiter" 
		Command="{Binding Path=SelectedEmployee.HelloCommand}" 
		Margin="5,0,0,0"
		Background="LightYellow" />

Prinzipiell funktioniert das schon, wenn ein Mitarbeiter ausgewählt ist.
Wenn jedoch kein Mitarbeiter ausgewählt ist, wird der Button nicht ausgegraut, was nicht sonderlich schön ist.

Sicherlich wird der ein oder andere jetzt fragen, warum ich den HelloCommand nicht einfach im MainViewModel anlege (also alle Commands im MainViewModel).
Es gibt bei Anwendungen durchaus Commands wie Cut/Copy/Paste, die man in mehreren ViewModels braucht, aber unterschiedliche Umsetzungen benötigen.
Hätte man z.B. ein DocumentViewModel (sei mal dahingestellt, ob das für Texte, Zeichnungen ausgelegt ist) und ein SolutionExplorerViewModel, dann möchte man z.B. bei Copy bei einem aktiven Dokument eine Linie kopieren bzw. im aktiven SolutionExplorer eine Datei kopieren. Also je nach Fokus ist das Ziel des Commands ein anderes. Aber selbst wenn gar kein Dokument ausgewählt ist bzw. der SolutionExplorer nicht den Fokus hat, sollten die MenuItems, (Ribbon)Buttons, ... ausgegraut werden.

Man kann zwar ein CommandTarget setzen, aber ich sehe keine Änderung:
<Button Content="Hallo Mitarbeiter" 
		Command="{Binding Path=SelectedEmployee.HelloCommand}"
		CommandTarget="{Binding Path=SelectedEmployee}"
		Margin="5,0,0,0" />

Wie setzt man das richtig um, wenn man nicht nur im MainViewModell Commands hat?

Thema: Problem mit Binding
Am im Forum: GUI: WPF und XAML

<Button
        Height="{x:Static SystemParameters.IconHeight}"
        Width="{x:Static SystemParameters.IconWidth}"
        Content="{Binding Path=Height, RelativeSource={RelativeSource Self}}"
        />

Thema: Threadsicherer Zugriff auf ObservableCollection und LINQ
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo herbivore,

danke für deine Antwort.

Zitat von herbivore
es liegt überhaupt nicht in der Zuständigkeit der SyncObservableCollection, den richtigen Dispatcher zu ermitteln/erzeugen. Diese Klasse kann überhaupt nicht wissen, welches der richtige Dispatcher ist. Im Grunde liegt es noch nicht mal in ihrer Zuständigkeit, überhaupt zu dispatchen. Wenn die Klasse aus Komfortgründen dispatchen soll, dann sollte der Benutzer der Klasse den richtigen Dispatcher übergeben, z.B. als Konstruktorparameter.
Also das mit dem Konstruktor-Parameter hatte ich mir auch schon gedacht und vermutet, dass das als Einwand kommen wird. Eigentlich finde ich den Dispatcher in der Collection einfach nur nervig. Der hat da nichts zu suchen. Am liebsten würden ich den komplett rausschmeißen, wäre da nicht WPF mit den Bindings, wenn man doch mal die Collection direkt in der View nutzen will (was in MVC erlaubt ist). Freilich könnte man eine Art Schicht (bzw. ViewModel) nochmal zwischenschalten, dort dann das CollectionChanged der Collection abfangen und die Steuerelemente bzw. eine ObservableCollection (die an Steuerelemente gebunden ist) dann über Dispatcher.Invoke befüllen.
Aber wenn man mit vielen Collections arbeitet, wird das im Code kein Spaß mehr (bzw. copy&paste). Ich frage mich mittlerweile ernsthaft, ob man eventuell ein Art Konverter bzw. eigene CollectionView zwischenschalten könnte, die sich um das Dispatchen kümmert.

Zitat
Aber auch das alles wird in der FAQ behandelt, siehe den ersten Absatz in "Spezielle Probleme" und Eleganteste Art aus Worker-Thread auf Controls zugreifen [generell Kontrollfluss zwischen Threads]. Deshalb nochmal meine Bitte, das Thema Dispatcher nicht weiter zu behandeln, da alles notwendige in der FAQ steht. (Wenn du findest, dass was fehlt, nimm Kontakt zum Team auf.)
Hab ich mir durchgelesen, ist informativ. Hilft mir aber nur bedingt weiter. Werde das Forum am nächsten Wochenende nochmal durchforsten.


Zitat von herbivore
Was die Synchronisation bei Linq angeht, hast du ja erkannt, dass (mindestens) ein lock fehlt. Wenn ich es richtig sehe, möchtest du über alle alle Features über alle Teilnehmer iterieren. Dann müsstest du wohl auch alle Sync-Objekte aller Feature-Collections aller Teilnehmer sperren.
Und genau darauf bezieht meine Frage. Wie lassen sich die Sync-Objekte der Feature-Collections jeweils sperren?

So etwas in der Art meine ich:

var features =  linqLock(teilnehmer.SyncRoot)
                from t in teilnehmer
                linqLock(t.Features.SyncRoot)
                from f in t.Features
                select f;

foreach (Feature f in features)
{
    //Mach was...
}
Ich hab mal vorsichtshalber mit lock(...), sondern linqLock(...) geschrieben, damit man das nicht fehlinterpretiert.
Ich habe den Eindruck, dass LINQ für so etwas nicht ausgelegt ist und man doch wieder auf for/foreach zurückgreifen muss.
Zitat
Oder eben doch mit Kopieren der Collections arbeiten.
Finde ich eine Notlösung, die ich so nicht akzeptieren kann/will (u.a. wegen Observer-Pattern).

Viele Grüße
Thomas

Thema: Threadsicherer Zugriff auf ObservableCollection und LINQ
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo,

ich wollte hiermit vom aktuellen Status dieses genannten Problems berichten, falls jemand das Thema verfolgt:

Zitat von Master15
Obwohl alles über Dispatcher.Invoke geht, konnte ich beim Öffnen von Fenstern mehrfach erkennen, das z.B. 3 Elemente in der ListView angegezeigt werden, obwohl in der Collection definitiv nur 2 vorkommen (ein Objekt wurde in der ListView mehrfach eingefügt, lässt sich auch an der doppelten blauen Selektierung erkennen).

Ich vermute das Problem gefunden zu haben. In meiner SyncObservableCollection<T> hole ich mir den Dispatcher mit Dispatcher.CurrentDispatcher:

public class SyncObservableCollection<T> : ObservableCollection<T>
{
    private Object syncRoot;
    private Dispatcher dispatcher;    

    public SyncObservableCollection()
    {
        syncRoot = new Object();
        dispatcher = Dispatcher.CurrentDispatcher;
    }

    //...

In der Model-Schicht habe ich so eine Art Hauptklasse (Singleton). Beim Starten der Anwendung wird alles vom UI-Thread initialisiert und auch diverse SyncObservableCollections werden angelegt. (Erst später greifen darauf andere Threads zu)
Es gibt natürlich teils auch Klassen, die wiederum eine bzw. mehrere SyncObservableCollections nutzen. Neue Instanzen hatte bisher aber immer der Benutzer über die Benutzeroberfläche (UI-Thread) erzeugt. D.h. auch der Konstruktor der SyncObservableCollection wurde vom UI-Thread aufgerufen.

Bei meinem ersten Beitrag hatte ich erwähnt:
Zitat von Master15
Ein Gerät hat eine Liste aus Teilnehmer. Ein Teilnehmer hat eine Liste aus Features.

Genau hier scheint das Problem zu liegen, denn neue Teilnehmer werden von einem Task/Thread eingefügt, der mit der Hardware kommuniziert. Diese Teilnehmer-Klasse beinhaltet eine SyncObservableCollection mit Features, aber diese Collection wird dann nicht vom UI-Thread angelegt. Damit holt sich Dispatcher.CurrentDispatcher leider nicht den Dispatcher vom UI-Thread, was man an unterschiedlichen Thread-Ids "dispatcher.Thread.ManagedThreadId" erkennt.

Um das Problem zu beheben, hatte ich zunächst folgendes getestet:

public class SyncObservableCollection<T> : ObservableCollection<T>
{
    private Object syncRoot;
    private static Dispatcher dispatcher;    

    public SyncObservableCollection()
    {
        syncRoot = new Object();
        
        if(dispatcher == null)
            dispatcher = Dispatcher.CurrentDispatcher;
    }

    //...

Da ich mir sicher bin, dass der UI-Thread als erstes diesen Konstruktor aufruft, sollte der dispatcher somit danach immer richtig gesetzt sein.
Leider habe ich nicht beachtet, dass das eine generische Klasse ist und es mit der Klassenvariable so nicht funktioniert, denn die ist bei jedem Typ trotzdem anders. Ich habe mir nur mal als Workaround folgendes zusammengeschustert:

public static class SyncObservableCollectionDispatcherHelper
{
    private static Dispatcher dispatcher;
    public static Dispatcher Dispatcher
    {
        get
        {
            if (dispatcher == null)
                dispatcher = Dispatcher.CurrentDispatcher;

            return dispatcher;
        }
    }
}

public class SyncObservableCollection<T> : ObservableCollection<T>
{
    private Dispatcher dispatcher;
    private Object syncRoot = new Object();
        
    public SyncObservableCollection()
    {
        syncRoot = new Object();
        dispatcher = SyncObservableCollectionDispatcherHelper.Dispatcher;
    }

    //...

Zumindest ist jetzt der Dispatcher immer der des UI-Threads, vorausgesetzt man sorgt dafür, dass der Konstruktor der Collection beim ersten Mal vom UI-Thread aufgerufen wird.
Die Lösung ist deshalb eine gefährliche Sache.

Am liebsten wäre mir natürlich sowas in der Art:

public class SyncObservableCollection<T> : ObservableCollection<T>
{
    private Dispatcher dispatcher;
    private Object syncRoot = new Object();

    public SyncObservableCollection()
    {
        syncRoot = new Object();

        Thread uiThread = Thread.GetMainThread(); //pseudo
        dispatcher = Dispatcher.FromThread(uiThread);
    }

    //...
Leider hab ich noch keine passenden Hinweise im WWW gefunden, ob es eine einfache Möglichkeit gibt an den Hauptthread der Anwendung zu kommen.



Bzgl. LINQ wollte ich auch nochmal nachhaken:
Ich will einfach nicht überall in den Properties Kopien der Collections erstellen, sondern mit den "originalen" Collections (observable) arbeiten, um eventuell auch an manchen Stellen das CollectionChanged-Event zu registrieren.

Folgender schnell eingetippter Code als Beispiel:

public class Teilnehmer
{
    private SyncObservableCollection<Feature> features = new SyncObservableCollection<Feature>();
    public SyncObservableCollection<Feature> Features
    {
        get { return features; }
    }
}

public class Feature
{
    public String Name { get; set; }
}

public class Test
{
    private SyncObservableCollection<Teilnehmer> teilnehmer = new SyncObservableCollection<Teilnehmer>();

    public Test()
    {
        Task.Factory.StartNew(() => { TeilnehmerUndFeatureErzeugung(); });
        Task.Factory.StartNew(() => { MachWasMitForeach(); });  
        Task.Factory.StartNew(() => { MachWasMitLinq(); });
    }

    public void TeilnehmerUndFeatureErzeugung()
    {
        Random random = new Random();

        while(true)
        {
            Teilnehmer t = new Teilnehmer();
            teilnehmer.Add(t);

            for (int i = 0; i < random.Next(10); i++)
                t.Features.Add(new Feature { Name = String.Format("Feature {0}", i) });
        }
    }

    public void MachWasMitForeach()
    {
        while(true)
        {
            lock (teilnehmer.SyncRoot)
            {
                foreach (Teilnehmer t in teilnehmer)
                {
                    lock (t.Features.SyncRoot)
                    {
                        foreach (Feature f in t.Features)
                        {
                            //Mach was...
                        }
                    }
                }
            }
        }
    }

    public void MachWasMitLinq()
    {
        while (true)
        {
            var features = from t in teilnehmer
                            from f in t.Features      // where... 
                            select f;

            foreach (Feature f in features) //Das führt natürlich zu einer InvalidOperationException
            {
                //Mach was...
            }
        }
    }
}

Der Code-Teil mit dem foreach macht keinerlei Probleme. Den finde ich allerdings etwas unleserlich, besonders wenn man noch diverse if-Bedingungen einbaut.

Für solche Fälle hat mir LINQ schon immer ganz gut gefallen. Aber hier hat man es leider mit mehreren Threads zutun. Man könnte den Code-Teil mit einem lock(teilnehmer.SyncRoot) umschließen, jedoch fehlt dann noch ein lock von der Features-Collection.
Gibt es in LINQ keine Möglichkeit das thread safe zu gestalten?

Bitte berichtigt mich, wenn ich hier irgendwas falsch interpretiere. Vielleicht hat ja der ein oder andere noch ein paar Tipps über die ich ich mich sehr freuen würde.

Viele Grüße
Thomas

Thema: Threadsicherer Zugriff auf ObservableCollection und LINQ
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Guten Tag,

Zitat von herbivore
mag sein, dass in den Büchern nicht direkt steht, ob die laufende Verarbeitung unterbrochen wird, aber in den Büchern und im Netz steht genug, um sich die Abläufe selber zu erklären. Deshalb gehe ich hier nicht näher darauf ein, sondern verweise dich auf [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke). Da das GUI single-thtreaded arbeitet und demzufolge die Nachrichten sequentiell verarbeitet und Dispatcher.Invoke einfach eine Nachricht schickt, ergibt sich die Antwort in einem Schritt. Deshalb bitte keine weiteren Fragen zu diesem Aspekt.

Leider kann man sich nicht alle Aspekte selber erklären. Wenn das so wäre, bräuchte man sicherlich auch kein Forum. Vielleicht bin ich aber auch einfach nicht so schlau wie ihr.
Diese sequentielle Verarbeitung hatte ich mir bisher immer schon so gedacht, aber nach den Effekten die ich zuletzt erlebt habe, hat sich mein Verständnis in Luft aufgelöst.
Obwohl alles über Dispatcher.Invoke geht, konnte ich beim Öffnen von Fenstern mehrfach erkennen, das z.B. 3 Elemente in der ListView angegezeigt werden, obwohl in der Collection definitiv nur 2 vorkommen (ein Objekt wurde in der ListView mehrfach eingefügt, lässt sich auch an der doppelten blauen Selektierung erkennen).
Aber egal, es muss dann andere Gründe haben, denen ich nachgehen werde. Habe heute mit einem .NET-Entwickler über den UI-Thread und Dispatcher geredet. Daran sollte es ja eigentlich nicht liegen. Muss mal suchen, wo da der Wurm drin ist.
Zitat von Abt
Ich frag mich aber: wenn Du Dir doch so unsicher in Deine Implementierung bist, wieso schaust Du Dir nicht eine der vielen vielen Implementierungen diesbezüglich an, die bereits existieren?
Hatte ich eigentlich schon im ersten Beitrag geschrieben, dass ich vor ca. 2 Jahren schon ein paar Implementierungen getestet hatte. Die meisten hatten das Problem, dass threadübergreifende Vorgänge problematisch waren, oder es nach außen kein SyncRoot-Object gab, das man für das Iterieren durch die Collection locken könnte. Auch hatte ich damals eine kleine Anwendung in WPF geschrieben, bei der ca. 20 Threads Listenoperationen durchführten. Ich kann mich nicht erinnern, dass das länger als ein paar Sekunden/Minuten gut ging und hatte dann selber noch etwas dran rumgebaut. Auch wenn ich selber nicht zufrieden war/bin, hat es mit der Collection bisher im 8h Betrieb keinerlei Probleme gegeben, wobei da die Listenänderungen meist vom Benutzer (UI-Thread) ausgegangen sind und die anderen Tasks größtenteils nur die Liste durchlaufen oder eventuell beim CollectionChanged-Event bestimmte Aktionen ausgeführt haben. Diese Konstellation hat sich etwas geändert und deshalb sind mir bei "Belastungstests" Deadlocks aufgefallen.
Daher dachte ich, dass es vielleicht Sinn macht hier mal im Forum nachzufragen, wie eine Implementierung solch einer Collection am sinnvollsten ist.

Gruß
Thomas

Thema: Threadsicherer Zugriff auf ObservableCollection und LINQ
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo,

zunächst Danke für die Antworten.

Zitat von Abt
Das ist aber falsch. Die Logik steckt bei MVC im Controller und nicht in den Modellen.
Ich kann nur jedem Entwickler raten, die eigentliche Logik der Software nicht in die Controller-Schicht zu packen.

Zitat von herbivore
gerade bei der Synchronisation von nebenläufigen Vorgängen und wegen mögliche Race Coditions erhält man nur eine sehr begrenzte Aussage. Auf einem anderen Rechner oder auch nach einen Betriebsystem-Update kann es ganz anders aussehen. Wo die Race Coditions vorher möglicherweise alle in die eine Richtung ausgegangen sind gehen sie dann möglicherweise öfter in die andere Richtung aus. Wo es vorher 8 Stunden ohne Probleme ging, knallt es nachher vielleicht alle fünf Minuten. Bei Synchronisierung sollte man wissen, was man tut. Einfach etwas auf Verdacht zu programmieren und nachher zu testen, ist keine gute Option. Siehe z.B. SyncQueue <T> - Eine praktische Job-Queue einen Fall, der sich durch einen einfachen Dauertest kaum finden lässt.
Zitat von michlG
Jedenfalls ist es immer wichtig dass man sich genau überlegt was man macht. Und sich nicht nur auf Dauertests verlässt.
Ich gebe euch beiden vollkommen Recht. Mir wäre auch eine anständige Lösung am liebsten, wenn ich die kennen würde.
Ich sehe aktuell nur die hier genannten Ansätze über den Dispatcher, was sicherlich auch noch Ärger macht oder aber den genannte Kompromiss in .NET 4.5, den ich auch noch nicht komplett durchschaue.

Ich muss zu meiner Schande gestehen, dass mir meine ältere zusammengebastelte Lösung über den Dispatcher auch schon Kopfschmerzen bereitet hat, da ich nicht so wirklich weiß, wie der Dispatcher intern arbeitet.

Mal so rein theoretisch angenommen:
Wir haben eine Art ObservableCollection, deren Operationen über den Dispatcher laufen und die Collection zudem ein SyncRoot-Objekt (public property) zur Verfügung stellt, um auch von außerhalb ein lock(list.SyncRoot) durchführen zu können.
Es gibt ein MainWindow (WPF), mit einem Button. Dort kann der Benutzer weitere Fenster öffnen, das jeweils eine ListView beinhaltet. Die Collection wird an die ItemsSource-Eigenschaft übergeben (bzw. Binding).

Über den internen Ablauf der Steuerelemente habe ich bisher leider nicht so viele nützliche Hinweise finden können. Sicherlich muss durch die Collection iteriert werden, um die Items anzuzeigen. Das CollectionChanged-Event wird auch registriert, um spätere Änderungen mitzubekommen. Doch was passiert, wenn genau während dieses Vorgangs ein Thread beispielsweise Add aufruft. Es wird dann durch den Dispatcher an den UI-Thread weitergereicht.
Was macht dann der UI-Thread? Unterbricht er eventuell sogar die Iteration durch die Collection, was dann zu einer InvalidOperationException (Collection was modified) führt, oder registriert er das Event womöglich zu früh bzw. zu spät?
Sorry für diese Frage, aber ich konnte das in meinen Büchern bzw. Tutorials nirgends nachlesen und oftmals ist ja sogar noch eine ListCollectionView beteiligt.

Viele Grüße
Thomas
@Michael (michlG): Der Solar Inspector sieht interessant aus

Thema: Threadsicherer Zugriff auf ObservableCollection und LINQ
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Guten Abend,

ich muss gestehen, dass ich die Antwort von Abt nicht so recht verstanden habe. Ich verwende in meinen meisten Anwendungen diverse Entwurfsmuster. Bezogen auf WPF setzte ich eine Art MVC ein, da ich mich mit MVVM bisher nicht recht anfreunden konnte. Business-Logik /DAL-Schicht zähle ich hier mit zur Model-Schicht.

Zitat von Abt
Du erreichst mit Deiner Implementierung die Thread-Sicherheit auf den Typ T
Hat dieser Typ eine Property mit einer Collection und Du benötigst hier ebenfalls eine Thread-Sicherheit, so musst Du diese dort ebenfalls implementieren.
Ist mir nicht ganz klar, wie das gemeint ist. Man könnte natürlich in einer Property (teilnehmer.Features) für Thread-Sicherheit sorgen, indem man nur von dort auf eine interne Liste zugreift, diese lockt und nach außen nur eine Kopie (toList(), toArray(),...) zurückgibt. Damit dürfte das sogar mit LINQ threadsicher sein. Damit bekommt man Änderungen (CollectionChanged-Event) aber dann an anderen Stellen nicht mehr mit.


Zitat von herbivore
man muss man zwischen Threadsicherheit und der Vermeidung von unzulässigen threadübergreifenden Vorgängen unterscheiden. Das erste bedeutet, dass man aus mehreren Threads gleichzeitig auf gemeinsame Daten zugreifen darf, ohne dass dadurch Probleme entstehen. Das kann man z.B. durch lock o.ä. erreichen. Das zweite bedeutet, dass alle Zugriffe aus einem bestimmten Thread erfolgen müssen, damit es keine Probleme gibt, unabhängig davon, ob die Zugriffe gleichzeitig erfolgen würden oder nicht. Das kann man z.B. durch Dispatcher.Invoke erreichen.

So wie ich es verstehe, musst du beides sicherstellen.

Danke für die Erklärungen, wieder was gelernt.
Ich denke, dass es in meinem Fall somit hauptsächlich threadübergreifende Vorgänge sind.

Ich habe in der Anwendung diverse Listen und da können zahlreiche Threads drauf zugreifen. Bisher war es oft so, dass Listenoperationen (Add, Remove,...) meist vom Benutzer (UI-Thread) ausgegangen sind und die Threads nur "gelesen" haben.
Diese Anforderung ändern sich jedoch etwas und besonders bei der Kommunikation mit der Hardware kann es vorkommen, dass Threads bzw. Events (z.B. SerialPort.DataReceived) in den Listen Änderungen vornehmen. Deshalb ist mir das mit den Deadlocks auch jetzt erst nach einigen Erweiterungen aufgefallen.

Hinzu kommt noch, dass bei Listenänderungen (CollectionChanged-Event) bestimmte Aktionen in der Model-Schicht von anderen Threads ausführt werden sollen. Deshalb dachte ich, dass eine ObservableCollection<T> da relativ gut passt. Manchmal kommt es halt vor, dass ich solch eine Liste auch mal in der View darstellen will. Besonders schön bei WPF sind natürlich die Bindings z.B. direkt an ItemsSource. Leider kommt WPF jedoch nicht damit klar, wenn das CollectionChanged-Event nicht vom UI-Thread gefeuert wird. Nur deshalb hatte ich damals den Kompromiss mit dem Dispatcher gemacht und dachte nun bei .NET 4.5, dass bei BindingOperations.EnableCollectionSynchronization diese Probleme der Vergangenheit angehören. Jedoch steige ich noch nicht so recht durch, was da genau passiert. Ich wollte mir nicht die Arbeit machen und den .NET Code studieren bzw. eigene Datenstrukturen überlegen. Für ein Privatprojekt ist mir das einfach zu viel Aufwand.

Eine echte threadsichere Liste (observable) kenne ich nicht. Die in .NET 4 eingeführten Thread-safe Collections sind zwar oftmals hilfreich, aber eher als Puffer zu gebrauchen (FIFO, LIFO).

Ich werde eventuell doch mal mit meiner o.g. TestObservableCollection<T> einen Dauertest durchführen. Wenn das ca. 8h ohne Absturz läuft, dann wäre ich schon zufrieden.

Gruß
Thomas

Thema: Threadsicherer Zugriff auf ObservableCollection und LINQ
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo C#-Profis,

zunächst wünsche ich hiermit ein gesundes und erfolgreiches neues Jahr 2013.

Ich habe schon länger ein paar Verständnisprobleme mit Threads, Tasks, ObservableCollections (auch in Verbindung mit WPF Bindings), LINQ und wollte deshalb hier im Forum mal bei euch nachfragen.

Ich habe eine Software, die viel asynchron mit der Hardware (Geräten) kommuniziert (z.B. RS232, USB, Ethernet, CAN,...).
Ich verwendet u.a. Threads, BackgroundWorker und mittlerweile am liebsten Tasks. Die Kommunikation selber ist kein Problem, da das meist nach dem Schema Producer-Consumer abläuft.
Ich hatte dafür früher die SyncQueue<T> von herbivore genutzt. Aktuell verwende ich in .NET 4 die BlockingCollection<T>.

Es kommt jetzt jedoch noch ein anderer Bereich hinzu, den ich hier als Beispiel nur vereinfacht darstelle:
Bei der Kommunikation mit den Geräten werden bestimmte Teilnehmer gemeldet und jeder Teilnehmer kann bestimmte Features aufweisen, die er unterstützt.
Kurz: Ein Gerät hat eine Liste aus Teilnehmer. Ein Teilnehmer hat eine Liste aus Features.

Während das Gerät verbunden ist, können jederzeit Teilnehmer oder sogar deren Features wegfallen oder neu hinzukommen.
Solche Änderungen müssen in der Software andere Einheiten mitbekommen und auch der Benutzer, der sich vielleicht gerade die Features eines Teilnehmers ansieht. Deshalb brauche ich dafür ObservableCollections die threadsicher sind.
Vor ca. 2 Jahren hatte ich mit SynchronizedObservableCollection, SafeObservableCollection, ThreadSafeObservableCollection Tests gemacht und letztlich mir einen Verschnitt daraus zusammengebastelt, wobei alle Listenoperationen mittels Dispatcher.Invoke an den UI-Thread weitergereicht werden, sodass auch das NotifyCollectionChanged-Event keine Probleme bei Bindings und WPF macht. Zudem wird intern ein syncRoot-Object gelockt. Auf dies kann man von außen (Property) zugreifen, z.B. wenn man durch die Liste iterieren will (foreach etc.).
Bisher funktionierte das glücklicherweise im Dauerbetrieb (ca. 8h). Neulich kamen aber noch ein paar Tasks dazu und ich müsste dann lange auf Fehlersuche gehen, da es immer mal wieder zu Deadlocks kam.

Der Grund so nebenbei: Ein Task sollte ein Feature durch ein anderes ersetzen und hat zunächst die Liste gelockt, mit Contains nach dem Feature gesucht, es mit Remove gelöscht und eine anderes mit Add hinzugefügt. Da die Listeoperationen jedoch intern mit Dispatcher.Invoke verarbeitet werden, versuchte der UI-Thread ebenfalls die Liste zu locken. Task und UI-Thread haben sich gegenseitig blockiert. Das asynchrone Dispatcher.BeginInvoke führte leider zu anderen Problemen.

Mir wäre es lieber, wenn die Listenoperationen direkt vom jeweiligen Thread/Task ausgeführt werden und nicht über den Dispatcher laufen.
Ich will eventuell auf .NET 4.5 umsteigen und mache deshalb gerade Tests mit der ObservableCollection<T> und BindingOperations.EnableCollectionSynchronization.

Habe mir jetzt mal eine total einfache TestObservableCollection<T> erstellt:

public class TestObservableCollection<T> : ObservableCollection<T>
{
	private Object syncRoot;

	public TestObservableCollection() : base()
	{
		syncRoot = ((ICollection)this).SyncRoot;
		BindingOperations.EnableCollectionSynchronization(this, syncRoot);
	}

	public Object SyncRoot
	{
		get { return syncRoot; }
	}
}

In WPF habe ich eine ListView und DataGrid (CanUserAddRows=True) verwendet und daran die Listen gebunden (zudem lassen sich mehrere Fenster öffnen). Es laufen mehrere Tasks, die Listenoperationen durchführen bzw. durch die Liste iterieren, wobei natürlich das SyncRoot-Objekt gelockt wird.
Bisher lief das reibungslos, aber ich wollte trotzdem mal nachfragen, ob das so Sinn macht, bevor ich das in eine Anwendung einbaue?



Es gibts dann noch so eine Sache mit LINQ. Ich habe LINQ wirklich schätzen gelernt und will das eigentlich nicht mehr missen, aber mir ist das mit der Threadsicherheit nicht klar.
Pseudocode:

public void MachWas(Gerät gerät)
{
	var featureNames = from teilnehmer in gerät.Teilnehmer
			           from feature in teilnehmer.Features
			           where feature.Id < 10 && feature.Status == FeatureStatus.On
			           select feature.Name;
			   
	foreach(String featureName in featureNames)
		//mach was...
}

Wie ich das mit zwei foreach-Schleifen threadsicher machen könnte, ist mir klar. Aber wie funktioniert das in LINQ? Wo baut man dort die lock-Anweisungen ein? Leider spuckt das WWW dazu nichts aus oder ich verwende die falschen Suchbegriffe.

Ich würde mich über eure Tipps freuen!

Viele Grüße
Thomas

Thema: Ungültiger threadübergreifender Vorgang [wieder mal: Bitte schaut vorher in die FAQ]
Am im Forum: GUI: Windows-Forms

Hallo,

das DataReceived-Event wird nicht vom UI-Thread ausgelöst. Somit macht dein Zugriff auf die textBox1 Probleme.

Ich bin zwar kein Windows.Forms Profi, aber Suche mal nach den Stichworten Invoke, BeginInvoke.

Thema: RS232: Wird DataRecieved (zuverlässig) gefeuert, wenn das Gerät alle Daten gesendet hat?
Am im Forum: Rund um die Programmierung

In meinen Anwendungen nutze ich auch schon lange die SerialPort-Klasse für diverse Hardwareansteuerungen (teils 8 Stunden Dauerbetrieb). Bisher hatte ich mit dem DataReceived-Event noch keine Probleme und alle Bytes sind angekommen.
Da die Bytes blockweise ankommen, lese ich diese aus und füge sie in eine Liste/Queue (Instanzvariable) hinzu. Danach mache ich die Auswertung, sodass die ankommenden Daten als Nachricht interpretiert werden.

Entscheidend ist natürlich auch das Protokoll/die Befehlsbeschreibung. Meist hatte ich das Glück, dass ein eindeutiger Delimiter/Separator (z.B. 0xFF) definiert ist und z.B. das nachfolgende Byte die Länge der Datenbytes angibt. So geht die Auswertung wunderbar, selbst wenn von einer Nachricht erstmal nur ein paar Bytes ankommen.

Auf eine bestimmte Länge warten. Hmmm... sowas ähnliches hatte ich neulich auch mal.
Da war das Protokoll sehr simpel aufgebaut. Es legte nur fest, dass eine Nachricht 13 Bytes lang ist. D.h. also im DataReceived-Event wiederum mit einer Liste/Queue (Instanzvariable) arbeiten. Bei der Auswertung dann jeweils 13 Bytes als Nachricht interpretieren, solange die Listenlänge ≥13 ist.
Das war auch ein Gerät, das ungefragt Nachrichten an den PC sendet (also kein Polling bzw. Request/Response).

Eine Sorge habe ich allerdings noch heute: Bei Geräten (RS232 bzw. USB via VCP) sind vermutlich diverse Puffer im Hintergrund.
Solch eine Auswertung mit dem Zählen von Bytes würde total ins Hemd gehen, wenn nach serialPort.Open() im Empfangspuffer noch "Müll-Bytes" z.B. von der letzten Verbindung bestehen. Dann kommt das Zählen total durcheinander. Mir ist das noch nicht passiert und auch andere Kollegen konnten mir keine Auskunft darüber geben.
Jemand hat mir mal empfohlen nach dem serialPort.Open() einfach mit Read() den Empfangspuffer zu leeren. Meiner Ansicht nach, kann das aber auch nach hinten losgehen, wenn da das Gerät schon mit dem Senden angefangen hat und man womöglich relevante Bytes ins Nirwana wirft.

Thema: Wo anfangen, wenn ein konkretes Projekt ansteht, das unbekannte Gebiete umfasst?
Am im Forum: Rund um die Programmierung

Zitat von Die Dose
Ich möchte ein Programm entwickeln welches sich unten Rechts in der Taskbar minimiert und im Hintergrund läuft. Es soll sich automatisch zu einem Webserver verbinden und dort den Text der in einer .txt oder ähnlichem steht auf dem PC anzeigen. Meine Frage ist nun... wo fange ich an?
Jeder Programmierer wird sich da seine eigenen Gedanken drüber machen, wie er die gestellte Aufgabe am besten mit seinem aktuellen Wissensstand löst. Ansonsten muss man sich weiteres Wissen aneignen (Bücher, WWW, ...)

Spontane Idee: WPF-Anwendung, ShowInTaskbar="False", NotifyIcon, WebClient...

Thema: Coding Styles Horror
Am im Forum: Smalltalk

Studienkollege wollte bei Projekt auf Nummer sicher gehen. Bei bool weiß man ja nie

bool isAktiv = //....
if (isAktiv)
{
    //Mach was
}
else if (!isAktiv)
{
    //Mach was anderes
}

Thema: Menüeinträge werden links außerhalb des Window dargestellt?!
Am im Forum: GUI: WPF und XAML

Hallo Jürgen,

ich würde aus Sicherheit trotzdem mal nachschauen, eventuell wurde das doch verstellt.

Habe mal kurz im Internet recherchiert. Drück doch mal:
[WINDOWS-LOGO] + [R]

Und dann das hier ausführen:
shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}

Habs gerade selber getestet und ein Bild angehängt, wie das eingestellt sein muss. Wenn das nicht hilft, weiß ich leider auch nicht weiter.

Gruß
Thomas

Thema: [erledigt] Viele Methoden verzögert ausführen: Timer wird vorzeitig vom GC freigegeben?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Guddn Tach,

Zitat von herbivore
ich habe nicht gesagt, dass man sich die Zeit als DateTime merken soll. :-) Man muss das natürlich in geeigneter Weise tun. Vermutlich bekommt man es mit Environment.TickCount hin.
Finde ich für meinen Fall zu kompliziert und da stellen sich mir gleich wieder neue Fragen. Ich bleibe lieber in dieser Anwendung beim KISS-Prinzip.
Zitat von ujr
Der optimale Wert hängt von der Granularität Deiner Aktionszeitpunkte und von der gewünschten Genauigkeit ab.
Genau auch aus diesem Grund bleibe ich erstmal bei der Lösung von winSharp93. So genau muss das nicht sein, nur ungefähr. Wichtig ist nur, dass der SchalteAus-Befehl gesendet wird, sonst brennt u. U. eine Spule durch. Ein paar ms hin oder her sind egal und Realtime wird das mit C# und Managed Code eh nie werden.

[Ironie]Wenn ich mal viiiiiiiiiiiieeeeeeeeel Zeit habe, setze ich ein Gentoo mit Linux Kernel 3 und RT- bzw. RTAI-Patch auf. Dann übergebe ich die Aufgabe per XML/JSON übers Netzwerk an ein Embedded System und dort wird das An- und Abschalten genaustens mittels Realtime Clock ausgelöst.[/Ironie]
Zitat von ujr
Außerdem muss man die Genauigkeit der Timer selbst (~55ms, außer beim Multimediatimer) berücksichtigen. Und die Dauer der Aktionen ist auch nicht ganz unwichtig, weil z. B. System.Threading.Timer auch auslöst, wenn der letzte noch nicht fertig ist.
Hört sich interessant an, werde ich mir mal mit dem Multimediatimer merken. Eventuell braucht man das mal bei einer anderen Anwendung.

Gruß
Thomas

Thema: Menüeinträge werden links außerhalb des Window dargestellt?!
Am im Forum: GUI: WPF und XAML

Zitat von talla
Ahh, das ist bei mir auch der Fall Hab mein Tablet schon so lange das ichs gar nicht mehr anders kenne, daher habe ich auch angenommen es ist das Standardverhalten.

Aber ein Grund mehr das nicht zu ändern. Die gewünschten Einstellungen des Users sollte man schon berücksichtigen.
hehe... Also ich bin am Anfang erschrocken, so ungefähr "Shit, wie stellt man den Käse wieder um".
Den MultiTouch-Rahmen hatte ich nur für Testzwecke angeschlossen. Bei mir ist es auch nur ein Desktop-Rechner, den ich zu 99% mit der Maus bediene.

Thema: Menüeinträge werden links außerhalb des Window dargestellt?!
Am im Forum: GUI: WPF und XAML

Hallo Jürgen,

eventuell verstehe ich dein Problem auch falsch, aber ist das wirklich nur in deiner Anwendung?

Ich habe aus Testzwecken einen MultiTouch-Rahmen. Als ich die Treiber vor einiger Zeit installiert hatte, gingen nach Neustart alle Menüs so auf, wie in deinem Bild.
Das lag daran, dass das für Rechtshänder eingestellt wird, sodass man besser mit dem Stift die Menüs auswählen kann.

Wenn der TouchScreen eingesteckt ist, kann man das in der Systemsteuerung unter "Tablet PC-Einstellungen" wieder auf "Linkshändig" einstellen und dann ist alles wieder normal.
Es gibt aber auch einen Registry-Schlüssel, wenn ich mich richtig erinnere.

Gruß
Thomas

Thema: System.Windows.Forms.Timer gestoppt erzeugen
Am im Forum: GUI: Windows-Forms

Zitat von Abt
Der Code würde nicht mal kompilieren, da Du in button2_Click auf ein Timer-Objekt zugreifen willst, das es gar nicht gibt.

Ich habe zwar nicht viel Ahnung von Windows.Forms, aber der Code kompiliert bei ihm bestimmt. Wie es Chris360 richtig erwähnt, wurde da sehr wahrscheinlich ein Timer im Designer reingezogen. Der nennt sich dann timer1 und ist vermutlich auch noch auf Enabled gesetzt.
Der timer1 in button1_Click bringt in der Tat gar nichts

trip2137 mach dir halt einfach mal eine Consolen/Debug-Ausgabe in timer1_Tick mit Zeitausgabe rein. Der eine Timer läuft bestimmt sofort los, wenn du die Anwendung startest.

Thema: [erledigt] Viele Methoden verzögert ausführen: Timer wird vorzeitig vom GC freigegeben?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Zitat von winSharp93


Timer timer = null;
timer = new //...
und schon kannst du in der anonymen Methode auf den Timer zugreifen.

Ok, super! Stimmt, darum hat es bei meinem Test vorhin nicht funktioniert, da ich es nicht auf null gesetzt habe.

Funktioniert auch wunderbar, "Schalte aus" ist bei impuls > 0 jetzt immer brav da.
Vielen Dank! Dieses Thema ist auf meiner TODO-Liste somit erstmal erledigt
Zitat von winSharp93
Man kann aber keine Threads "entziehen".
Etwas in die Richtung ist nur mit .ContinueWith bzw. .ContinueWhenAll möglich (in diesem Kontext aber nicht sinnvoll einsetzbar). Aber auch da verzichtet der Code quasi frewillig auf den Thread.
Interessant. Ich nutze statt eines Background-Threads, BackgroundWorker und co neuerdings gerne mal einen Task, u.a. auch wegen .ContinueWith, CancellationTokenSource und der einfacheren Handhabung. Bei .ContinueWith ist mir nur mal aufgefallen, dass dies oftmals auch ein anderer Thread aufruft (Ausgabe von Thread.CurrentThread.ManagedThreadId) und manchmal auch der Gleiche.

Thema: [erledigt] Viele Methoden verzögert ausführen: Timer wird vorzeitig vom GC freigegeben?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo,

danke erstmal an herbivore für die Erklärung der Task, klingt plausibel.

Ich habe eigentlich vermutet, dass die TPL so intelligent ist, dass einem Task, der längere Zeit durch Thread.Sleep, WaitHandle (ManualResetEvent, AutoResetEvent) blockiert werden, der Thread entzogen wird. Der Thread könnte dann für andere Aufgaben/Tasks genutzt werden.
Leider steht in meinen älteren C#-Büchern nichts drin. Im Internet findet man zwar viel über die Programmierung, aber was im Hintergrund passiert, bleibt offen. Im Openbook Visual C# 2010 Kapitel 11.3 ist es auch nur allgemein formuliert.


Die Lösung von winSharp93 verstehe ich ungefähr so:


private List<Timer> timerList = new List<Timer>();
public void Aktiviere(int impuls)
{
    SchalteAn();

    if (impuls > 0)
    {
        Timer timer = new System.Threading.Timer(t => 
        { 
            SchalteAus();

            //lock (((ICollection)timerList).SyncRoot)
            //    timerList.Remove(timer);

            //timer.Dispose();
        }, null, Timeout.Infinite, Timeout.Infinite);
                
        lock(((ICollection)timerList).SyncRoot)
            timerList.Add(timer);
                
        timer.Change(impuls, Timeout.Infinite); //Manueller Timer-Start
    }
}

Wobei das mit dem Löschen und Dispose noch so eine Sache ist (auskommentiert). Bin gerade überfragt, wie ich einen Verweis auf den Timer am einfachsten übergeben kann.

Zur Lösung mit nur einem Timer von ujr und herbivore:
Verstehe ich euch da richtig? Einen Timer laufen lassen, der z.B. jede 10ms eine Methode aufruft. Die überprüft dann, ob es in einer Liste Einträge gibt (z.B. Aus-Befehl mit TimeStamp), deren Zeit abgelaufen ist. Ist das der Fall wird ein entsprechender Aktion ausgeführt und der Aus-Befehl aus der Liste gelöscht.
Nur in dem Fall frage ich mich was der optimale Wert für das Timer-Intervall ist.

Einfache Dinge können so kompliziert sein... uff...
Mein innerer Timer sagt jetzt erstmal Essenszeit

Gruß
Thomas

Thema: [erledigt] Viele Methoden verzögert ausführen: Timer wird vorzeitig vom GC freigegeben?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Guten Abend,

Zitat von winSharp93
Ist aber eine ganz schlechte Idee, da es ziemlich viele Resourcen zieht, lauter sleepende Threads in der eigenen Anwendung zu haben.
Evtl. führt das letztlich sogar dazu, dass dein kompletter TaskScheduler blockiert und letztlich die Wartezeiten überhaupt nicht mehr stimmen.
Ich muss gestehen, dass ich mir darüber noch keine Gedanken gemacht habe. Gut dieses Problem hatte ich auch noch nie. Normalerweise nutze ich einen Timer, um Dinge zyklisch auszuführen und da hat man dann natürlich einen Verweis drauf.

Theoretisch angenommen man hat 1000 Tasks, die mittels Thread.Sleep, WaitHandle (ManualResetEvent, AutoResetEvent) blockiert werden. Heißt das jetzt, dass auch tatsächlich 1000 Threads blockieren?

Zitat von winSharp93
Aber gut - eine Liste wäre wohl zu einfach.
Um das gehts mir eigentlich gar nicht. Ich versuche nur von vornherein eine Software so zu programmieren, dass die verwendeten Listen auch einen Sinn für die eigentliche Anwendung ergeben. In diesem Fall pfeift aber irgendwie mein innerer Schweinehund, eventuell mal wieder unbegründet.
Aber wenn das mit einem Task wirklich nicht vertretbar ist und es keine andere Möglichkeit gibt, dass man eine Methode verzögert ausführen kann, dann werde ich das so machen müssen.

Gruß
Thomas

Thema: [erledigt] Viele Methoden verzögert ausführen: Timer wird vorzeitig vom GC freigegeben?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

hmmm... irgendwie hatte ich es geahnt, dass das mit der Liste kommt. Nagut, werde ich mir mal überlegen. Eventuell lasse ich auch den Task.
Trotzdem Danke!

Thema: [erledigt] Viele Methoden verzögert ausführen: Timer wird vorzeitig vom GC freigegeben?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo winSharp93,

Zitat von winSharp93
du musst den Timer in einer Instanzvariable speichern, da er sonst vom GC entsorgt wird.

Die Instanzvariable würde dann aber bei jedem Aufruf von Aktiviere(200) wieder überschrieben werden. D.h. nur auf den letzten erzeugten Timer hat man dann durch die Instanzvariable einen Verweis.
Wenn ich das richtig verstehe, wäre das sogar noch schlimmer, da so ein Fehler noch schwieriger zu finden ist.

Vielleicht gibt es ja noch andere Ideen.

Gruß
Thomas

Thema: [erledigt] Viele Methoden verzögert ausführen: Timer wird vorzeitig vom GC freigegeben?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo C#ler,

ich habe eine Methode, die von diversen anderen Threads aufgerufen werden kann. Diese soll etwas anschalten. Ist eine Impulsdauer angegeben, soll es automatisch wieder ausgeschaltet werden. Der Aufrufer darf aber nicht blockiert werden (z.B. durch Thread.Sleep(impuls)).

Das Problem habe ich mal in eine separate Testanwendung gepackt:


/// <summary>
/// Aktiviert irgendwas
/// </summary>
/// <param name="impuls">Impuls in ms für automatische Abschaltung</param>
public void Aktiviere(int impuls)
{
    SchalteAn();

    if (impuls > 0)
        new System.Threading.Timer(t => { SchalteAus(); }, null, impuls, Timeout.Infinite);
}

private void SchalteAn()
{
    Debug.WriteLine(DateTime.Now.ToString("mm:ss.fff") + " Schalte an");
}

private void SchalteAus()
{
    Debug.WriteLine(DateTime.Now.ToString("mm:ss.fff") + " Schalte aus");
}

Ich habe jetzt mehrfach in der Testanwendung Aktiviere(200) aufgerufen und festgestellt, dass nicht immer die Ausgabe "Schalte aus" erscheint.
Meine Vermutung ist, dass mir da der Garbage Collector einen Strich durch die Rechnung macht und meinen Timer freigibt, da kein Verweis darauf vorhanden ist. Laut MSDN scheint meine Vermutung zu stimmen.

Ersetzt man den Timer durch einen Task, funktioniert es scheinbar immer:


Task.Factory.StartNew(() =>
{
    Thread.Sleep(impuls);
    SchalteAus();
});

Macht das mit dem Task Sinn, oder gibt es beim Timer irgendeinen Trick, dass das immer funktioniert?
Gibt es eventuell eine andere/bessere Lösung, eine Methode verzögert auszuführen?

Gruß
Thomas

Edit: [erledigt]

Thema: Eigenschaften mehrerer Hauptfenster sichern
Am im Forum: GUI: WPF und XAML

Hallo,

hat wirklich niemand einen Tipp für mich ?(

Eventuell einfachere Frage:
Gibt es irgendeine Möglichkeit einmal eine Methode aufrufen zulassen, wenn der Benutzer X, Alt+F4, Datei->Beenden oder in Taskleiste "Fenster schließen" aufruft (abgesehen von Prozess beenden)?
ApplicationCommands.Exit wird leider auch meist nicht ausgelöst.

Gruß
Thomas

Thema: Eigenschaften mehrerer Hauptfenster sichern
Am im Forum: GUI: WPF und XAML

Hallo,

das mit dem Singleton habe ich sogar schon für was anderes drin.
Eine weitere Collection ist natürlich kein Problem, in der ich dann selber die geöffneten Hauptfenster verwalte.
Im MainWindow-Konstruktor könnte ich dann folgendes einbauen:

View.Instance.MainWindowList.Add(this);

In OnClosing oder OnClosed zudem:

View.Instance.MainWindowList.Remove(this);

Ich will jetzt nichts falsches sagen, aber ich glaube das kommt aufs gleiche hinaus, wie bei Application.Current.Windows. Wenn die OnExit-Methode in der App.xaml.cs aufgerufen wird, sind die einzelnen OnClosing der Hauptfenster bereits ausgeführt worden. Somit ist die MainWindowList wieder leer.

Hatte schon ein paar andere Versuche, bisher hat mir aber kein Ansatz gefallen. Ich will eigentlich nur, dass die Eigenschaften (Position, Fensterstatus,...) mehrerer Hauptfenster gespeichert werden, sodass beim Start wieder genau so viele Hauptfenster geöffnet werden (Multi-Head), wie der Benutzer zuletzt geöffnet hatte.

Das sollte auch unabhängig davon sein, was der Benutzer verwendet (X, Alt+F4, Datei->Beenden). Bei mehr als einem Hauptfenster würde ich noch gerne einen Dialog haben, damit man nur das Fenster oder die ganze Anwendung schließen kann:

//Ungetestet
if (View.Instance.MainWindowList.Count > 1)
{
    MessageBoxResult result = MessageBox.Show("Soll die gesamte Anwendung beendet werden?", "", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);

    switch (result)
    {
        case MessageBoxResult.Yes: //Anwendung beenden
            Application.Current.Shutdown();
            break;
        case MessageBoxResult.No: //Nur dieses Fenster schließen
            this.Close();
            break;
        case MessageBoxResult.Cancel:
            Debug.WriteLine("Der Benutzer will doch nichts schließen");
            break;
    }
}
else 
{
    //Wenn nur dieses Fenster offen, einfach schließen. (ShutdownMode==OnLastWindowClose)
    this.Close();
}

Problem ist nur, wer das aufrufen soll. Bei Datei->Beenden kein Problem. Aber für X, Alt+F4 finde ich nur das Abfangen mit OnClosing. Das würde dann aber für jedes Fenster aufgerufen werden und nicht einmal.

Ich sehe momentan keine einfache Lösung.

Gruß
Thomas

Thema: Eigenschaften mehrerer Hauptfenster sichern
Am im Forum: GUI: WPF und XAML

Hallo C#ler,

bisher hatte ich immer in den WPF-Anwendungen ein Hauptfenster. In der überschriebenen Methode OnClosing habe ich die relevanten Eigenschaften in den Settings gesichert, um u.a. die Fensterposition beim nächsten Start wiederherzustellen.

In einer anderen WPF-Anwendung erlaube ich dem Benutzer mehrere Hauptfenster zu öffnen.
Ist nur ein Fenster aktiv und man drückt X, Alt+F4 oder Datei->Beenden so ist das mit OnClosing kein Problem.

Bei mehreren geöffneten Hauptfenstern wäre es gut, wenn ein Dialog erscheint, was man machen möchte (Nur dieses Fenster schließen oder gesamte Anwendung beenden).
Möchte man die gesamte Anwendung beenden, so dachte ich, dass ich die OnExit-Methode in der App.xaml.cs überschreibe und darin die relevanten Werte aller Fenster sichere. Leider ist dort dann aber die Anzahl der Fenster schon 0:

Application.Current.Windows.OfType<MainWindow>()

OnClosing der einzelnen Windows bringt mir auch nichts, da die unabhängig voneinander sind.
Eine Idee ist es nach dem Aufrufen von X, Alt+F4 oder Datei->Beenden eine eigene Methode (z.B. Close) zum Beenden in der App.xaml.cs aufzurufen. Darin wird erstmal alles gesichert und erst danach erfolgt der Shutdown-Befehl.
Bei Datei->Beenden ist es kein großes Problem, dort dann aufzurufen:

((App)Application.Current).Close();
Das wird dann genau einmal aufgerufen. Danach kann dann alles beendet werden. OnClosing der einzelnen Fenster sind somit irrelevant.

Aber wie sieht es bei X und Alt+F4 aus? Gibt es da eine Möglichkeit das einfach abzufangen und einmal die Close-Methode aufzurufen?

Gruß
Thomas

Thema: Exception Handling bei DllImport
Am im Forum: Rund um die Programmierung

Hallo,

erstmal vielen Dank für eure Hilfe.

Zitat von ProGamer
Also ich stelle mal die behauptung auf dass es in nem Thread zu einer Exception kommt.
Kannst ja mal versuchen das AppDomain.UnhandledException-Ereignis (WPF) oder Application.ThreadException-Ereignis (WinForms) zu abonnieren.

Das es evt. in einem Thread zu einer Exception kommt, habe ich auch schon in Erwägung gezogen. AppDomain.UnhandledException kannte ich noch gar nicht, hatte bisher in WPF-Anwendungen nur DispatcherUnhandledException abgefangen (App.xaml.cs).

protected override void OnStartup(StartupEventArgs e)
{
	this.DispatcherUnhandledException += new App_DispatcherUnhandledException;
	AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}

void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
	Debug.WriteLine("App_DispatcherUnhandledException: " + e.Exception.Message);
}

void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
	Exception ex = (Exception)e.ExceptionObject;
	Debug.WriteLine("CurrentDomain_UnhandledException: " + ex.Message);
	Debug.WriteLine(ex.StackTrace);
}

CurrentDomain.UnhandledException werde ich mir in Zukunft merken, um Fehler schneller zu finden.

Zitat von winSharp93
aktiviere mal in VS alle Exceptionbreakpoints (via Debug-Exception); also auch die für C++ und COM-Exceptions.
Dann solltest du sehen, wo genau die Exception auftritt bzw. um welche es sich handelt (und wie diese weitergereicht wird).
Wieder was gelernt. Ich habe immer in "Debuggen->Optionen und Einstellungen..." nach Einstellungen gesucht. In "Debuggen->Ausnahmen..." habe ich jetzt mal alles aktiviert und siehe da, der Debugger war gleich viel gesprächiger.

Der Fehler tritt anscheinend wirklich nur auf, wenn die Bluetooth Verbindung abreißt und man einen Befehl von PC an die Wiimote sendet. Danach wird an einer anderen Stelle beim asynchronen Lesen eines Streams eine IOException geworfen, jedoch nirgends angefangen.

Das müsste dann sogar der Fehler http://wiimotelib.codeplex.com/workitem/7980 hier sein.
Nur die Lösung gefällt mir nicht... Einfach nur ein catch-Block mit Debug-Ausgabe macht wenig Sinn. Am schönsten wäre natürlich, wenn bei wm.SetRumble(isChecked) die IOException geworfen wird. Wird vermutlich durch den asynchronen Lesevorgang schwierig.

Habe sowas in diesem Zusammenhang noch nie selber programmiert, aber evt. bietet sich hier ein Exception-Event in der Klasse Wiimote an (z.B. wm.ErrorReceived), so nach dem Schema wie bei serialport.ErrorReceived!?

Gruß
Thomas