Forum: PC-Programmierung C# Events anlegen?!


von Jan H. (janiiix3)


Lesenswert?

Hällochen (:

ich würde gerne via. Event etwas an meine GUI weitergeben.
Undzwar habe ich eine Klasse ("cmd") die sich um das Parsen von 
eingehenden Daten kümmert.
Sobald es ein neues Daten Paket gibt, möchte ich das ein Event quasie 
meine Daten in meine verschiedenen Textboxen rein schreibt.
Hoffe ich habe mich richtig ausgedrückt..?!

Hier ist ein Ausschnitt meiner Funktion, wenn diese Aufgerufen wird, 
soll ein Event losgehen, damit ich weiß das es neue Daten gibt und diese 
halt geschrieben werden können..

Kann mir da jemand weiter helfen? Im Internet gibt es zwar zig. 
verschiedene Lösungen aber da stelle ich mich doch ein wenig doof an 
glaube ich.
1
        public CommandoRawData  ProcessCommando     (Commando_Struct input )    
2
        {
3
            if ( input.id == (byte)cmd.Id_Codes_Enum.ID_VOLTAGE )
4
            {
5
                WSQ3000.Form1.wsq.Voltage = System.BitConverter.ToUInt16( input.data , 0 );
6
            }
7
            if ( input.id == (byte)Id_Codes_Enum.ID_CURRENT )
8
            {
9
                WSQ3000.Form1.wsq.Current = System.BitConverter.ToSingle( input.data , 0 );
10
            }
11
12
            return CalculatedValues;
13
        }

von Cyblord -. (Gast)


Lesenswert?

Jan H. schrieb:
> Kann mir da jemand weiter helfen? Im Internet gibt es zwar zig.
> verschiedene Lösungen aber da stelle ich mich doch ein wenig doof an
> glaube ich.

Nein, es gibt nicht zig Lösungen wie man ein Event anlegt und feuert, 
das ist ziemlich klar definiert, aber es gibt ein paar Fallstricke zu 
beachten je nachdem ob man sich in einer multithreaded Umgebung befindet 
oder nicht.

von Jan H. (janiiix3)


Lesenswert?

Okay... Und wie würde dieser "eine" Weg aussehen?

von Cyblord -. (Gast)


Lesenswert?

Jan H. schrieb:
> Okay... Und wie würde dieser "eine" Weg aussehen?

Ich verlinke mal in die Doku:

https://msdn.microsoft.com/de-de/library/5z57dxz2(v=vs.110).aspx

von Frank (Gast)


Lesenswert?

So aus dem Gedächtnis:
1
public class Cmd
2
{
3
  public EventHandler<DataReceivedEventArgs> DataReceived;
4
5
  protected virtual void OnDataReceived(DataReceivedEventArgs e)
6
  {
7
    this.DataReceived()?.Invoke(e);
8
  }
9
10
  public CommandoRawData ProcessCommando(Commando_Struct input)
11
  {
12
    ...
13
    this.OnDataReceived(new DataReceivedEventArgs(CalculatedValues));
14
    ...
15
  }
16
}
17
18
19
public class Form1
20
{
21
  public Form1()
22
  {
23
    var cmd = new Cmd();
24
    cmd.DataReceived += this.DataReceivedEventHandler;
25
  }
26
27
  private void DataReceivedEventHandler(object sender, DataReceivedEventArgs e)
28
  {
29
    this.wsq.Voltage = e.Voltage;
30
  }
31
}
32
33
34
public class DataReceivedEventArgs : EventArgs
35
{
36
  public DataReceivedEventArgs(CommandoRawData data)
37
  {
38
    this.Data = data;
39
  }
40
41
  public CommandoRawData Data { get; }
42
}

Du deklarierst in deiner Cmd-Klasse einen EventHandler (z.B. 
DataReceived) vom Typ EventHandler<DataReceivedEventArgs>. 
DataReceivedEventArgs ist dabei eine Hilfsklasse, die die Daten kapselt, 
die du über das Event weitergeben willst. Falls du keine Daten 
weitergeben willst, kannst du auch einfach EventHandler<EventArgs> 
schreiben.

Üblicherweise gibt es dann eine Methode "On<EventName>" die für das 
eigentliche Auslösen des Events zuständig ist. Diese wird dann einfach 
zu dem Zeitpunkt aufgerufen, wenn das Event ausgelöst werden soll.
Wichtig ist, dass diese Methode das Event nur auslöst (Aufruf von 
Invoke()), wenn der EventHandler ungleich null ist (ansonsten gibt es 
eine Exception). Ab C# 7 (?) kann das einfach über den .? Operator 
sichergestellt werden. Damit ist übrigens auch die 
Multithreading-Problematik abgehandelt, die Abradolf L. meinte.

ACHTUNG: Der Code in der verlinkten Doku (in OnCountdownCompleted) ist 
NICHT für Multithreading-Umgebungen geeignet. Zwischen der Prüfung auf 
null und der Invocation könnte Code, der in einem anderen Thread 
ausgeführt wird, sich vom Event abhängen. Wenn das dann der 
letzte/einzige EventHandler war, ist CountdownCompleted plötzlich doch 
null und es knallt.

In deiner Form registrierts du dann nur noch eine Methode am Event, die 
ausgeführt werden soll, wenn das Event auftritt. Dieser werden deine 
DataReceivedEventArgs übergeben, über die du auf deine Daten Zugriff 
hast.

von Frank (Gast)


Lesenswert?

Und
1
private void DataReceivedEventHandler(object sender, DataReceivedEventArgs e)
2
{
3
  this.wsq.Voltage = e.Voltage;
4
}

muss natürlich
1
private void DataReceivedEventHandler(object sender, DataReceivedEventArgs e)
2
{
3
  this.wsq.Voltage = e.Data.Voltage;
4
}

heißen.

von Frank (Gast)


Lesenswert?

Und zweiter Nachtrag: Mit dem .? Operator ist selbstverständlich der ?. 
Operator gemeint.

In der Doku zu diesem Operator wird das mit dem thread safe Invoke bei 
Events übrigens auch noch erwähnt:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

von Cyblord -. (Gast)


Lesenswert?

Frank schrieb:
> Ab C# 7 (?) kann das einfach über den .? Operator
> sichergestellt werden. Damit ist übrigens auch die
> Multithreading-Problematik abgehandelt, die Abradolf L. meinte.

Das ist nicht ganz korrekt. Man kann damit die NullReferenceException in 
multithreaded Umgebungen mitigieren, das ist richtig, aber letztendlich 
ist das nur eine Kurzform von:
1
var handler = CountdownCompleted
2
if (handler!= null)
3
    handler(this, e);

Ob das in Gänze thread-safe ist hängt von den restlichen Umständen ab, 
da käme mir zum Beispiel eine ObjectDisposedException in den Sinn.

von Frank (Gast)


Lesenswert?

Abradolf L. schrieb:
> Frank schrieb:
>> Ab C# 7 (?) kann das einfach über den .? Operator
>> sichergestellt werden. Damit ist übrigens auch die
>> Multithreading-Problematik abgehandelt, die Abradolf L. meinte.
>
> Das ist nicht ganz korrekt. Man kann damit die NullReferenceException in
> multithreaded Umgebungen mitigieren, das ist richtig, aber letztendlich
> ist das nur eine Kurzform von:
> var handler = CountdownCompleted
> if (handler!= null)
>     handler(this, e);

Korrekt.

Abradolf L. schrieb:
> Ob das in Gänze thread-safe ist hängt von den restlichen Umständen ab,
> da käme mir zum Beispiel eine ObjectDisposedException in den Sinn.

Klar das löst natürlich nicht grundsätzlich alle Probleme, die durch 
Nebenläufigkeit entstehen können. Aber zumindest das reine Auslösen des 
Events sollte damit mal safe sein.

Was auch noch wichtig ist: Wenn das Objekt, das den EventHandler 
registriert, nicht mehr gebraucht wird, die Cmd-Instanz aber weiter im 
Speicher gehalten wird, sollte der EventHandler natürlich abgehängt 
werden. Andernfalls hält die Cmd-Instanz über den registrierten 
EventHandler eine Referenz auf dieses Objekt (im Beispiel die Form) im 
Speicher. Sprich: der Speicher wird nicht freigegeben (=> MemoryLeak) 
und der EventHandler wird auch weiterhin ausgeführt.

von nicht"Gast" (Gast)


Lesenswert?

Frank schrieb:
> So aus dem Gedächtnis:
>
>
1
> 
2
>     this.DataReceived()?.Invoke(e);
3
> 
4
>


kleine Korrektur. Es muss natürlich
1
    DataReceived()?.Invoke(this,e);


heißen.

Grüße

von Dirk (Gast)


Lesenswert?

Hallo, wenn deine GUI mittels WPF erstellt wurde, dann schau Dir das 
MVVM Pattern an.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.