Ich verwende WPF, aber die konkreten asynchronen Services, welche dann von der oben geposteten Klasse abgeleitet sind, werden dem PersonenManager über einen IOC Container injeziert.
Dieser Container registriert die asynchronen Services innerhalb der App.xaml.cs also direkt im GUI-/Main-Thread.
Statt mit extra Threads und Control.Invoke, kann man auch mit der BackgroundWorker-Klasse ungültige threadübergreifende Vorgänge vermeiden. Dazu müssen bei BackgroundWorker alle Zugriffe auf das GUI einfach aus den ProgressChanged- oder RunWorkerCompleted-EventHandlern durchgeführt werden. Wenn man sich daran hält, ist explizites Control.Invoke bei BackgroundWorker nicht erforderlich, denn die genannten EventHandler werden hinter den Kulissen automatisch per Control.BeginInvoke aufgerufe
Und das ist es ja, was ich auch erwartet habe ... vielleicht zum besseren Verständniss ein Ausschnitt aus meinem BackgroundWorker:
public abstract class MyAsyncServiceBase
{
(...)
protected void AnyGenericInvoke<TAsyncRequest, TAsyncResponse>(TAsyncRequest asyncRequest, Func<TAsyncRequest, TAsyncResponse> method, Action<TAsyncResponse> asyncCallback)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
args.Result = method(asyncRequest);
};
worker.RunWorkerCompleted += (sender, args) =>
{
if (asyncCallback != null)
asyncCallback(args.Result as TAsyncResponse);
};
using (worker)
{
worker.RunWorkerAsync();
}
}
}
Ich hoffe es gibt hier jemanden, der mich "erleuchten" kann ..
*edit Ja, den habe ich auch gerade gefunden, den Thread. Aber wie ich ja oben zitiert habe sagt auch dieser aus, dass es mit dem Backgroundworker so funktionieren sollte..
In schmeisse in keiner asynchronen Methode ein Event.
public class PersonViewModel()
{
public ObservableCollection<Person> People;
private IPersonManager _personManager;
public PersonViewModel(IPersonManager personManager)
{
_personManager = personManager;
People = new ObservableCollection<Person>();
_personManager.People.CollectionChanged += ((object sender, NotifyCollectionChangedEventArgs e) => this.People = _personManager.People);
}
}
public class PersonManager
{
public ObservableCollection<Person> People;
private IAsyncPersonService _asyncPersonService;
private IPersonChangeInitiator _initiator;
public PersonManager(IAsyncPersonService asyncPersonService, IPersonChangeInitiator initiator)
{
People = new ObservableCollection<Person>();
_asyncPersonService = asyncPersonService;
_initiator = initiator;
_initiator.OnAddNewPerson += onAddNewPerson;
}
public void onAddNewPerson()
{
_asyncPersonService.InvokeGetAnyPerson(new GetAnyPersonRequest(), invokeGetAnyPersonCallback);
}
private void invokeGetAnyPersonCallback(GetAnyPersonResponse response)
{
if(response != null)
{
Person person;
if(response.IsNewPerson)
{
person = response.Person;
People.Add(person);
}
else
// Für dieses Beispiel setze ich einfach mal voraus, dass der Nachname eindeutig ist
person = People.GetPersonByName(response.Person.Name);
doAnythingWithPerson(person);
}
}
}
Der AsyncPersonService startet die aufgerufene Methode über einen Backgroundworker asynchron.
Wenn der PersonChangeInitiator das erste mal das OnAddNewPerson-Event schmeisst, und es sich um eine neue Person handelt, wird diese ohne Probleme der Liste im PersonManager hinzugefügt
und das CollectionChanged-Event kann ohne Probleme im View-Model gefangen werden. Das bedeutet, auch die Liste im ViewModel hat nun die neue Person.
Problem 1:
Schmeisst der PersonChangeInitiator jedoch ein zweites mal dieses Event, natürlich dieses mal mit anderen Werten um noch eine andere Person anzulegen, bekomme ich bei "People.Add(person);" folgende Exception:
Fehler
Von diesem CollectionView-Typ werden keine Änderungen der "SourceCollection" unterstützt, wenn diese nicht von einem Dispatcher-Thread aus erfolgen
Problem 2:
Wird nach dem Ersten OnAddNewPerson-Event vom PersonChangeInitiator erneut das OnAddNewPerson-Event geschmissen, und in der GetAnyPersonResponse befindet sich keine neue Person sondern die gerade zuvor angelegte,
kann ich mir das zuvor angelegte Personenobjekt über den Namen aus der People-Liste holen("People.GetPersonByName") und in der Methode "doAnythingWithPerson(person)" zwar verändern,
dass ViewModel wird darüber allerdings nicht mehr über das CollectionChanged-Event der People-Liste benachrichtigt.
Ich vermute mal, dass diese beiden Probleme miteinander zusammenhängen, richtig?
Ich hatte schon vor etwas längerer Zeit mal Probleme mit Threading, Dispatcher usw. Hatte damals aber auch keine MVVM-Anwendung und habe es einfach im Codebehind über so einen Dispatcher-Workaround gedreht.
Da ich mich damals aber noch nicht so gut mit Programmierung auskannte, war der Code sehr unübersichtlich und schlecht geschrieben, auch wenn er funktionierte.
Deshalb hatte ich mich zum Entwurf einer komplett neuen Struktur entschlossen, die nun wie eben gesagt auf dem MVVM Pattern basiert und mit asynchronen Methoden arbeitet.
Ich dachte eigentlich, durch das Verwenden des Callbacks bei asynchronen Methoden könnte ich sicher stellen, dass ich immer wieder im gleichen Thread lande, nachdem die asynchrone Methode durchgelaufen ist.
Von daher hatte ich mir davon eigentlich erhofft, die Dispatcher-Problematik umgangen zu haben.
Könnt ihr mir eventuell weiter helfen, was ich verändern / anpassen muss um die Probleme zu beheben?
Und wo liegt der Fehler in meiner Denkweise, bezogen auf das Threading-Verhalten der MVVM Struktur mit asynchronen Methoden?
Ich war auch der Meinung das die Referenz übergeben wird und genau das war auch der Grund warum ich so ratlos war.
Letztendlich lag das Problem nun darin, dass die Peson in der Liste zwar aktualisiert wurde (was ich auf deine Anmerkung hin direkt im Callback getestet habe) aber ich in meinem Unit Test noch die alte Liste hatte weil die Überprüfung des Tests nicht gewartet hat, bis der Callback fertig gelaufen war.
Das bedeutet der Fehler lag in meinem Unit Test, nochmals vielen Dank :)
Nein, weil ich nicht wirklich weiss wo ich es fangen sollte um mein Problem zu lösen. Ich will ja bloß, dass die Person aus der Liste innerhalb der Liste aktualisiert wird wenn eine Eigenschaft der Person in einer asynchronen Methode geändert wird.
Ich habe die Person Klasse nun wie folgt angepasst:
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return this._name; }
set
{
if (value != this._name)
{
this._name = value;
NotifyPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public Person()
{
Name = "Olaf";
}
}
Es funktioniert allerdings leider trotzdem noch nicht.
Beim debuggen fällt auf, dass diese Bedingung "PropertyChanged != null" immer false ist und somit das Event nicht geschmissen wird.
Das diese Bedingung false ist sagt ja aus, dass es keine Abonnenten für das Event gibt, wisst ihr wo mein Fehler liegt?
ich habe folgendes Problem:
Wenn ich eine Eigenschaft eines Objektes, welches sich in einer Liste von Objekten befindet, während einer asynchron laufenden Methode verändere, bekommt der Thread, welcher die asynchrone Methode aufgerufen hat, die Veränderung an dem Objekt nicht mit.
Könnt ihr mir sagen was ich dafür verändern muss?
Hier ein kleines Beispiel:
ViewModel.cs
public ObservableCollection<Person> Persons;
...
public void onAddPerson(object sender, EventArgs e)
{
// Der Name der Person wird im Konstruktor auf Olaf gesetzt. Ob man das so machen würde
// sei dahin gestellt, es geht nur darum meine Situation an einem Beispiel zu
// verdeutlichen
var person = new Person();
Persons.Add(person);
var request = new ChangePersonRequest(person);
// Der asynchrone Aufruf erfolgt über einen Backgroundworker
_asyncService.invokeChangePerson(request, changePersonCallback);
}
private void changePersonCallback(ChangePersonResponse response)
{
// An dieser Stelle ist der Name der Person in der Liste immernoch Olaf.
// Die Person soll aber den Namen haben, der ihr in der asynchronen Methode zugewiesen wurde
persons[0].Name
}
Service.cs
....
public ChangePersonResponse ChangePerson(ChangePersonRequest request)
{
request.CurrentPerson.Name = "Max";
var response = new ChangePersonResponse(request.CurrentPerson);
return response;
}
ChangePersonResponse.cs
....
public class ChangePersonResponse
{
public Person CurrentPerson {get; set;}
public ChangePersonResponse(Person person)
{
CurrentPerson = person
}
}
ChangePersonRequest.cs
public class ChangePersonRequest
{
public Person CurrentPerson {get; set;}
public ChangePersonRequest(Person person)
{
CurrentPerson = person
}
}
Person.cs
public class Person
{
public string Name {get; set;}
public Person()
{
Name = "Olaf";
}
}
Das heisst, das Senden der Daten soll ebenfals in so einem asnychronen Methodenaufruf erfolgen.
Der Service zum senden der Daten würde der asynchronen Methode als request Parameter übergeben werden. Dürfte ich von meiner asynchronen Methode aus, die sich hinter dem asynchronen Proxy befindet, auf eine statische SyncQueue zugreifen, die sich in dem ViewModel befindet das die asynchrone Methode aufruft?
Auch in Bezug auf das MVVM Pattern..
Ich hoffe ihr versteht was ich meine :)
Viele Grüße
*edit -> Oder wie wäre es, wenn ich die SyncQueue in das request objekt einbauen würde? Wäre das "sauberer"?
ich stehe zur Zeit vor folgendem Problem:
Ich habe einen Service, der es mir ermöglicht Daten zu versenden.
Das senden der Daten würde ich gerne asynchron über einen Backgroundworker laufen lassen.
Es müssen von mehreren verschiedenen Stellen aus Daten gesendet werden können.
Nun zu meinen zwei Fragen:
1. Ist die wahrscheinlichkeit groß, dass es dabei zu einer Kollision kommt?
2. Wie könnte ich so eine Kollision verhindern? Am besten wäre wenn ich einem Thread sagen könnte: "Wenn gerade jemand anders Daten sendet, dann warte bis der damit fertig ist und fang dann erst an zu senden"
Schade das mir hierbei wohl keiner Helfen kann ...
Dann schraube ich meine Wünsche ein wenig zurück.
Kann mir denn vielleicht jemand sagen wie ich einfach eine Navigation bauen kann die Submenüs hat? Lassen wir die effekte einfach mal weg, von mir aus kann das auch einfach eine ganz normale Liste sein wie sie hier im Forum auf der linken Seite ja auch zu sehen ist.
Dort drin sind Themes enthalten, aber ich habe nun seit stundenlanger Recherche keine Möglichkeit gefunden, diese in eine ganz simple HelloWorld Website so einzubauen das sie funktionieren. Ich kann sie zwar so einbauen das es beim compilieren keinen Fehler gibt, aber dann wird mir auf der Seite nichts angezeigt.
Kann mir jemand von euch sagen, wie ich dieses Themes korrekt verwenden kann so das sie auch angezeigt werden?
gibt es eine Möglichkeit der ComboBox zu sagen, dass sie ein Item nur dann selektieren soll, wenn es wirklich genau (!) mit der Eingabe in der edtitierbaren ComboBox übereinstimmt?
Denn ich habe z.Z. das Problem, wenn ich zum Beispiel "Micha" eingebe, wird "Michael" zwar richtig ausgewählt, wenn ich dann aber das "a" am Ende löschen und durch ein "s" ersetze, bleibt Michael markiert. Meine Erwartungshaltung wäre allerdings, dass die Markierung direkt verschwindet, wenn die Eingabe nicht mehr genau mit einem der Items übereinstimmt.
Gibt es irgendein Attribut was man bei der CB evtl. setzen kann um das zu ermöglichen? Habe mir schon den MSDN Eintrag zur CB durchgelesen aber keinen Hinweis gefunden.
Klar könnte man sowas auch selber schreiben aber ich denke sowas muss die CB doch auch standardmäßig unterstützn oder ...?
ich habe das Problem, dass ich beim Starten meiner Anwendung den Fokus auf dem ersten TabItem meines tabControls haben möchte.
Jedoch liegt der Fokus zu Begin auf dem Control, dass NACH dem TabControl den geringstens TabIndex hat.
Ich habe das Gefühl, dass der TabIndex bei dem TabControl nicht beachtet wird.
1. Wenn ein Buchstabe geschrieben wird, wird dieser direkt zu Beginn markiert.
Das bedeutet es ist nicht möglich, z.B. "Michael" hintereinander zu schreiben.
Denn das "M" wird direkt markiert, sodass im Endeffekt dann "ichael" in der Textbox steht. Man muss also erst "M" schreiben, dann die Markierung per Hand wegmachen und "ichael" hinterher schreiben.
Gibt es eine Möglichkeit, diese automatische Markierung nach einem Zeichen auszustellen?
2. Wenn ich einen Namen ausgesucht habe, und diesen aus der Textbox lösche bleibt er trotzdem in der Textbox ausgewählt.
Im Debugger ist zu sehen, dass das TextChanged Event 2 mal geschmissen wird. Zuerst mit dem Text "", was meiner Erwartungshaltung entspricht.
Daraufhin wird auch der Filter der Combobox (vermutlich) korrekt gesetzt.
Das Problem ist nun, dass danach direkt noch ein TextChanged Event geschmissen wird, welches der Textbox wieder den Wert gibt, den sie vorher hatte. Ich habe keine Ahnung wer dieses Event schmeißt und warum es plötzlich den Wert der Textbox wieder zurück setzt.
Könnt ihr mir sagen wie ich dieses zweite TextChanged Event unterbinden kann und wo es her kommt bzw. wer es schmeißt?
Ich würde mich freuen, wenn ihr mir helfen würdet.
ich habe mich gerade ein wenig mit den Validierungsmöglichkeiten in WPF auseinandergesetzt.
Nun kann man beispielsweise beim Validieren einer Textbox ein ErrorTemplate definieren, dass das Aussehen der Textbox im Falle eines Fehlers beschreibt.
Zur Zeit wird mein Fenster nach Auftreten eines Fehlers mit einem normalen roten Ramen umrundet.
Ich würde diesen Ramen gerne gegen einen DropShadowEffect austauschen, so wie ich ihn in der Textbox darunter (ohne Fehlerbehandlung) implementiert habe.
Ja, der Expander macht eigentlich schon das richtige, nur das er den Content nach unten ausklappt, und ich möchte gerne das sich der Header praktisch mit nach oben schiebt.
Desweiteren würde ich dir Geschwindigkeit des Hochklappens gerne regulieren.
Das ich die Reaktion auf das Mouseover mit Triggern lösen kann habe ich bereits verwendet.
Mein Problem liegt darin, dass sich der Content Bereich der Groupbox während der ANimation vergrößern soll, ohne das sich der Header und der Text im Header dabei verzerren (d.h. mit vergrößern)
ich versuche gerade meine Kenntnisse zum Thema Animationen in WPF ein wenig zu erweitern.
Nun wollte ich mal versuchen, eine Groupbox ausklappen zu lassen, sobald der Mauszeiger darüber geht.
Ich arbeite mit Expression Blend, habe aber leider keine Möglichkeit gefunden, diese Animation zu entwickeln.
Ich möchte innerhalb meines Timestamps die Groupbox vergrößern.
Wenn ich nun auf 2 Sekunden in der Zeitleiste klicke und danach die Groupbox größer ziehe wird alles mit vergrößert (Schriftart, Header, ...) sodass man praktisch keinen Platz für Inhalt dazu gewinnt, sondern lediglich alles größer wird.
Ich habe auch schon versucht ein Template für eine GroupBox anzulegen und dieses dann entsprechend zu animieren, leider hat das aber auch nicht zum Ziel geführt.
Ich würde mich freuen, wenn mir jemand von euch weiter helfen könnte.
Der Eazfuscator.NET obfuscator sollte an dieser Stelle ebenfalls genannt werden. Eazfuscator.NET
Ich habe nun schon mehrere Obfuscatoren ausprobiert, meiner Ansicht nach sind der Phoenix Protector und der Eazfuscator.NET obfuscator die beiden besten.
Wobei der Eazfuscator.NET obfuscator die Namespaces, Klassen und Methoden stärker verschlüsselt, wohingegen der Phoenix Protector meinen Disassembler öfter zum Abstürzen bringt.
danke für deine Informationen.
Ich habe mir das FAQ und alle verlinkten Themen durchgelesen, konnte dort aber keine Antwort auf meine Frage finden.
Wenn ich das hier richtig verstanden habe:
// Pass a logRounds parameter to GenerateSalt to explicitly specify the
// amount of resources required to check the password. The work factor
// increases exponentially, so each increment is twice as much work. If
// omitted, a default of 10 is used.
string hashed = BCrypt.HashPassword(password, BCrypt.GenerateSalt(12));
// Check the password.
bool matches = BCrypt.CheckPassword(candidate, hashed);
Wird das Passwort hier ebenfalls in einer Variable "password" gespeichert?!
Meine Frage ist nun wo ich den Klartext, den die Passwortvariable enthält am besten bzw. sichersten zuweisen kann?
ich möchte mein E-Mail Passwort im Code so verschlüsseln, das es beim disassemblern nicht ausgelesen werden kann.
Beim verschlüsseln Hilft mir die Klasse ProtectedData mit der Methode Protect.
Beispiel:
byte[] s_aditionalEntropy = { 9, 8, 7, 6, 5 };
System.Text.ASCIIEncoding asc = new System.Text.ASCIIEncoding();
byte[] secure = ProtectedData.Protect(asc.GetBytes("ich bin ein passwort"), s_aditionalEntropy, DataProtectionScope.CurrentUser);
System.Net.NetworkCredential nc = new System.Net.NetworkCredential("blackdynamic", asc.GetString(ProtectedData.Unprotect(secure, s_aditionalEntropy, DataProtectionScope.CurrentUser)));
Meine Frage ist nun, muss ich die Zeile (asc.GetBytes("ich bin ein passwort")) noch irgendwie so verschlüsseln, dass ich "ich bin ein passwort" in den Application Settings ablege o.ä.?
Oder kann das Passwort so schon nicht mehr beim disassemblern ausgelesen werden?
Mittels Profiler kannst du rausbekommen, wo dein Programm viel Zeit verbraucht.
Wie benutze ich so einen Profiler denn bzw. wo bekomme ich einen her?
Zitat von michlG
Messen braucht man da imho nix.
Alle Vorgänge die unter bestimmten Umständen länger als 1 Sek dauern, sollten meiner Meinung ausgelagert werden (sofern es sich nicht um die GUI handelt)
wie soll ich denn ohne Messen herausfinden welcher Vorgang über eine Sekunde dauert?