Laden...

Forenbeiträge von BlonderHans Ingesamt 41 Beiträge

05.06.2023 - 06:55 Uhr

Eine win32.dll kenne ich von Windows nicht. Meinst du eventuell die Windows-API (formerly known as Win32-API)?

Dann sollte das eigentlich deine Frage beantworten:

Mithilfe der Windows-API können Sie Anwendungen entwickeln, die auf allen Windows-Versionen erfolgreich ausgeführt werden, und gleichzeitig die für jede Version einzigartigen Features und Funktionen nutzen. (Beachten Sie, dass dies früher als Win32-API bezeichnet wurde. Der Name Windows-API spiegelt seine Wurzeln in 16-Bit-Windows und seine Unterstützung unter 64-Bit-Windows genauer wider.)

Quelle: Windows-API-Index

Generell ist es eine gute Idee, sich beim Hersteller zu erkundigen.

02.06.2023 - 10:24 Uhr

Zitat von Kriz

-> Würde das hier gern schöner formatieren, aber es wird leider nicht richtig übernommen...

Du hast für deinen Code(-Block) die Code Formatierung verwendet und nicht den direkt daneben liegenden Button für Code-Block einfügen.

Mit Code-Block einfügen sieht das dann so aus:

switch (overlay.Type)
{
    case OverlayType.Ellipse:
        ShowEllipse = true;
        ShowRectangle = false;
        ShowPath = false;
        break;
    case OverlayType.Rectangle:
        ShowEllipse = false;
        ShowRectangle = true;
        ShowPath = false;
        break;
    case OverlayType.Path:
        ShowEllipse = false;
        ShowRectangle = false;
        ShowPath = true;
        break;
    default:
        break;
}

Die Code Formatierung verwendest du, wenn du im Text etwas als Code formatieren möchtest, wie z.B. den Typen string oder deinen overlay.Type.

02.06.2023 - 10:09 Uhr

WÄre es nicht einfacher so?

public bool ShowEllipse => overlay.Type == OverlayType.Ellipse;
public bool ShowRectangle => overlay.Type == OverlayType.Rectangle;
public bool ShowPath => overlay.Type == OverlayType.Path;

und immer wenn sich in overlay die Eigensschaft Type ändert, dann folgenden Code noch ausführen

OnPropertyChanged(nameof(ShowEllipse));
OnPropertyChanged(nameof(ShowERectangle));
OnPropertyChanged(nameof(ShowPath));

Alternativ gäbe es auch noch diesen EnumToBoolConverter aus dem CommunityToolkit

29.05.2023 - 19:58 Uhr

Zitat von RobertStahl

Perfekt, danke, aber das @GWu1 wird anscheinend benötigt für die Zuweisung im Update-Befehl

Ja, ähm ... genau ... ich habe doch auch nicht gesagt "entferne das @ Zeichen" sondern du musst bei Parameters.Add den Parameter-Namen so verwenden wie du ihn im Statement geschrieben hast. Und zwar genau so.

Denn

Parameters can be prefixed with either :, @, or $.

aber für einen Präfix musst du dich zwingend entscheiden und der Parameter-Name ist immer mit Präfix anzugeben.

29.05.2023 - 10:05 Uhr

Wenn du es allerdings ganz bequem haben möchtest, dann kannst du auch Dapper verwenden. Dann würde das so aussehen:

class GrenzwerteDto
{
    public int ProductReference { get; set; }
    public decimal GWu1 { get; set; }
}

connection.Open();
var data = new GrenzwerteDto { ProductReference = 42, GWu1 = 12.45, };
connection.Execute( "UPDATE Grenzwerte SET GWu1 = @GWu1 WHERE REF_Produkt = @ProductReference", data );

oder mit noch weniger Boilerplate

connection.Open();
connection.Execute( "UPDATE Grenzwerte SET GWu1 = @GWu1 WHERE REF_Produkt = @ProductReference", 
    new { ProductReference = 42, GWu1 = 12.45, } );

Kann man sich überlegen

29.05.2023 - 09:45 Uhr

Da sind drei Dinge falsch.

1. Anführungszeichen

command.CommandText = $"UPDATE Grenzwerte SET GWu1 = '@GWu1'  WHERE REF_Produkt = 'Int aus einer Textbox'

Die Parameter kümmrn sich von ganz alleine darum, dass die Werte korrekt übergeben werden. Jede weitere Bevormundung durch Anführungszeichen o.ä. empfinden diese als übergriffig.

So ist es korrekt

command.CommandText = $"UPDATE Grenzwerte SET GWu1 = @GWu1  WHERE REF_Produkt = 'Int aus einer Textbox'

2. Parameters.Add verwenden

Statt

command.Parameters.AddWithValue("GWu1", DbType.Decimal).Value =  NudGw_Gwu1.Value;

so

command.Parameters.Add("GWu1", DbType.Decimal).Value =  NudGw_Gwu1.Value;

3. Parameter-Name

Der Name des Parameters lautet exakt so wie er in dem Command-Text angegeben wurde - wirklich exakt und kein "so irgendwie in der Art evetuell"

Wenn dort der Parameter also als @GWu1 angegeben wurde, dann ist genau das auch der Name.

Also statt

command.Parameters.Add("GWu1", DbType.Decimal).Value =  NudGw_Gwu1.Value;

so

command.Parameters.Add("@GWu1", DbType.Decimal).Value =  NudGw_Gwu1.Value;
28.05.2023 - 18:44 Uhr

Es ist etwas konfus was du da schreibst ... du könntest ruhig etwas konkreter werden, dann könnte man auch konkreter Hilfestellung geben.

Worauf du dich mit getters/setters beziehst erschließt sich mir nicht (im Code von Palladin sehe ich nur Getter).

Hast du dir das Spezification Pattern mal angesehen? Das ist eigentlich für so etwas der richtige Ansatz.

27.05.2023 - 03:09 Uhr

Hallo, du hast den klassischen Anfängerfehler (nicht falsch verstehen, den macht gefühlt jeder Programmierer auf seiner Reise - ich auf jeden Fall auch) gemacht.

Wenn du ein SQL-Statement per string zusammenbaust, dann ist das ein grober Fehler - Grüße von Little Bobby Tables.

Sicherer und einfacher funktioniert das mit Parametern denn die Parameter sorgen dafür, dass die Werte beim SQL-Server auch korrekt ankommen.

24.05.2023 - 17:22 Uhr

Ich sprach eigentlich von diesem VERSIONINFO und nicht von System.Version, denn dort passt es problemlos, weil dort für Build (und auch die anderen Teile) ein Int32 verwendet wird.

24.05.2023 - 09:02 Uhr

Wenn der Build bei 100.000 anfangen soll wird dann auch die Struktur in VERSIONINFO geändert?

Dort wird die Version ja als 64bit Wert angelegt (4x 16bit = 0..65535).

Meine Glaskugel ist in dem Bereich aber ganz wolkig, somit lassen wir uns überraschen.

22.05.2023 - 20:06 Uhr

Da wir nicht wissen, wie die Versionsnummern in der Zukunft weitergezählt werden, sollte man aber auch zwingend Major und Minor daraufhin prüfen, ob diese auch wirklich 10 bzw. 0 sind. Sonst ist die Verwirrung groß wenn (warum auch immer) Windows 12 die Version 10.1.12345.0 hat.

21.05.2023 - 11:21 Uhr

Kannst du mal mit DISKPART die Attribute des Laufwerks prüfen?

DISKPART
SELCT DISK 6
ATTRIBUTES DISK

Dann sollte er so etwas anzeigen

Aktueller schreibgeschützter Zustand: Nein
Schreibgeschützt  : Nein
Startdatenträger  : Nein
Auslagerungsdatei-Datenträger  : Nein
Ruhezustandsdatei-Datenträger  : Nein
Absturzabbild-Datenträger  : Nein
Clusterdatenträger : Nein

Eventuell ist bei Windows 10 dort der Schreibschutz drin.

18.05.2023 - 10:32 Uhr

Probier mal diese kleine Änderung am Code:

public void AppendText(string text)
{
    _text += text;
    this.StateHasChanged();
}
14.05.2023 - 13:14 Uhr

Das riecht ein wenig nach Specification Pattern

06.05.2023 - 09:27 Uhr

Arbeitest du in deinem aktuellen Projekt mit DI?

Dazu würde ich auf jeden Fall raten egal ob MVVM oder nicht. Ich bevorzuge z.B. nuget:Wpf.Extensions.Hosting.

Wenn du dann irgendwann mit DI deine kompletten Abhängigkeiten auflöst hast, dann ist der nächste Schritt zu MVVM auch nicht mehr weit.

05.05.2023 - 16:58 Uhr

Main ruft MethodAAsync auf und Main ist blockiert. MethodeAAsync führt seine Befehle aus bis zum ersten await in der for-Schleife. Jetzt wird Main entblockiert und arbeitet die for-Schleife ab

Ja, kann man so sagen

MethodeAAsync wacht nach 100 ms auf, blockiert Main

Nein, definitiv nicht ... du siehst doch an deinem Log, dass beide auf verschiedenen Threads laufeen

und führt ein Console-Befehl aus

hier könnte es zu einer Blockierung kommen, wenn der Console-Befehl nur den Aufruf von einem Thread zulässt. Blockiert wird allerdings auch nur dann, wenn gleichzeitig von unterschiedlichen Thread dieser ausgeführt werden soll. Das ist wie ein Raum in dem nur eine Person Platz hat. Man muss wrten bis der Raum vom Vorgänger verlassen wurde und dann kann man rein. Wenn ich aber nicht rein muss, werde ich auch nicht blockiert.

und legt sich wieder schlafen.
...
Ist das so richtig zusammengefasst?

Nein, noch nicht ganz.

PS: Ich muss gerade an den Witz mit dem Frosch-Forscher denken. Der wo einem Frosch alle Gliedmaßen abschneidet und dann dem Frosch mit lauter Stimme befiehlt zu hüpfen. Als dieser nicht hüpft, schreibt er auf: "Frosch ohne Beine ist taub."

05.05.2023 - 15:01 Uhr

Also zu dieser Zeile

Task<int> taskA = MethodAAsync();

sagst du

Main startet MethodAAsync mit Task<int> taskA = MethodAAsync(); stellt es in den Hintergrund, kehrt direkt zurück und führt die for-Schleife.

Das stimmt so nicht, denn der Rücksprung erfolgt immer erst beim ersten await wenn es auch wirklich etwas zum awaiten gibt. Bei einem await Task.Delay(0) erfolgt kein Rücksprung und bei jedem anderen erledigtem Task.

Das kann man auch sehen, wenn man etwas mehr Logging einbaut:

    static async Task<int> MethodAAsync()
    {
        ConsoleWriteLine("A - ENTER");
        for (int i = 0; i < 5; i++)
        {
            ConsoleWriteLine($" A{i}");
            await Task.Delay(100);
        }

        int result = 123;
        ConsoleWriteLine($"A returns result {result}");
        return result;
    }

    static async Task Main(string[] args)
    {
        ConsoleWriteLine($"Main - ENTER");
        Task<int> taskA = MethodAAsync();
        ConsoleWriteLine("Main - before loop");
        for (int i = 0; i < 5; i++)
        {
            ConsoleWriteLine($" B{i}");
            Task.Delay(50).Wait();
        }

        ConsoleWriteLine("Wait for taskA termination");
        await taskA;
        ConsoleWriteLine($"The result of taskA is {taskA.Result}");
        Console.ReadKey();
        return;
    }

Die Ausgabe sieht dann wie folgt aus

Main - ENTER                 Thread 1 MAIN
A - ENTER                    Thread 1 MethodA
 A0                          Thread 1 MethodA - jetzt kommt der await Task.Delay(100);
Main - before loop           Thread 1 MAIN
 B0                          Thread 1 MAIN
 B1                          Thread 1 MAIN
 A1                          Thread 5 MethodA - ist jetzt in einem anderen Thread-Kontet unterwegs
 B2                          Thread 1
 B3                          Thread 1
 A2                          Thread 7
 B4                          Thread 1
Wait for taskA termination   Thread 1
 A3                          Thread 7
 A4                          Thread 5
A returns result 123         Thread 7
The result of taskA is 123   Thread 7
27.04.2023 - 22:10 Uhr

Es ist doch für diese Frage völlig unerheblich, was du warum auch immer noch zusätzlich benötigst - für deine hier gestellte Frage spielt der EventAggregator keine Rolle.

Wenn du eine Frage zum Prism-Eventaggregator-ViewModel-Constructor hast, dann kannst du ja eine neue Frage stellen.

24.04.2023 - 01:01 Uhr

@Palladin007

Genau, die View darf so viel Code haben wie sie möchte, solange sich dieser Code nur um die Darstellung kümmert.

Das mit der Komplexität (ein NuGet-Package und ein Command) sehe ich nicht so, denn in meinen MVVM-Projekten verwende ich immer ReactiveUI und warum sollte ich das für so eine Lösung nicht verwenden? Es sieht also nur scheinbar komplex aus aber im Gesamtbild macht es keinen Unterschied.

Ich hätte dieses mit einer AttachedProperty gelöst, um die Bindung sehr elegant rein in XAML zu definieren. Dadurch kann ich den zugrunde liegenden Code auch noch wiederverwenden - wenn man den in ein eigenens NuGet-Package verfrachtet sogar für mehrere Projekte.

So sähe das also bei mir aus:

<DockPanel>
    <Label Content="Password:"
           DockPanel.Dock="Top" />
    <PasswordBox utils:PasswordBoxHelper.BindableSecurePassword="{Binding Password1}"
                 utils:PasswordBoxHelper.IsActive="True">
        <PasswordBox.Style>
            <Style TargetType="PasswordBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding PasswordIsValid}"
                                 Value="True">
                        <Setter Property="Background"
                                Value="LightGreen" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </PasswordBox.Style>
    </PasswordBox>
</DockPanel>
<DockPanel>
    <Label Content="Passsword (retype):"
           DockPanel.Dock="Top" />
    <PasswordBox utils:PasswordBoxHelper.BindableSecurePassword="{Binding Password2}"
                 utils:PasswordBoxHelper.IsActive="True">
        <PasswordBox.Style>
            <Style TargetType="PasswordBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding PasswordsMatch}"
                                 Value="True">
                        <Setter Property="Background"
                                Value="LightGreen" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </PasswordBox.Style>
    </PasswordBox>
</DockPanel>

und das ViewModel

public class RegisterWindowViewModel : ReactiveObject
{
    private readonly ObservableAsPropertyHelper<bool> _usernameIsValid;
    private readonly ObservableAsPropertyHelper<bool> _passwordIsValid;
    private readonly ObservableAsPropertyHelper<bool> _passwordsMatch;

    public RegisterWindowViewModel()
    {
        _usernameIsValid = this.WhenAnyValue(
            e => e.Username,
            ( u ) => !string.IsNullOrEmpty( u ) && u.Length >= 1 ) // darf auch gerne komplexer sein
            .ToProperty( this, e => e.UsernameIsValid );

        _passwordIsValid = this.WhenAnyValue( 
            e => e.Password1, 
            ( p ) => p is not null && p.Length >= 1 ) // darf auch gerne komplexer sein
            .ToProperty( this, e => e.PasswordIsValid );

        _passwordsMatch = this.WhenAnyValue(
            e => e.PasswordIsValid,
            e => e.Password1,
            e => e.Password2,
            ( p1valid, p1, p2 ) => p1valid && p1 is not null && p2 is not null && p1.Length == p2.Length && p1.IsEqualTo( p2 ) )
            .ToProperty( this, e => e.PasswordsMatch );

        var canRegister = this.WhenAnyValue(
            e => e.UsernameIsValid,
            e => e.PasswordsMatch,
            ( u, p ) => u && p );
        Register = ReactiveCommand.CreateFromTask( OnRegisterAsync, canRegister );
    }

    private Task OnRegisterAsync( CancellationToken cancellationToken )
    {
        /// 
        /// Hier die Registrierung durchführen
        ///
        Username = string.Empty;
        Password1 = null;
        Password2 = null;
        return Task.CompletedTask;
    }

    [Reactive] public string Username { get; set; } = string.Empty;
    public bool UsernameIsValid => _usernameIsValid.Value;
    [Reactive] public SecureString? Password1 { get; set; }
    public bool PasswordIsValid => _passwordIsValid.Value;
    [Reactive] public SecureString? Password2 { get; set; }
    public bool PasswordsMatch => _passwordsMatch.Value;
    public ReactiveCommand<Unit, Unit> Register { get; }
}

und noch der PasswordBoxHelper

public static class PasswordBoxHelper
{
    public static SecureString GetBindableSecurePassword( PasswordBox obj )
    {
        return (SecureString)obj.GetValue( BindableSecurePasswordProperty );
    }

    public static void SetBindableSecurePassword( PasswordBox obj, SecureString value )
    {
        var old = obj.GetValue( BindableSecurePasswordProperty ) as SecureString;
        obj.SetValue( BindableSecurePasswordProperty, value );
        old?.Dispose();
    }

    public static readonly DependencyProperty BindableSecurePasswordProperty =
        DependencyProperty.RegisterAttached( "BindableSecurePassword", typeof( SecureString ), typeof( PasswordBoxHelper ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBindableSecurePasswordChanged ) );

    private static void OnBindableSecurePasswordChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        var pb = (PasswordBox)d;
        if ( e.NewValue is null )
            pb.Clear();
    }

    public static bool GetIsActive( PasswordBox obj )
    {
        return (bool)obj.GetValue( IsActiveProperty );
    }

    public static void SetIsActive( PasswordBox obj, bool value )
    {
        obj.SetValue( IsActiveProperty, value );
    }

    public static readonly DependencyProperty IsActiveProperty =
        DependencyProperty.RegisterAttached( "IsActive", typeof( bool ), typeof( PasswordBoxHelper ), new FrameworkPropertyMetadata( false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsActiveChanged ) );

    private static void OnIsActiveChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        var pb = (PasswordBox)d;

        if ( (bool)e.OldValue )
        {
            pb.PasswordChanged -= PasswordChanged;
        }

        if ( (bool)e.NewValue )
        {
            pb.PasswordChanged += PasswordChanged;
        }
    }

    private static void PasswordChanged( object sender, RoutedEventArgs e )
    {
        var password = (PasswordBox)sender;
        SetBindableSecurePassword( password, password.SecurePassword.Copy() );
    }
}
19.04.2023 - 09:01 Uhr

Keine Ursache.

Nun ja, eigentlich ist es nicht wirklich anders ... der Teil, der bei dir nicht funktioniert (laden und anzeigen der Datei), den habe ich eigentich so gut wie unverändert übernommen.

Wenn also mein Projekt funktioniert und deins nicht, dann liegt es nicht an dem Code den du uns zeigst.

Lad doch mal dein nicht funktionierendes Projekt (so wie meins reduziert auf die RTF Anzeige) hier hoch dann können wir uns ansehen, was der wirkliche Störenfried da ist.

18.04.2023 - 09:51 Uhr

Eigentlich müsste das auch funktionieren.

Im Anhang mal eine Beispiel-Anwendung die genau das macht (wenigstens auf meinem System).

Probier es aus und wenn das auch bei dir funktioniert, dann vergleiche es mit deiner Anwendung.

17.04.2023 - 21:59 Uhr

Das geht mit Linq so

using System.Linq;

...

var Obstkorb = new [] { "Apfel", "Orange", "Moehre", "Banane", "Birne" };
var Obstliste = new [] { "Orange", "Moehre", "Banane", "Erdbeeren", "Ananas", "Traube", "Zitrone", "Pfirsich" };

var Rest = Obstkorb.Except( Obstliste ).ToArray();

ausführbares Beispiel auf dotnetfiddle

16.04.2023 - 13:59 Uhr

Ich weiß ja nicht was du so vorhast, aber ich hätte das so gelöst (was auch funktioniert) und macht genau das, was du bisher so beschreiben hast. Man tippt einen beliebigen Wert und beim Drücken der Return-Taste wird ein Command aufgerufen und der eingegebene Wert steht im ViewModel zur Verfügung.

<xctk:DecimalUpDown Value="{Binding ValueTwo, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <xctk:DecimalUpDown.InputBindings>
        <KeyBinding Key="Return"
                    Command="{Binding DoSomething}"
                    CommandParameter="{Binding ValueTwo}" />
    </xctk:DecimalUpDown.InputBindings>
</xctk:DecimalUpDown>
16.04.2023 - 11:40 Uhr

@TreaxP

Komplett rund ist die Lösung aber nicht, denn mit dem StringFormat wird kein Wert dargestellt, das Feld bleibt leer. Taugt also nur für ein reines Eingabefeld.

Wenn das reicht, dann ist es eine ausreichende Lösung. Bei mehr würde ich ein passendes Control empfehlen.

16.04.2023 - 09:47 Uhr

@Th69
Meinn System ist auch auf deutsch und Thread.CurrentThread.CurrentCulturesowie Thread.CurrentThread.CurrentCulturestehen auf de-DEund trotzdem muss ich in einer TextBox als Dezimal-Trenner ein .verwenden.

Um das zu ändern muss man beim Start der Anwendung folgendes aufrufen:

FrameworkElement.LanguageProperty.OverrideMetadata(
    typeof( FrameworkElement ),
    new FrameworkPropertyMetadata( XmlLanguage.GetLanguage( CultureInfo.CurrentCulture.IetfLanguageTag ) ) );

Nun klappt es auch mit dem ,.

16.04.2023 - 09:18 Uhr

Zitat von TreaxP

Wenn du mir noch eine letzte Frage erlaubst:

Ich bin in keinerlei Position hier etwas zu verbieten geschweige denn etwas zu erlauben 😃

Zu deiner eigentlichen Frage:

Wenn du dafür eine vernünftige Lösung haben willst, dann solltest du auf ein spezielles Control zurückgreifen, was explizit für die Eingabe eines double oder decimal Werts gedacht ist.

Da wäre z.B. DecimalUpDown vom Extended.Wpf.Toolkit

15.04.2023 - 17:58 Uhr

So ein Control hat einen DataContext.

Wird dieser nicht explizit gesetzt, dann wird dieser vom Parent durchgereicht.

Du hast diesen ja explizit auf Option1 gesetzt und somit wird dieses

<KeyBinding Key="Return" Command="{Binding CalculateOptionCommand}" CommandParameter="{Binding RowNumber, Mode=OneWay}"/>

gegen diesen DataContext versucht aufzulösen.

Aber genau das willst du ja nicht, sondern du willst das ja mit den Eigenschaften vom Control binden. Das habe ich dir schon gezeigt, hier aber noch einmal reduziert auf die wesentliche Teile:

<UserControl ...
             x:Name="Control"
             ...>
    ...
    <KeyBinding Key="Return"
                Command="{Binding ElementName=Control, Path=CalculateOptionCommand}"
                CommandParameter="{Binding ElementName=Control, Path=RowNumber}" />
    ...
</UserControl>

Allerdings sehe ich nicht, wo du das Binding für RowNumber machst.

Hier auf jeden Fall nicht:

<uc:OptionsRow Grid.Row="0" 
               Visibility="{Binding Option1.OptionsRowIsVisible, Converter={StaticResource booleanToVisibilityConverter}}" 
               DataContext="{Binding Option1}"
               CalculateOptionCommand="{Binding RelativeSource={RelativeSource AncestorType=vm:MainViewModel}, Path=CalculateOptionCommand}"/>

Wenn du weiter Hilfe benötigst, dann solltest du ein reduziertes aber vollständiges Projekt hochladen, dann kann ich da einmal drüberschauen, entsprechende Änderungen vornehmen und dir zurückschicken.

15.04.2023 - 16:44 Uhr

Obwohl ich es persönlich wohl eher so lösen würde, dass das UserControl eine DependencyPropertyfürOption, CalculateCommand und CalculateCommandParameter bereitstellt.

Im Control würde das dann wie folgt gemappt:

<UserControl x:Class="UserControlBinding.WpfApp.OptionControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:UserControlBinding.WpfApp"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             x:Name="Control"
             d:DesignHeight="450"
             d:DesignWidth="800"
             mc:Ignorable="d">
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding ElementName=Control, Path=Option.RowNumber, Mode=OneWay}" />
            <TextBlock Text="{Binding ElementName=Control, Path=Option.BS, Mode=OneWay}" />
            <TextBlock Text="{Binding ElementName=Control, Path=Option.PC, Mode=OneWay}" />
            <TextBlock Text="{Binding ElementName=Control, Path=Option.Pr, Mode=OneWay}" />
            <TextBox Text="{Binding ElementName=Control, Path=Option.KUl, Mode=TwoWay}">
                <TextBox.InputBindings>
                    <KeyBinding Key="Return"
                                Command="{Binding ElementName=Control, Path=CalculateCommand}"
                                CommandParameter="{Binding ElementName=Control, Path=CalculateCommandParameter}" />
                </TextBox.InputBindings>
            </TextBox>
        </StackPanel>
    </Grid>
</UserControl>

und würde so verwendet

<uc:OptionsRow CalculateCommand="{Binding CalculateOptionCommand}"
               CalculateCommandParameter="{Binding Option1.RowNumber}"
               Option="{Binding Option1}"
               Visibility="{Binding Option1.OptionsRowIsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
15.04.2023 - 14:09 Uhr

Du hast CalcualteOptionCommandvom DataContext bezogen und für die Eigenschaften von der Option Klasse hast du jeweils eine DependencyProperty erstellt.

Ich dachte das wäre klar ... denn nun bekommst du die ganzen Option Eigenschaften über den DataContext und folglich den CalculateOptionCommand ... z.B. über eine DependencyProperty. Es ist die gleiche Vorgehensweise aber nur mit vertauschten Rollen.

Denkbar wäre natürlich auch eine Erweiterung der Option Klasse um genau so eine CalculateOptionCommand Eigenschaft.

14.04.2023 - 12:09 Uhr

Hmmm, dann solltest du nochmal über die Konfiguration deiner IDE nachdenken, denn solche Dinge werden bei mir gleich vorgeschlagen und automatisch eingetragen.

Ansonsten sollte immer gehen: Cursor auf das problematische Element im Editor (also hier Cursors) und dann STRG+.da werden Sie geholfen

14.04.2023 - 08:56 Uhr

An Windows Desktop 7 liegt es schon mal nicht (siehe Cursors-Klasse).

Wohl eher an dem fehlenden using System.Windows.Input;

Das funktioniert bei mir ohne Probleme:

using System.Windows;
using System.Windows.Input; // Die muss da sein

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Cursor = Cursors.Arrow;
        }
    }
}
13.04.2023 - 15:07 Uhr

Also du hast dort eine Instanz, wo du die Events abonnierst:

DynamicMenuItems myObject = new DynamicMenuItems(); //Event deklarieren

myObject.MyEvent += myObject_MyEvent; //Event abonnieren

und dann erzeugst du eine andere Instanz um dort das anzulegen, was dann irgendwie dieses Event auslösen soll

var dmi = new DynamicMenuItems();
dmi.Menu = subtestToolStripMenuItem;

string[] items = { "SubMenu 1", "SubMenu 2", "SubMenu 3" };
dmi.AddMenuItems(items); //Menüeinträge aus einer List erzeugen

Das ist ungefähr so, wenn du dich benachrichtigen lässt, wenn bei Paul an der Wohnungstür jemand klingelt - du bei Sabine an der Wohnungstür auf die Klingel drückst und dich wunderst, warum du keine Benachrichtigung erhälst.

13.04.2023 - 10:43 Uhr

Es wäre sehr hilfreich wenn du deinen Code mit den entsprechenden Code-Tags posten würdest, das erhöht die Chance, dass sich überhaupt jemand die Mühe macht, deinen Code anzusehen.

Also statt so

namespace Hier
{
public class Da
{
public void Dort()
{
// ganz wichtig!
}
}
}

hätten wir es gerne so

namespace Hier
{
    public class Da
    {
        public void Dort() 
        {
             // ganz wichtig! 
        }
    }
}

PS: Und statt myObject_MyEvent(Object objSender, EventArgs e) hätten wir gerne myObject_MyEvent(Object objSender, EventArgs e)

10.04.2023 - 18:28 Uhr

Auch so ein UserControl hat ein DataContext wo dann das normale Binding funktioniert.

<uc:OptionsRow Grid.Row="0" DataContext="{Binding Option1}"/>

und im UserControl dann

<TextBox Style="{StaticResource styleRowTextBox}" Grid.Column="3" Foreground="Black" Text="{Binding KUl, UpdateSourceTrigger=LostFocus}">
	<TextBox.InputBindings>
    	<KeyBinding Key="Return" Command="{Binding CalculateOptionCommand}"
        	CommandParameter="{Binding RowNumber, Mode=OneWay}"/>
    </TextBox.InputBindings>
</TextBox>

Der Zugriff auf CalculateOptionCommand geht so dann natürlich nicht mehr (wegen dem geänderten DataContext).

07.04.2023 - 17:29 Uhr

Wenn du deiner DynamicMenuItems Klasse die Instanz zu Form1 übergibst, dann kannst du dir diese dort merke und dann auch auf die Methode zugreifen.

namespace Eugen.ESystem
{
    public class DynamicMenuItems
    {
        private readonly Form1 _form;
        
        public DynamicMenuItems( Form1 form )
        {
            _form = form;
            //Mache hier irgendwann irgendetwas
        }

        /// <summary>
        /// List of menu entries to be created
        /// </summary>
        public string[] MenuItems { get; set; }

        /// <summary>
        /// Menu into which the submenus are inserted
        /// </summary>
        public ToolStripMenuItem Menu { get; set; }

        /// <summary>
        /// Insert a menu item
        /// </summary>
        /// <param name="menuItem">Name of the Menu item</param>
        public void AddMenuItem(string menuItem)
        {
            ToolStripMenuItem menu = (ToolStripMenuItem)(Menu);
            Menu.DropDownItems.Add(menuItem, null, new EventHandler(ToolStripMenuItem_Click));
        }

        /// <summary>
        /// Insert menu entry from a list
        /// </summary>
        /// <param name="menuList">List of menu items</param>
        public void AddMenuItems(string[] menuList)
        {
            //Für jedes Element in der Liste einen Menüpunkt anlegen
            foreach (var element in menuList)
            {
                AddMenuItem(element);
            }
        }

        /// <summary>
        /// Check a menu item
        /// </summary>
        /// <param name="selectedItem">Item name</param>
        public void CheckMenuItem(string selectedItem)
        {
            //Hier soll der Menüeintrag, der dem Übergebenen Wert entspricht, auf 'Checked = true' gesetzt werden
            foreach (ToolStripMenuItem item in Menu.DropDownItems)
            {
                if (item.Text == selectedItem)
                {
                    item.Checked = true;
                }
                else
                {
                    item.Checked = false;
                }
            }
        }

        private void ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem MenuItem = (ToolStripMenuItem)sender;

            CheckMenuItem(MenuItem.Text);

            MessageBox.Show("Menu " + sender + " angeklickt");
            DoSomething();

            _form.DoSomethingInForm1(); //Hier liegt das Problem !!!
        }

        public void DoSomething()
        {
            OnMyEvent(EventArgs.Empty);

            MessageBox.Show("Jetzt sind wir hier");
            // ...
        }

        public event EventHandler MyEvent;

        protected virtual void OnMyEvent(EventArgs e)
        {
            EventHandler myEvent = MyEvent;
            if (myEvent != null)
            {
                myEvent(this, e);
            }
        }
    }
}

hier der Code von Form1:

namespace Test_DynamicMenuItems
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //...
        }

        public static string Value { set; get; }

        private void buttonAddmenuItems_Click(object sender, EventArgs e)
        {
            var dmi = new DynamicMenuItems( this );
            dmi.Menu = subtestToolStripMenuItem;

            string[] items = { "SubMenu 1", "SubMenu 2", "SubMenu 3" };
            dmi.AddMenuItems(items); //Menüeinträge aus einer List erzeugen

            DynamicMenuItems myObject = new DynamicMenuItems( this );
            myObject.MyEvent += myObject_MyEvent;

            //myObject.DoSomething();
        }

        public static void myObject_MyEvent(Object objSender, EventArgs e)
        {
            MessageBox.Show("myObject_MyEvent (" + objSender + ", " + e + ")");
        }

        public static void DoSomethingInForm1()
        {
            // jetzt kommen wir hier auch hin
            MessageBox.Show("Juhu, es hat funktioniert");
            //Und da passieren noch ganz viele andere Dinge
        }
    }
}

So richtig gesund ist das irgendwie nicht - allerdings sehe ich noch nicht so richtig, was für ein Problem du damit eigentlich lösen möchtest.

PS: Deinen Code solltest du mit Code-Tags versehen, dann ist der auch so leserlich wie meiner hier.

04.04.2023 - 20:08 Uhr

Erklär doch bitte mal warum du diese wiederholende Zuweisung benötigst.

Auch der Setter der Eigenschaft ist eher suboptimal. So kann es passieren, das trotz Änderung des Eigenschaftswertes kein PropertyChangedausgelöst wird.

Als weiteren Kritikpunkt hätte ich das mit der Verwendung des UI-Typen Visibility im ViewModel. Der hat da nichts zu suchen. Wenn es nur zwei Zustände gibt, dann nimm einen bool Wert dafür und in der View setzt du diesen boolWert in das um, was dort am besten ist. Im ViewModel sollte man keinen Gedanken daran verschwenden, wie man etwas darstellt, nur was man darstellen möchte.

Zum Setter gäbe es noch mehr zu sagen, aber das führt zu weit und erledigt sich evtl. wenn du uns erklärst, was du da erreichen möchtest.

03.04.2023 - 09:13 Uhr

Und in einigen Beiträgen sieht der C# Code seltsam aus, obwohl selbiger als Markdown betrachtet korrekt erscheint

Beispiel

Und in meinem Beitrag fehlt dem C#-Code die Farbe

Siehe

31.03.2023 - 21:39 Uhr

Hier ein weiterer von den >100 möglichen Wegen:

Mit der DataGridTemplateColumn kann man das wie folgt umsetzen:


<Window
  x:Class="WpfApp1.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:col="clr-namespace:System.Collections;assembly=System.Runtime"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="clr-namespace:WpfApp1"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  Title="MainWindow"
  Width="800"
  Height="450"
  mc:Ignorable="d">

  <Window.Resources>
    <col:ArrayList x:Key="projects">
      <local:Projekt ID="1" ProQuali="0" />
      <local:Projekt ID="2" ProQuali="1" />
      <local:Projekt ID="3" ProQuali="2" />
    </col:ArrayList>

  </Window.Resources>
  <Grid>
    <DataGrid
      AutoGenerateColumns="False"
      CanUserAddRows="False"
      ItemsSource="{StaticResource projects}">
      <DataGrid.Columns>
        <DataGridTextColumn
          Binding="{Binding ID}"
          Header="Id"
          IsReadOnly="True" />
        <DataGridTextColumn
          Binding="{Binding ProQuali}"
          Header="Qualität"
          IsReadOnly="False" />

        <!--#region Template Column-->

        <DataGridTemplateColumn Header="Qualität">
          <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
              <Ellipse Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}" Margin="2">
                <Ellipse.Style>
                  <Style TargetType="Ellipse">
                    <Setter Property="Fill" Value="Red" />
                    <Style.Triggers>
                      <DataTrigger Binding="{Binding ProQuali}" Value="1">
                        <Setter Property="Fill" Value="Yellow" />
                      </DataTrigger>
                      <DataTrigger Binding="{Binding ProQuali}" Value="2">
                        <Setter Property="Fill" Value="Green" />
                      </DataTrigger>
                    </Style.Triggers>
                  </Style>
                </Ellipse.Style>
              </Ellipse>
            </DataTemplate>
          </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <!--#endregion-->

      </DataGrid.Columns>
    </DataGrid>
  </Grid>
</Window>


using System.Windows;

namespace WpfApp1;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

public class Projekt
{
    public int ID { get; set; }
    public int ProQuali { get; set; }
}

28.03.2023 - 09:32 Uhr

Wäre es da nicht besser, dem Kinde gleich die richtigen Namen zu verpassen?

Du hast es doch mit dem Schema quasi schon fertig:


class Section
{
    public string Header { get; set; }
    public Dictionary<string,string> Values { get; set; }
}

class Packet
{
    public string Description { get; set; }
    public List<Section> Sections { get; set; }
}

28.03.2023 - 09:19 Uhr

Eigentlich ist es ganz einfach:

Jeder Typ muss explizit im DI-Container registriert werden, damit dieser auch aufgelöst werden kann.

Wenn du also den Typ IWorkerTicketProvider<MyWorker> irgendwo injecten möchtest, dann musst du IWorkerTicketProvider<MyWorker> auch explizit registrieren, so wie du das jetzt eben gemacht hast.

17.03.2023 - 13:42 Uhr

Ich möchte hier auch mal mit mischen.

Schauen wir uns doch mal 2 mögliche Angriffsvektoren an und wie die PasswordBox bzw. der darin gekapselte SecureString da schützen.*Keylogger
NEIN, kein Schutz, denn die Information wird abgegriffen bevor diese den SecureString überhaupt erreicht

*Speicher auslesen
JEIN, nur bedingt, denn irgendwann muss man die Hosen runterlassen und das Password muss in Klartext (als string oder char[]) an die entsprechende Authentifizierungs-Logik übergeben werden und dann ist das Kennwort wieder abgreifbar (je nach Umsetzung für eine kurze oder sehr lange Zeitspanne)

Wenn man sich also auf den SecureString verlässt, dann hat man lediglich die Script-Kiddies ausgesperrt, die mal eben so im Speicher nach Passwörtern Ausschau halten und dann aufgeben, wenn sie da nichts finden. Für alle anderen - die auf wirklichen Schaden aus sind - stellt das keine ernst zu nehmende Hürde dar und ist damit gleichbedeutend wie gar kein Schutz.

Die Lösung dafür liegt eher im Anmelde-Konzept, wie z.B.*Anmeldung mit 2FA *Passwortlose Anmeldung (wie z.B. die Anmeldung bei Office 365 mit der MS Authenticator APP)

Dort spielt es keine Rolle, ob das Kennwort erbeutet wird, bzw. es gibt einfach keins zu erbeuten.