Laden...

Forenbeiträge von ill_son Ingesamt 227 Beiträge

05.03.2024 - 14:42 Uhr

Hallo,

ich wollte mich nochmal zur Thematik melden und zur Diskusion betragen. Ich hab nochmal ins Buch geschaut und die Stelle gefunden. Ich hab das Buch in der 2019er Ausgabe. Da ich mir nicht sicher war, ob ich das einfach ablichten und ins Forum stellen darf, hab ich es fix nachgezeichnet. Das Bild hab ich dem Post angehängt, im Buch auf Seite 68, Bild 3.11.

Auf Seite 63 steht auch nochmal:

"Because our goal is to inverse the dependency between the domain layer and the data layer, IProductRepository is defined in the domain layer."

Meine Problematik ist, das ich absehbar vor der Aufgabe stehe, in meiner Anwendung die erzeugten Daten abhängig davon, wer mit der Anwendung arbeitet, zu verarbeiten. Beispielsweise Anwender aus Gruppe A legt beim Betätigen des "Speicher-Buttons" Daten nur lokal ab, Anwender aus Gruppe in der Datenbank. Wenn jetzt Implemetierung A und B in unterschiedlichen Bibliotheken oder Projekten sind, ginge das ja nur so wie oben beschrieben. Wo liegt mein Denkfehler?

Grüße, Alex

02.03.2024 - 09:54 Uhr

Hallo Abt,

danke für deine Antwort. Das Buch heißt "Dependency Injection Principles, practices and patterns" von Mark Seemann.

Dein kurzes Beispiel habe ich in dem Buch genau anders verstanden, nämlich so, dass IUserRepository in der Logikschicht definiert sein muss, damit diese nicht von der Datenschicht abhängt. Sonst müsste ich ja immer die Logikschicht anpassen müssen, wenn sich die Datenschicht andert. Die Datenschicht implementiert dann das Interface. Wenn aber die Datenschicht aus einer anderen Bibliothek stammt, müsste ich da ja noch eine Eben einfügen, die das "anpasst", weil ja das konkrete UserRepository aus der Bibliothek nichts vom Interface meiner Logikschicht weiß. Es gibt ja hier im Forum auch einen Thread zum Thema Schichtarchitektur. Da erwähnt jemand, dass man unter Umständen viel Gluecode schreiben muss, wenn man das konsequent durchzieht. Ist das damit gemeint?

Grüße, Alex

01.03.2024 - 14:30 Uhr

Hallo,

ich lese gerade ein Buch über Dependency Injection und ausgehend davon wird auch Dependency-Inversion und Drei-Schicht-Architektur erwähnt. Ich möchte das ganze nun auch in meinem Projekt anwenden und habe eine grundsätzliche Frage.

Im Buch wird erwähnt, dass die Datenzugriffsschicht von der Logikschickt abhängen soll und nicht andersherum bzw. auch, dass jede Schicht die Interfaces, die sie verlangt, selbst definieren soll.

Frage: Wie mache ich dass, wenn die Datenschicht in einer eigenen Bibliothek vorliegt? Aus dem Bauch heraus würde ich sagen, man braucht eine zusätzliche Vermittlungsschicht, die dann von der Logik- und der Datenschicht abhängt und das dann vermittelt, Stichwort Glue-Code. Wie gesagt, das wäre meine erster Gedanke. Wie ist der "offizielle" Weg?

Grüße, Alex

20.12.2023 - 13:46 Uhr

Hallo Abt,

danke für deine Antwort. Ich habe es jetzt so gelöst, dass eine Methode

IChannelCommunication? GetChannelCommunication(string id);		
    
public interface IChannelCommunication
{
    bool IsConnected { get; }
    string Id { get; }
    void Write(byte[] data);
    byte[] ReadPackage();
    void Clear();
}

ein Kommunikations-Objekt zurückgibt, das alles beinhaltet, um mit dem Sensor zu reden, aber die Verbindungsfunktionalitat nicht mit veröffentlicht. Die manage ich dann im "Hub".

Grüße, Alex

14.12.2023 - 10:03 Uhr

Hallo Alf,

danke für deine Antwort. Die Repository hat einen Scanner, der die Sensoren sucht und bei einem detektierten Sensor einen Channel erzeugt. Die Channel-Objekte entstehen also im Scanner. Ich habe jetzt folgendes gemacht, ich habe im Scanner eine Methode

Task<byte[]?> SendWaitResponse(string id, byte[] data, IProgress<int>? progress = null);

implementiert. Dann kann ich anhand der Id den Kanal suchen und die Daten schicken und lesen. Der Scanner hält die Channel-Objekte und kann diese dann auch aufräumen.

13.12.2023 - 13:23 Uhr

Hallo,

ich habe folgende Frage:

ich habe eine Klasse welche die Kommunikationsobjekte zu an den Rechner angeschlossen Sensoren verwaltet. Diese Objekte implementieren IDisposable. Wird ein Sensor angeschlossen oder entfernt, wird ein Event ausgelöst. Nun frage ich mich, wie ich am besten das Aufrufen von Dispose() der Kommunikationsobjekte gestalte.

Wenn jetzt die darüberliegende Klasse eine Objekt von der Repository abfragt:

public interface IRepository
{
    event EventHandler<ChannelEventArgs>? ChannelAdded;
    event EventHandler<ChannelEventArgs>? ChannelRemoved;
    
    ICommunicationChannel? GetChannel(string id);
    
    string[] Channels;
}

public sealed class ChannelEventArgs : EventArgs
{
    public string Id { get; }
    
    public ChannelEventArgs(string id)
    {
        Id = id;	
    }
}

ICommunicationChannel? channel = _Repsitory.GetChannel(id);

wer ist dann für den Aufruf von Dispose() zuständig? Ich würde jetzt sagen das Repository. Sollte der Channel dann ein Disposed-Event haben?

Danke und Grüße,

Alex

25.07.2023 - 13:38 Uhr

Hallo Abt,

danke für Deine Antwort. Mein Fehler bestand darin, die Android-spezifischen Quellcodedateien nicht im Platforms-Ordner für Android unterzubringen, sondern in einem allgemeinen Ordner.

Danke und Grüße,

Alex

24.07.2023 - 13:42 Uhr

Hallo,

ich habe folgende Frage. Ich habe eine kleines Testprojekt, in dem ich mich mit einem WLAN verbinden möchte. Nach einiger Recherche habe ich rausgefunden, dass das über NetworkRequest und WifiNetworkSpecifier realisert wird. Allerdings bekomme ich den gefundenen Beispiel-Code nicht zum laufen. Es scheitert daren, dass der namespace Android.Net.Wifi nicht gefunden wird. Die Intellisense bietet mit diesen zwar mit dem Verweis "net7.0-android33.0 available" an, trotzdem bekomme ich die Fehlermeldung: CS0246 The type or namespace 'Android' could not be found. Wenn ich das richtig verstanden habe, muss eine Mono.Android.dll referenziert werden. Ich haben nicht rausfinden können, wie? Weiß jemand Rat?

Danke und Grüße,

Alex

05.03.2023 - 14:47 Uhr

Das Model weiß auch so nichts über den Filter, kennt nur das Interface. Messdatenerzeugung, Filter usw. habe ich als Interfaces und Contructor Injection injiziert. Meine Frage ist eher, wie verheirate ich das mit dem Viewmodel.

05.03.2023 - 14:37 Uhr

Hallo Weressated,

ich muss gestehen, dass ich deine Antwort nicht ganz verstehe. Vielleicht habe ich zuwenig Informationen über mein Problem geliefefert. Ich versuche mal, es zu konkretisieren.
In meiner Anwendung geht es darum, dass aus einer Hardware ein Satz Messdaten kommt und der dann verarbeitet werden soll. Das Filtern ist nur eine Schritt davon, der innerhalb der Verarbeitung passiert. So werden die Daten gefiltert, dann mit Referenzwerten verglichen, noch andere Parameter berechnet und viele dieser Dinge müssen einstellbar sein. Es gib also mehrere Klassen, die einstellbare Parameter haben und das möchte ich möglichst sauber umsetzten.

05.03.2023 - 14:15 Uhr

Hallo,

ich habe folgende Frage:

In meinem Projek gibt es einige Klassen, die sich weit unten in der Hierarchie befinden, also weit innerhalb des Models. Diese berechnen z.B Filterdaten und müssen über die UI einstellbar sein (Filterfrquenz) . So gibt es z.B. eine Klasse, die die Versuchsdaten berechenet, darin befindet sich dann die Filterklasse usw. Nun Frage ich mich, ob ich die Properties bis nach oben durchreiche und so zugänglich mache oder ob ich besser eine Setup-Dependency injiziere, die ich dann an anderer Stelle im ViewModel ändern kann? Wie wäre da der saubere Weg?

Danke und Grüße, Alex

03.03.2023 - 11:47 Uhr

Hallo Abt,

Danke für die Hilfe und Info.

03.03.2023 - 10:11 Uhr

Hallo T-Virus,

danke für deine Antwort. Deshalb hatte ich versucht, eine "neue" InvalidOperationException zu werfen, statt der JsonException, für die man ja die externe Json-Bibliothek benötigt.. Trotzdem kommt die FileNotFoundException. Text.Json schau ich mir mal an.

03.03.2023 - 09:33 Uhr

Hallo,

ich habe folgende Frage:

in einer Klassenbibliothek, die ich gern in mehreren Projekten verwenden möchte, habe ich Newtonsoft.Json über den Package Manger eingebunden. Nun möchte ich dies nicht auch in jedem Projekt tun müssen, in dem ich die Klassenbiblithek verwende. Wenn jedoch die unten stehende Json-Funktion z.B. eine Exception wirft, bekomme ich im aufrufenden Code eine FileNotFoundException, weil ja die dll fehlt. Ich hatte versucht, das ganze, wie unten im catch-Block stehend, zu umgehen. Das klappt aber nicht (immer noch FileNotFoundException). Wie kann ich das lösen bzw. gibt es dafür einen "offiziellen" Weg (best practise, oder so)?


public T? Deserialize<T>(string text)
{
    if (text is null)
    {
        throw new ArgumentNullException(nameof(text));
    }

    try
    {
        return JsonConvert.DeserializeObject<T>(text);
    }
    catch (JsonException exc)
    {
        string message = exc.Message;
        throw new InvalidOperationException(message);
    }
}

Danke und Grüße, Alex

29.03.2022 - 17:24 Uhr

Hallo gfoidl,

danke für deine Anwort. In die Richtung hatte ich auch überlegt. Das Problem ist, dass die Schritte teilweise voneineander anhängen. In einem Schritt wird das File eingelesen und, wenn es geht, in ein Intel Hex File Format geparst. Im nächsten daraus die MemoryMap erstellt, die geflasht werden soll usw. Dann habe ich auch noch einen optionalen Schritt. Wenn z.B. das Gerät nicht gleich gefunden wurde, muss eine Auforderung an den Benutzer kommen, einen Reset durchzuführen. Wenn es direkt gefunden wurde (weil der Flash noch leer ist und der Controller auf den Flashvorgang wartet), dann geht es direkt los, zwingend ohne Reset. Das ist irgendwie nicht so richtig straight forward.

29.03.2022 - 15:37 Uhr

Hallo,

folgende Frage: Ich arbeite aktuell an einer Software zum Firmwareupdate eine Gerätes. Nun besteht die ganze Prozedur aus mehreren Schritten (Datei einlesen, in Hex-File umwandeln, Com-Port erstellen, Gerät suchen, gegenbenfalls zum Reset auffordern, Flash programmieren, rücklesen usw.) und zwischendurch soll auch noch mittels Progress über den Ablauf informiert werden. Abhängig davon, ob der eine Schritt erfolreich war, wird der nächste durchgeführt oder abgebrochen. Nun ist das eine Schachtelei von if-Bedinungen. Ich wollte fragen, ob es dafür ein schlaues Pattern gibt, oder ob sich das nicht anders lösen lässt?

Grüße, Alex

17.03.2022 - 11:50 Uhr

Möglichkeit zwei wäre eine Lösung mit externem Mikrocontroller. (z.B. Arduino Nano) Natürlich viel größerer Aufwand, aber man hat die volle Kontrolle. Der Controller puffert die Daten und der PC holt sie irgendwann ab.

Das ist leider keine Option. Der Sensor kommt fertig mit Konverter und wird in großer Zahl im Feld eingesetzt.

17.03.2022 - 11:48 Uhr

Der verlinke Multimedia Timer ist leider für NET Core, ich hab hier noch .NET Framework.

Der Quellcode ist mit sehr sehr großer Wahrscheinlichkeit 1:1 .NET Framework kompatibel, da er fast identisch aussieht wie
>
.

Danke. Den Link meinte ich, als ich schrieb, ich hätte was passendes gefunden.

17.03.2022 - 08:42 Uhr

Hallo Wilfried,

der Sensor redet binär über RS485, das müsste dann RTU sein. Mir war es wichtig, eine genauere Zeitbasis zum Samplen zu haben. Ich hab 100ms Samplezeit, da waren mir die oben erwähnten 15ms Unschärfe zu viel.

16.03.2022 - 13:09 Uhr

Ja, aller 100ms. Der verlinke Multimedia Timer ist leider für NET Core, ich hab hier noch .NET Framework. Habe bei Code Project aber was passendes gefunden. Danke für den Tipp.

15.03.2022 - 12:14 Uhr

Hallo ill_son,

deine Schleife

  
while (sw.ElapsedMilliseconds < milliSeconds) { }  
  

ergibt doch schon 100% Last auf einem Prozessorkern. Du mußt mindestens ein Sleep(0) bzw. Delay(0) einbauen, damit der Task (Thread) entlastet wird (s.a.
>
).

In die Richtung zielte meine Frage. Die leere Schleife hat mir auch nicht gefallen. Die Links sind sehr aufschlussreich.

Warum nicht direkt Sleep(milliSeconds) bzw. Delay(milliSeconds)
Ist auch nicht exakt, aber weniger Code und arbeitet nicht mit solchen Krücken.

Das hatte ich probiert, war mir aber zu ungenau. Mit meiner Krücke ging es erst mal. Deshalb hier die Frage, ob das "sauber" ist.

10.03.2022 - 11:34 Uhr

Danke für deine Rat.

10.03.2022 - 11:27 Uhr

Das hatte ich so nicht auf dem Schirm. Ich dachte nur: geht, prima. Sind die 15,6 ms irgendwie sicher, oder kann das auch schlechter werden, wenn der Rechner zu tun hat?

10.03.2022 - 11:20 Uhr

Ich dachte bis jetzt, dass die Auflösung bzw. Genauigkeit der StopWatch Klasse besser sei als die oben genannten 15ms.

10.03.2022 - 11:04 Uhr

Hallo Abt,

danke für deine Antwort. Wie gesagt, mit der oben gezeigten Lösung funktioniert es. Meine Frage ist eher, ob man das so machen kann. Stichwort: best practice, anti pattern usw. Vor allem mit Blick auf die leere while-Schleife.

10.03.2022 - 10:06 Uhr

Hallo,

ich habe folgende Frage. Ich arbeite mit einem Drucksensor, der über Modbus arbeitet und der keinen eigenen Takt hat, sondern ständig gepollt werden muss. Sowohl für die Kommunikation als auch für den Abfragetakt hätte ich gern eine präzise Delay-Funktion, wobei ich festgestellt hab, dass Task.Delay nicht reicht. Wenn ich das richtig recherchiert habe, sind da etwa 15ms Unschärfe drin.

Ich habe folgendes versucht, was auch funktioniert nach meinem Dafürhalten. Ich möchte gern wissen, ob man das so machen kann oder ob das besser geht und wenn ja, wie.


public Task WaitPrecisely(int milliSeconds)
{
    return Task.Run(() =>
    {
        Stopwatch sw = new();
        sw.Start();
        while (sw.ElapsedMilliseconds < milliSeconds) { }
        sw.Stop();
     });
}

Danke und Grüße, Alex

04.02.2022 - 18:08 Uhr

Die Klasse, die den Request kapselt und in der sich die CancellationTokenSource befindet, implementiert IDisposable und disposed dann auch die cts. Die wird auch nur einmal an einer Stelle erstellt und nicht mehrmals.

04.02.2022 - 16:16 Uhr

Ich habe noch etwas festgestellt. Wenn ich die CancellationTokenSource ohne Timeout erstelle und CancelAfter nur im EventHandler aufrufe funtioniert das zurücksetzten. Wenn ich den parameterbehafteten Konstruktor verwende oder CancelAfter direkt nach dem Erstellen der cts aufrufe, funktioniert CancelAfter im EventHandlers nicht.

04.02.2022 - 16:02 Uhr

Der EventHandler wird vom SerialPort im DataReceived-Event aufgerufen. Häufigkeit habe ich mir mal mit einer StopWatch mal ausgemessen. Aller paar 100 ms. Das schwankt natürlich etwas.

04.02.2022 - 15:46 Uhr

Schau mal in meinen Anfangspost. Da wird im EventHandler eine Funktion ResetTimeout aufgerufen. Die greift auf die CancellationTokenSource zu.

04.02.2022 - 15:27 Uhr

Mein obiges Minimalbeispiel funktioniert ja auch, weshalb auch ich die Vermutung habe, dass es damit zu tun hat, dass das CancelAfter im SerialPort Event aufgerufen wird. Aber da steck ich nicht tief genug in der Materie. Ich mach nichts weiter also oben beschrieben: Daten im EventHandler lesen, prüfen und CancelAfter aufrufen. Wenn der Frame fertig ist, TaskCompleationSource setzen. Wenn die Datenmenge groß ist, kommt das Event mehrfach. Wenn ich das Timeout nicht verwende (CancellationTokenSource - Konstruktor ohne Argument), dann läuft die Übertragung durch.

04.02.2022 - 15:07 Uhr

Und da sind wir wieder beim meinem Problem. Trotz aufrufen dieser Methode im DataReceived event wird mein Task abgebrochen.

Aus der Doku

Subsequent calls to CancelAfter will reset the delay for this CancellationTokenSource, if it has not been canceled already.

Und genau das klappt bei mir nicht.

04.02.2022 - 14:56 Uhr

Das kann ich mal probieren. Mein eigentlichen Problem war ja das Timeout. Wenn was unvorhergesehenes passiert (Gerät hört auf zu senden), dann soll der Vorgang nach einer Weile abgebrochen werden. Ich weiß auch vorher nicht, wie lange das insgesamt dauert, weil ich die Datenmenge nicht kenne. Deshalb brauche ich ein Timeout, dass ich immer, wenn noch Daten kommen, wieder zurücksetzten kann.

04.02.2022 - 14:43 Uhr

Aber warum liest Du nicht einfach synchron?

Meint synchron, im UI-Thread? Weil die Übertragung vom dem Gerät auch mal etwas länger dauern kann, wenn große Datenmengen anfallen.

04.02.2022 - 14:18 Uhr

Hallo gfoidl,

Das wäre ein Anwendungsfalls für System.IO.Pipelines in .NET.

Danke, das schaue ich mir auf jeden Fall an. Löst das mein Resetproblem? Ich hätte vielleicht noch erwähnen sollen, dass ich mit Binärdaten arbeite.

Die oben beschriebene Klasse ist pro Request. Nur kann eben die Antwort auf mehrere Events verteilt sein und ich setzte dann die CancellationTokenSource zurück, weil ich ja weiter warten muss. Wenn sie ausgelöst hat, dann ist der Request abgebrochen und beendet.

folgendes Minimalbeispiel hat bei mir funktioniert, weshalb ich denke, dass es was mit dem Event zutun hat (Context oder so?):

Edit: CancellationTokenSource.TryReset kann ich hier gar nicht finden (.NET Framework 4.8)


private CancellationTokenSource cts;

public IAsyncCommand StartCommand { get; } = new AsyncCommand(Start);
public ICommand ResetCommand { get; } = new RelayCommand(Reset);

private async Task Start()
{
    cts?.Dispose();
    cts = new CancellationTokenSource(5000);
    Task.Delay(1000000000, cts); //warte echt lange
}

private void Reset(object dummy)
{
    try
    {        
        cts?.CancelAfter(5000);
    }
    catch { }
}

Wenn ich einmal den Start- und anschließend regelmäßig den Reset-Button gedrückt habe, hat die cts nicht ausgelöst. Und nichts anderes passiert in meiner Request-Klasse, nur dass da der Reset aus dem SerialPort-Thread kommt.

04.02.2022 - 13:23 Uhr

Hallo,

ich habe folgendes Problem: Ich habe eine Klasse zur seriellen Komunikation (SerialPort) mit einem Gerät. Diese schickt eine Anfrage und wartet auf einen kompletten Frame. Der Frame muss aber nicht auf einmal als Paket kommen, sondern kann auch mal länger sein. Nach jedem DataReceivedEvent packe ich die empfangenen Daten in eine globale Liste, schaue ob Anfangs- und Endekennung da sind und gebe den Rahmen zurück, wenn nicht resete ich den Timeout (CancelAfter) und warte weiter auf das Ende des Frames.

Hinter dem Requestable_Object im EventHandler verbirgt sich ein SerialPort. Mein Problem ist nun, dass die CancellationTokenSource auslöst, obwohl ich immer im EventHandler
CancelAfter aufrufe. Das scheint keinen Effekt zu haben. Ich habe mir mal ein Minimalbeispiel mit Task.Delay und Buttons zum Rücksetzten und Viewmodel geschrieben, da ging es.

Woran liegt das?

Danke und Grüße, Alex


private readonly TaskCompletionSource<byte[]> _TaskCompletionSource;
private CancellationTokenSource _CancellationTokenSource;

private void SetTimeout()
{
      _CancellationTokenSource = new CancellationTokenSource(_Timeout);
      _CancellationTokenSource.Token.Register(() => CancelRequest());
}

private bool CancelRequest()
{
      return _TaskCompletionSource.TrySetResult(Array.Empty<byte>());
}

private void ResetTimeout()
{
      try
      {
          _CancellationTokenSource.CancelAfter(_Timeout);
      }
      catch (ObjectDisposedException) { }
}

public async Task ProcessRequest()
{
      SendRequest();
      SetTimeout();
      Data = await GetResponseAsync();
}

private void RequestableObject_DataAvailable(object sender, RequestDataEventArgs e)
{
     ResetTimeout();
     AddData(e.Data);
    
     byte[] data = _ReceivedData.ToArray();

     if (_ProtocolDecoder.IsFullFrame(data) && _ProtocolDecoder.TryDecode(data, out byte[] outData) == FrameCheckResult.Frame)
     {
           _ = _TaskCompletionSource.TrySetResult(outData);
     }
}

18.01.2022 - 13:34 Uhr

Ich habe einen Button mit eigenem Template und ich möchte abhängig vom Hintergrund, vor dem sich der Button befindet, die MouseOverColor anpassen können, damit immer ausreichend Kontrast vorhanden ist.

17.01.2022 - 14:59 Uhr

Ich hatte mal gelesen, das Binding im der Animation nicht funktioniert:
wpf-animation-binding-to-the-to-attribute-of-storyboard-animation

Ich hatte das Propblem schon mal, deswegen hatte ich es weiter oben auch schon erwähnt.

17.01.2022 - 13:35 Uhr

Das ist sicher ein Tippfehler, im Code stimmt es aber.

folgendes habe ich versucht:


<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=(resources:Extentions.MouseOverColor)}" Duration="0:0:0"/>

Im Output erscheint: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Button', AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target element is 'ColorAnimation' (HashCode=47260332); target property is 'To' (type 'Nullable`1')

15.01.2022 - 17:04 Uhr

Hallo Th69,

danke für deine Antwort. Ich glaube aber, dass löst mein Problem, bzw. beantwortet die Frage nicht. Ich möchte nicht die AttatechProperty bei Verwendung eines Buttons an einen Wert binden, sondern in einem Style in der Animation dem Zielwert den Wert der AttatchedProperty zuweisen.


To="local:Extentions.MouseOverColor"

Ich frage mich nur gerade, ob das überhaupt geht, weil für in der Animation ja Binding nicht funktioniert und dann auch eine (Attatched) DependencyProperty wenig Sinn mach.

Wie gesagt, ich weiß nicht, ob ich hier völlig auf dem Holzweg bin.

15.01.2022 - 13:50 Uhr

Hallo,

ich habe folgende Frage. Ich möchte die Hintergrundfarbe eines Buttons bei MouseOver animieren und dabei gern die Zielfarbe variabel gestalten. Ich hatte den Gedanken, dass über eine AttatchedProperty zu lösen. Dazu habe mit einen Style definiert


<Color x:Key="ButtonMouseOverColor" A="#FF" R="#52" G="#52" B="#5E"/>
<Style TargetType="{x:Type Button}">
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border x:Name="Border" TextBlock.Foreground="{TemplateBinding Foreground}" CornerRadius="4" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="{StaticResource ButtonMouseOverColor}" Duration="0:0:0"/>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed"/>
                                <VisualState x:Name="Disabled"/>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

und folgende Attatched Property, die ich bei Verwendung des Buttons setzten kann und über die dann die Background-Color der Border im Style gesetzt werden soll.


using System.Windows;
using System.Windows.Media;

namespace Sportboden.Resources
{
    public class Extensions : DependencyObject
    {
        public static readonly DependencyProperty MouseOverColorProperty = DependencyProperty.RegisterAttached("MouseOverColor", typeof(Color), typeof(Extensions), new PropertyMetadata(Color.FromArgb(0xFF, 0x46, 0x82, 0xB4)));

        public static void SetMouseOverColor(DependencyObject target, Color value)
        {
            target.SetValue(MouseOverColorProperty, value);
        }

        public static Color GetMouseOverColor(DependencyObject target)
        {
            return (Color)target.GetValue(MouseOverColorProperty);
        }
    }
}


Nun würde ich gern in der Animation unter "To" den Wert setzten, der in der AttatchedProperty steht, bring das aber nicht zusammen. Vielleicht habe ich aber auch einen Denkfehler und das geht so gar nicht. Wie kann ich das lösen?

Danke und Grüße,

Alex

14.06.2021 - 13:42 Uhr

Hallo,

danke für eure Antworten.

@JimShark: So habe ich es im Augenblick auch gemacht. Ich habe es noch ein bisschen umgeschrieben, um den Progress auch nur dort zu benutzen, wo ich ihn brauche.


private async Task<IEnumerable<NewSensor>> ScanPortsForSensorsAsync(IEnumerable<string> portNames)
{
    // scan goes here
}

private async void ComPortWatcher_PortsAdded(object sender, SerialCommEventArgs e)
{
    IEnumerable<NewSensor> newSensors = await ScanPortsForSensorsAsync(e.PortNames);
    foreach (NewSensor sensor in newSensors)
    {
        NewSensorDetected?.Invoke(this, new NewSensorEventArgs(sensor));
    }
}

Der SensorScanner aboniert das PortsAdded-Event des ComPortWatchers und löst daraufhin einen Scan aus. Die Frage war nur, ob das so einfach geht. Ich bin noch einbisschen neu in der Thematik async Task und alles was ich immer gelesen habe, war: Task mit Progress verwenden. Neulich hatte ich ein interessantes Tutorial, wo erklärt wurde, dass async void wie fire-and-forget zu betrachten ist. Momentan läuft es auch noch nicht zuverlässig.

14.06.2021 - 12:46 Uhr

Hallo,

folgendes Problem bekomme ich gerade nicht gelöst:

Ich habe eine Klasse, welche mir über VCP an den Rechner angeschlossene Sensoren sucht. Diese hat folgende Methode


public async Task ScanAsync(IProgress<NewSensor> progressIndicator)
{
    if (!IsBusy)
    {
        IEnumerable<string> portNames = _ComPortWatcher.GetCurrentPortNames();
        await ScanPortsForSensors(portNames, progressIndicator);
    }
}

Diese starte ich aus meinem VM mit einem AsyncCommand -> alles gut.

Nun soll aber der Scan auch ausgelöst werden, wenn neue Com-Ports auftauchen. Dafür gibt es eine Klasse, die auf entsprechende RegistryEvents reagiert und einen Scan anstößt.


private async void ComPortWatcher_PortsAdded(object sender, SerialCommEventArgs e)
{
    await ScanPortsForSensors(e.PortNames, null); 
}

Nun meine Frage: Wie bekomme ich die detektierten Sensoren gemeldet? Wenn ich es richtig verstanden habe kehrt ja der await-Aufruf sofort zurück (fire and forget), da ComPortWatcher_PortsAdded async void ist und nicht Task zurück gibt, was bei Eventhandlern ja legitim zu sein scheint, wie ich gelesen habe. Kann ich da im Anschluss ein Event auslösen?

Grüße, Alex

04.01.2021 - 18:24 Uhr

Hallo Abt,

Beispielsweise verrechne ich in der Bewertung zwei Messsignale miteinander. Wenn diese sich aber gar nicht zeitlich überlappen, weil der Benutzer die falschen Daten ausgewählt hat, dann kann ich kein valides Ergebnis erzeugen. Ansonsten kann es sein, dass das Ergebnis berechnet, aber der Test nicht bestanden wurde. Dann wäre das Ergebnis valide, aber negativ. Ich bin da für Vorschlage sehr offen, wenn du das anders/besser lösen würdest.

Edit: @Palladin: Meinst du eine Art TryEvaluate-Methode mit out-Parameter, wie das beim numerischen Parsen von strings verwendet wird?

Grüße, Alex

04.01.2021 - 10:22 Uhr

Hallo Th69,

danke für deine Antwort. Das mit dem Kontruktor war etwa unglücklich ausgedrückt. Die Implementierung von Enumerable.Empty hatte ich mir angeschaut.

Ich habe verschiedene Bewertungsmethoden, die verschiedene Ergebnisse zurückgeben. Deshalb ist das mit der einen invaliden Klasse bisschen schwierig. Ich hab es jetzt erst mal so gelöst, dass ich für jede Result-Klasse eine statische Methode in einer statischen Klasse habe, die mir das gewünschte Objekt liefert. st nicht optimal, aber für meinen Fall passt das so.

04.01.2021 - 07:54 Uhr

Hallo,

ich habe eine abstracte Klasse wie folgt:


public abstract class EvaluationResult
{
    public bool IsEvaluationValid { get; }
        
    public bool TestPassed { get; }

    public EvaluationResult(bool testPassed, bool isEvaluationValid)
    {
        TestPassed = testPassed;
        IsEvaluationValid = isEvaluationValid;
    }
}

Von dieser leite ich andere Result-Klassen ab, die neben den beiden Properies noch andere haben, in denen irgendwelche Erbebniswerte stehen.

Nun möchte ich für den Fall, dass keine Bewertung durchgeführt werden konnte, weil beispielsweise die Eingabeparameter der Bewertungsfunktion nicht sinnvoll sind, eine abgeleitete Klasse mit


IsEvaluationValid = false 

zurück geben und dazu eine ensprechende statische Methode


DerivedResult.Invalid()

haben, vergleichbar mit Enumerable.Empty<T>(), nur das T nur eine Subklasse von EvaluationREsult sein darf. Wie stelle ich das an?

Grüße, Alex

11.09.2020 - 15:00 Uhr

@T-Virus
Hmh, mir ist eigentlich gar nicht klar was der TE überhaupt damit will. Meistens ja um diese zu entfernen. Daher meine Antwort.

Ich möchte die Einträge erstmal markieren, um sie in der Anzeige hervorzuheben.

@all: Danke für die Hilfe.

Grüße, Alex

11.09.2020 - 11:52 Uhr

Hallo T-Virus,

Danke für deine Antwort. Ich muss gestehen, dass das Propblem meine aktuellen Linq-Kenntnisse etwas übersteigt. Wie müsste denn dann der Parameter für GroupBy aussehen?

11.09.2020 - 11:32 Uhr

Hallo,

ich habe eine IEnumerable mit Objekten:


public class MyObject
{
    public int Prop1 { get; } 
    public string Prop2 { get; }
    public int Prop3 { get; }
}

...
//schnipp schnapp
...

IEnumerable<MyObject> MyEnumerable;

Jetzt möchte ich alle Duplikate finden, wobei Einträge als Duplikat gelten, wenn Prop1 und Prop2 gleich sind, Prop3 ist für den Vergleich uninteressant.

Nun liefert mir


var dublicates = list.GroupBy(e => new { e.Prop1, e.Prop2 }).Where(g => g.Count() > 1).Select(g => g.Key);

ein IEnumerabel mit dem anonymen Typen {Prop1, Prop2}, ich benötige aber eine Auflistung mit den ganzen Objekten. Wie kann ich das anstellen?

Grüße, Alex