Laden...

Forenbeiträge von BlonderHans Ingesamt 144 Beiträge

05.07.2024 - 15:38 Uhr

Du kopierst also alle Dateien von beiden Anwendungen in einen Ordner?

Genau das würde ich nicht machen - unter anderem aus dem Grund warum du gerade fragst.

04.07.2024 - 18:52 Uhr

Wenn du eine kleine Linux-Kiste (z.B. virtuell) aufstellst kannst du postfix dafür verwenden.

Habe ich auch genau aus so einem Grund im Einsatz.

28.06.2024 - 18:37 Uhr

In Visual Studio ist die Erweiterung ResXManager sehr hilfreich.

20.06.2024 - 15:09 Uhr

Zitat von Abt

Das erste was man braucht, ist eigentlich die WMI. Über GetProccess bekommt man immer nur die gleichen Prozesse der Architektur, also x86 oder x64. Über WMI bekommt man alles.

Das war die Einleitung zum Beitrag.

Um direkt in dem Code die Ergebnisse von Process.GetProcesses() (wir erinnern uns: liefert zu wenig) und der WMI (wir erinnern uns: liefert alles) mit einem join zu verknüpfen und damit das was die WMI mehr liefert gleich wieder in den Ausguss zu spülen.

Das macht einfach keinen Sinn weil man so nicht mehr bekommt.

Auf diese Problematik wollte ich den unbedarften Copy-Paste-Nutzer der Zukunft hinweisen, nicht das der dann da mit seinem kurzen Hemd im Regen herumsteht und die Welt nicht mehr begreift, denn das hilft schon mal gar nicht.

20.06.2024 - 13:00 Uhr

@jogibear9988

Es kommt eben nicht nur darauf an, was man ausführt, sondern auch wer

using System.Diagnostics;
using System.Management;

Console.WriteLine( "Processes: " + Process.GetProcesses().Count() );
Console.WriteLine( "WMI Processes: " + GetWmiProcesses().Count );
Console.WriteLine( "RunningProcesses:" + GetRunningProcessesFromWmi().Count );

static List<ManagementObject> GetWmiProcesses()
{
    string wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
    using ManagementObjectSearcher searcher = new( wmiQueryString );
    using ManagementObjectCollection results = searcher.Get();

    return results.Cast<ManagementObject>().ToList();
}

static List<(Process Process, string Path)> GetRunningProcessesFromWmi()
{
    var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
    using ManagementObjectSearcher searcher = new( wmiQueryString );
    using ManagementObjectCollection results = searcher.Get();
    var query =
                from p in Process.GetProcesses() // Hier ist das große Mysterium, denn aus diesem weniger wird dann wieder mehr
                join mo in results.Cast<ManagementObject>()
            on p.Id equals (int)(uint)mo["ProcessId"]
                select (p, (string)mo["ExecutablePath"]);

    return query.ToList();
}

Mir ist es auch ein Rätsel, wenn Process.GetProcesses() 287 Items liefert und Abfrage der WMI 312 und dann ein JOIN aus beiden Abfragen (so wie bei GetRunningProcessesFromWmi()) es auch 312 sein müssten, denn wofür sollte man das sonst so machen.

Aber ich stecke auch nicht so tief drin in der Materie, ich mache da bestimmt etwas falsch, oder habe hier die falschen Einstellungen oder einfach nur die Brille nicht geputzt. Das ist bei mir alles mehr als möglich.

18.06.2024 - 19:19 Uhr

Den Pfad als Key zu verwenden würde ich nicht empfehlen, denn bei mehreren Instanzen wird es zum Zufallsgenerator welche Instanz angezeigt wird. Die ProcessId wäre hier eindeutig.

Den Einwand mit GetProcess kann ich irgendwie nicht nachvollziehen, denn diese Methode ist mir unbekannt.

Wenn damit Process.GetProcesses() gemeint ist, ok, aber wenn das suboptimal ist, dann verstehe ich das hier nicht

var query = from p in Process.GetProcesses()

Kann aber natürlich sein, dass ich das alles nicht verstanden habe.

18.06.2024 - 13:24 Uhr

Alternativ geht auch ParallelEnumerable.AsParallel das würde ohne lock oder anderes Concurrent-Gedöns funktionieren.

12.06.2024 - 12:10 Uhr

Zitat von jogibear9988

Natürlich muss man in WPF nicht alles mit MVVM machen. Vor allem für jedes kleine Tool was man baut finde ich das overkill (ist meine Meinung).

Meine Meinung dazu ist, dass du fast den gleichen Code schreibst allerdings logisch und sauber getrennt in einzelnen Klassen (ViewModel, Model, Service).

Was du anscheinend meinst, ist der Aufwand das Grundgerüst für WPF/MVVM zu erstellen, was man benötigt um überhaupt anfangen zu können. Nun ich habe eine eigene Projektvorlage dafür. Ein Klick und alles ist vorbereitet für WPF/MVVM/DI-Container.

Den Code dann in die unterschiedlichen Klassen zu verteilen dauert kaum länger als deine Variante.

Es hat also eher etwas mit der eigenen Organisation zu tun.

31.05.2024 - 21:53 Uhr

Wird in der Forensuche bei Einschränkung nach Benutzernamen etwas eingetragen, dann erscheint nach dem Klick auf Suchen

Oops! Es ist leider ein Fehler aufgetreten 😕

30.05.2024 - 12:34 Uhr

Grundsätzlich würde ich erst einmal sagen, dass der Ansatz falsch gewählt ist.

So ist ein Getränke-Automaten ein Endlicher Automat und sollte zunächst auch so skizziert werden. Das ist erst einmal unabhängig von jeder Programmiersprache.

Anhand dieser Skizze geht man dann an die Umsetzung. Man kann dafür z.B. eine fertige Bibliothek (z.B. Stateless) nehmen oder auch selber umsetzen. Allerdings benötigt man hier auf jeden Fall die Grundlagen von OOP.

28.05.2024 - 14:03 Uhr

Ich habe dir mal ein kleines Projekt angehängt, wo du sehen kannst, was wir mit MVVM meinen und warum das am Ende "einfacher" ist.

Ja, es ist eine Umstellung wenn man nicht mehr alles auf einmal macht und danach ist es eben einfacher, weil man nicht mehr alles auf einmal macht.

Der eigentliche Teil (Logik) findet hier statt:

public class MainViewModel : ViewModelBase
{
    private readonly ObservableCollection<Dice> _dices;
    public ReadOnlyObservableCollection<Dice> Dices { get; }
    [Reactive] public int DiceCount { get; set; } = 6;
    public ReactiveCommand<Unit, Unit> RollDicesCommand { get; }

    public MainViewModel()
    {
        _dices = [];
        Dices = new( _dices );

        RollDicesCommand = ReactiveCommand.CreateFromTask( OnRollDiceCommandAsync );
    }

    private async Task OnRollDiceCommandAsync()
    {
        // Wir merken uns, wieviele Würfel wir werfen sollen wenn dieses Kommando gestartet wird
        var diceCount = DiceCount;
        // Alten Wurf leeren
        _dices.Clear();
        // Wir simulieren das Würfeln ... es dauert eben, bis die Würfel gerüttelt, geschüttelt, geworfen und zur Ruhe gekommen sind
        await Task.Delay( 500 );
        // Zufälliges Erzeugen der Würfelwerte
        var randomDiceValues = Random.Shared.GetItems<DiceValue>( DiceValue.Values.ToArray(), diceCount );
        // Aus den Würfelwerten erzeugen wir Würfel und tragen diese in die Liste ein, die den Wurf repräsentiert
        foreach ( var item in randomDiceValues.Select( e => new Dice( e ) ) )
        {
            _dices.Add( item );
        }
    }
}

public record Dice( DiceValue Value );

public record DiceValue( int Value )
{
    public int Value { get; } = Value < 1 || Value > 6 ? throw new ArgumentOutOfRangeException( nameof( Value ) ) : Value;

    public static ImmutableHashSet<DiceValue> Values = [new( 1 ), new( 2 ), new( 3 ), new( 4 ), new( 5 ), new( 6 )];
}

Die (mehr oder wenige) ansprechende Darstellung findet an einer ganz anderen Stelle statt, weil diese bei dem Logik-Gedöns eh nur stört.

    <DataTemplate x:Key="Template.Dice.Image" DataType="{x:Type vm:Dice}">
        <Border Margin="5"
                Padding="5"
                BorderBrush="Black"
                BorderThickness="1"
                CornerRadius="10">
            <Image Height="60">
                <Image.Style>
                    <Style TargetType="Image">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Value.Value}" Value="1">
                                <Setter Property="Source" Value="{StaticResource Bitmap.Dice.One}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Value.Value}" Value="2">
                                <Setter Property="Source" Value="{StaticResource Bitmap.Dice.Two}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Value.Value}" Value="3">
                                <Setter Property="Source" Value="{StaticResource Bitmap.Dice.Three}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Value.Value}" Value="4">
                                <Setter Property="Source" Value="{StaticResource Bitmap.Dice.Four}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Value.Value}" Value="5">
                                <Setter Property="Source" Value="{StaticResource Bitmap.Dice.Five}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Value.Value}" Value="6">
                                <Setter Property="Source" Value="{StaticResource Bitmap.Dice.Six}" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Border>
    </DataTemplate>

Fröhliches Herumspielen.

23.05.2024 - 08:36 Uhr

Wie würdest du das analog lösen? Also wenn du keinen Computer hast und du musst die Bestellung aufnehmen?

Wenn du dir das klar gemacht hast, dann suchst du nach einer Struktur, in der man das im Computer (Programmiersprache) abbilden kann. Und das hier ist ein klassisches Problem wofür man auch schon eine fertige Struktur erwarten kann (die es auch gibt).

23.05.2024 - 07:01 Uhr

Du kannst diese Würfel-Images im Constructor direkt nach InitializeComponent() in ein Array schreiben um dieses dann für solche Zwecke zu verwenden.

Alternativ (und grundsätzlich empfehlenswert) ist die Verwendung von MVVM und Binding.

07.05.2024 - 08:43 Uhr

Warum sollte es nicht möglich sein ein Label im laufenden Betrieb dynamische Inhalte anzeigen zu lassen?

Bei WPF solltest du dringend auch MVVM verwenden, denn genau das ist so von MS so angedacht gewesen und man ist immer gut beraten eine Technologie so zu verwenden wie angedacht.

Man muss dabei die Denkweise etwas abstrakter halten und nicht immer sofort in Labels, Textboxen und Buttons denken. Man trennt in Was (will ich eigentlich darstellen) und Wie (will ich das am schönsten präsentieren).

05.05.2024 - 10:11 Uhr

Der klassische Weg (auch wegen Sicherheit) ist eine Web-API die für die Persitenz (Datenbank, Dateien, etc.) zuständig ist.

Für zwei Clients kann man das auch via VPN (z.B. FritzBox & Wireguard) über einen Heim-Server betreiben.

30.03.2024 - 06:06 Uhr

Also ich habe mal die Standard-ConsoleApp mit folgenden dotnet Befehlen auf Windows, macOS und Ubuntu erzeugen lassen:

dotnet publish ConsoleApp -r osx-x64 --sc true -o ./pub/osx-x64 -p:PublishSingleFile=true
dotnet publish ConsoleApp -r win-x64 --sc true -o ./pub/win-x64 -p:PublishSingleFile=true

und dabei kam folgendes heraus

lauffähig Windows macOS Ubuntu
osx-x64 NEIN ja ja
win-x64 ja ja ja

Verwendete Solution im Anhang

28.02.2024 - 10:59 Uhr

Wenn dir OpenXML zu sperrig ist kannst du es auch mit ClosedXml versuchen. Das arbeitet im Hintergrund mit OpenXml, hat aber den Anspruch etwas freundlicher im Umgang zu sein.

17.02.2024 - 18:56 Uhr

Ich frage mich, wie du das sehen willst, denn du lässt die Form wo das passiert gar nicht anzeigen.

PS: Verwende für Code doch bitte die Code-Tags.

09.02.2024 - 10:25 Uhr

Zitat von CSharpNewbie2022

  1. Bei diesem Schichten Modell trennen wir GUI, Business Logic and Data Access. Das habe ich so adaptiert. Als Data Access wäre der Zugriff zur Datenbank und jegliche Art von Lesen und Beschreiben von Dateien. Aber wie sieht es ..
    1. mit Schnittstellen (Ethernet, Ethercat, SPI, RS232, ...)
    2. mit Webdiensten also Kommunikation zu Api s aus, gehören diese Elemente auch in den Data Access Layer?

Diese Dinger unterscheiden sich zu DB und Dateien Zugriff darin, dass mit einem externen Partner kommuniziert wird, also gehört das nun zur Business Logic oder zu dem Data Access Layer?

Alles was nicht in deiner Anwendung ist, ist außen/extern und du baust dir für deine Anwendung eine Schnittstelle um mit dieser Außenwelt zu kommunizieren. Du darfst das gerne unter dem Begriff Infrastructure zusammenfassen.

09.02.2024 - 10:17 Uhr

Zitat von CSharpNewbie2022

2. Ich nutze den DI Container von Microsoft für alle ViewModels und diverse Singleton Klassen aus der Business Logic, damit diese in der GUI Schicht zur Verfügung stehen. Aber kleine Klassen, die nur innerhalb einer Klasse verwendet werden, erstelle ich immernoch mit new. Ist das eher gegen das Konzept? Sollte jedes Object mit dem DI erstellt werden?

Mal eine vereinfachte Darstellung:

  • Alle Klassen, die für andere Klassen Abhängigkeiten (Dependencies) darstellen sowie die, die eben solche Abhängigkeiten (Dependencies) haben werden registriert und über den DI-Container erzeugt.
  • Die anderen Klassen ... wozu?

Das ViewModel ist abhängig von einem DatenService und dieser ist abhängig von einem DbContext also werden diese alle am DI-Container registriert und von diesem erzeugt.

Das ViewModel schickt jetzt einen Datensatz zum speichern an den DatenService und der schickt das an den DbContext weiter. Dieser Datensatz (ist eine Klasse) hat keinerlei Abhängigkeiten (Dependencies) zu irgendwas und hat somit nichts mit Dependency-Injection am Hut.

02.02.2024 - 06:34 Uhr

Zitat von Stefan68

Eine frage zum Thema MVP ist es in der Praxis üblich das im Model auch Forms-Objekte wie z.B. ListViewItems oder andere Forms-Controls erzeugt werden und an die Form übergeben werden?

Hier wird das gut beschrieben Model-View-Presenter in WinForms

25.01.2024 - 20:45 Uhr

Aber über OnKeyUp/OnKeyDown bekommst du doch alles was du benötigst. So wie ich das sehe werden diese Steuerzeichen übermittelt, als wenn du die über die Tastatur eingeben würdest (so hat man früher zu DOS Zeiten die Sonderzeichen bekommen):

  • ALT down
  • 0 down
  • 0 up
  • 0 down
  • 0 up
  • 3 down
  • 3 up
  • 0 down
  • ALT up
  • 0 up

Damit wird RS übermittelt. Die anderen Zeichen analog. Damit solltest du jetzt deinen Barcode zusammenstellen können.

24.01.2024 - 21:08 Uhr

Zitat von Abt

Persönlich lösche ich aber auch immer diese Collection und fülle sie dann.

Die generelle Empfehlung ist in WPF, genau das eigentlich nicht zu tun, weil der Overhead von OnPropertyChanged,der Allocation und die Bindungsaktualisierung i.d.R. teurer ist als das Clear, das O(n) ist.

Er meinte damit "lösche den Inhalt" - sieht man auch wenn man seinen Code dazu anschaut 😉 Also genau so wie empfohlen.

24.01.2024 - 20:56 Uhr

Zitat von Abt

OnPropertyChanged ist der Framework-Mechanismus, wenn Du Properties hast, die einen "Dirty State" haben, der eigentlich optimalerweise zu vermeiden ist. Damit triggerst Du WPF an, dass die Referenz neu bekannt gemacht wird und die Value wird aktualisiert.

Er lässt sich bei den Typen vermeiden, die eine direkte Möglichkeit der Bindung haben; wie die ObservableCollection.

Diese Aussage verstehe ich nicht, denn ich hatte noch niemals Probleme damit so eine ObservableCollection neu zu erstellen.

Doch hattest Du, denn die Bindung in diesem Moment ist nicht mehr vorhanden, sie zeigt auf die alte Referenz.
Du wirst sicherlich eben auch OnPropertyChanged triggern, um das zu lösen.

Also ich habe speziell diese "Technische Limitation" nicht verstanden, da es einfach nur eine observable Property braucht wie halt bei jeder Property, deren Wert sich ändern kann und diese Änderungs Benachrichtigung benötigt wird.

Hier von einem Problem zu sprechen?

Ich kann versichern, dass ich da genau kein Problem hatte/habe/haben werde.

24.01.2024 - 13:17 Uhr

Zitat von Abt

Technische Limitation: Deine ObservableCollection darf nicht neu erzeugt werden.

Diese Aussage verstehe ich nicht, denn ich hatte noch niemals Probleme damit so eine ObservableCollection neu zu erstellen.

Kannst du das bitte einmal erläutern?

22.01.2024 - 13:36 Uhr

Du kannst es mit SoundPlayer versuchen.

Der spielt z.B. eine WAV-Datei ab die du beliebig starten und anhalten kannst.

private readonly SoundPlayer _sound = new SoundPlayer("<Pfad zu einer WAV-Datei>");
const int DIT_LENGTH = 100;
const int DAH_LENGHT = DIT_LENGTH * 3;
const int SYMBOL_LENGTH = DIT_LENGTH;
const int CHAR_LENGth = DIT_LENGTH * 3;
const int WORD_LENGTH = DIT_LENGTH * 7;

public async Task SOS()
{
    // S
    await Dit();
    await SymbolGap();
    await Dit();
    await SymbolGap();
    await Dit();
    
    await CharGap();

    // O
    await Dah();
    await SymbolGap();
    await Dah();
    await SymbolGap();
    await Dah();
    
    await CharGap();

    // S
    await Dit();
    await SymbolGap();
    await Dit();
    await SymbolGap();
    await Dit();
}

public async Task Dit()
{
    _sound.Play();
    await Task.Delay(DIT_LENGTH);
    _sound.Play();
}

public async Task Dah()
{
    _sound.Play();
    await Task.Delay(DAH_LENGTH);
    _sound.Play();
}

public Task SymbolGap() => Task.Delay(SYMBOL_LENGTH);
public Task CharGap() => Task.Delay(CHAR_LENGTH);
public Task WordGap() => Task.Delay(WORD_LENGTH);
22.01.2024 - 12:41 Uhr

Die Antwort findest du auf stackoverflow.com.

TL;DR

Is so, liegt aber an Windows selber, dann dotnet leitet das nur an die Window Api weiter und das kommt dabei heraus.

BTW

Es wäre schön, wenn du Code in die entsprechenden Code-Tags packst, dann würde dein Code z.B. so aussehen (hübsch, gell?)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

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

        private void CmdEnde_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void CmdStart_Click(object sender, EventArgs e)
        {
            Console.Beep(600, 100); // Länge 50 ms
            Console.Beep(600, 100);
            Console.Beep(600, 100);
            Console.Beep(600, 300); // Länge 150 ms
            Console.Beep(600, 300);
            Console.Beep(600, 300);
            Console.Beep(600, 100); // Länge 50 ms
            Console.Beep(600, 100);
            Console.Beep(600, 100);
        }
    }
}
16.01.2024 - 23:23 Uhr

Wpf.Extensions.Hosting

// Create a builder by specifying the application and main window.
var builder = WpfApplication<App, MainWindow>.CreateBuilder(args);

// Configure dependency injection.
// Injecting MainWindowViewModel into MainWindow.
builder.Services.AddTransient<MainWindowViewModel>();

// Configure the settings.
// Injecting IOptions<MySettings> from appsetting.json.
builder.Services.Configure<MySettings>(builder.Configuration.GetSection("MySettings"));

// Configure logging.
// Using the diagnostic logging library Serilog.
builder.Host.UseSerilog((hostingContext, services, loggerConfiguration) => loggerConfiguration
    .ReadFrom.Configuration(hostingContext.Configuration)
    .Enrich.FromLogContext()
    .WriteTo.Debug()
    .WriteTo.File(
        @"Logs\log.txt", 
        rollingInterval: RollingInterval.Day));
    
var app = builder.Build();

await app.RunAsync();
16.01.2024 - 15:16 Uhr

Zitat von Th69

Oder suche mal nach "high precision/resolution timer", z.B. PrecisionTimer.NET.

Das löst das Problem aber auch nicht, es wird evtl. etwas kleiner. Mein Ansatz löst das Problem und macht es sogar noch testbar.

16.01.2024 - 14:24 Uhr

Hier habe ich mal etwas, womit du ein wenig herumspielen kannst.

https://dotnetfiddle.net/QqcXHo

using System;

var startDateTime = new DateTimeOffset( 2021, 01, 01, 00, 00, 00, TimeSpan.Zero );
var endDateTime = startDateTime.AddMinutes( 1 );

var clock = new FakeClock { UtcNow = new DateTimeOffset( 2021, 01, 01, 00, 00, 00, TimeSpan.Zero ) };
var foo = new Foo( clock, TimeSpan.FromMilliseconds( 500 ) )
{
    ActualSpeed = 60,
};
foo.Start();

while ( clock.UtcNow < endDateTime )
{
    var timeSpan = TimeSpan.FromMilliseconds( 500 + Random.Shared.Next( 5, 50 ) );
    clock.UtcNow += timeSpan;
    var slices = foo.Tick();
    Console.WriteLine( "[{0:hh:mm:ss.ffffff}] {1} ({2})", clock.UtcNow, foo.Distance, slices );
}

foo.Stop();


interface ISystemClock
{
    DateTimeOffset UtcNow { get; }
}

class RealTimeClock : ISystemClock
{
    public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
}

class FakeClock : ISystemClock
{
    public DateTimeOffset UtcNow { get; set; } = DateTimeOffset.UtcNow;
}

class Foo
{
    private DateTimeOffset _lastTick;
    public Foo( ISystemClock systemClock, TimeSpan slice )
    {
        SystemClock = systemClock;
        Slice = slice;
    }
    public bool IsRunning { get; private set; }
    public double ActualSpeed { get; set; }
    public double Distance { get; private set; }
    public ISystemClock SystemClock { get; }
    public TimeSpan Slice { get; }
    public int Tick()
    {
        if ( !IsRunning ) return 0;
        var now = SystemClock.UtcNow;
        var duration = now - _lastTick;
        if (duration < Slice) return 0;
        var slices = Convert.ToInt32( duration.TotalMilliseconds / Slice.TotalMilliseconds );
        var consumedDuration = slices * Slice;
        _lastTick = now - ( duration - consumedDuration );

        Distance += ActualSpeed / TimeSpan.FromMinutes( 1 ).TotalMilliseconds * slices * Slice.TotalMilliseconds;
        return slices;
    }

    public void Start()
    {
        if ( IsRunning ) return;
        _lastTick = SystemClock.UtcNow;
        IsRunning = true;
    }
    public void Stop()
    {
        if ( !IsRunning ) return;

        IsRunning = false;
    }
}
16.01.2024 - 00:21 Uhr

Das ist ja eine lustige Logik in dem Programm. Auf jeden Fall ist die Userliste immer sehr schön aufgeräumt.

13.01.2024 - 15:50 Uhr

Zitat von DanHue

Ich bin langsam am verzweifeln...

Anscheinend noch nicht genug.

Gibt es vielleicht ein Idee wie man das anders lösen könnte?

Wenn du ernsthaft an einer Lösung interessiert bist dann hättest du schon längst (gleich von Anfang an) ein kleines Demo-Projekt gezeigt anstatt größtmöglichst kompliziert verklausuliert den Ablauf beschrieben bzw. immer nur kleinste Fragmente gezeigt, die aber nicht den gesamten Zusammenhang darstellen. Es gibt viele falsche Wege und auf jeden Fall erheblich weniger richtige Wege. Woher sollen wir wissen, wo genau dein Fehler liegt ohne den gesamten (relevanten) Code zu sehen?

09.01.2024 - 14:03 Uhr

Na endlich.

Und wenn du beim nächsten Mal auch direkt Beispieldaten mitlieferst, dann geht das auch erheblich schneller.

Das Rumgeeiere fing vor 11 Tagen an und zog sich wie ein Kaugummi.

Heute endlich Beispieldaten und nach ein paar Stunden alles geklärt.

09.01.2024 - 09:21 Uhr

Also hier haben wir die Werte der Anschnittscheiben im DataGrid

//                                              Anschnittscheibe
TblProgramminfo.Rows.Add(new object[] { "5075777", "7778888", false, true });
TblProgramminfo.Rows.Add(new object[] { "5075999", "8889999", false, true });

Und diese aus der ComboBox

//                                       SAP-Nummer
TblScheibendaten.Rows.Add(new object[] { "8318555", "250x16x76,2", "Schneid-Formend", "Anschnitt / Fuehren"});
TblScheibendaten.Rows.Add(new object[] { "8318556", "250x16x76,2", "Schneid-Formend", "Anschnitt / Fuehren" });
TblScheibendaten.Rows.Add(new object[] { "8318557", "250x16x76,2", "Schneid-Formend", "Anschnitt / Fuehren" });

Und nach welcher Logik soll da jetzt 7778888 mit welchem der Werte 8318555,8318556,8318557 matchen?

06.01.2024 - 12:46 Uhr

Hier mal dein Beispiel umgesetzt

<Window x:Class="WpfApp.DataWindow"
        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:WpfApp"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="DataWindow"
        Width="800"
        Height="450"
        mc:Ignorable="d">
    <Grid>
        <DataGrid x:Name="AutomobileDataGrid"
                  AutoGenerateColumns="True"
                  AutoGeneratingColumn="AutomobileDataGrid_AutoGeneratingColumn" />
    </Grid>
</Window>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApp
{
    /// <summary>
    /// Interaktionslogik für DataWindow.xaml
    /// </summary>
    public partial class DataWindow : Window
    {
        private readonly IList<HerstellerLookup> _herstellerList =
            [
                new HerstellerLookup { Id = 1, Name = "BMW", },
                new HerstellerLookup { Id = 2, Name = "MERCEDES", },
                new HerstellerLookup { Id = 3, Name = "OPEL", },
                new HerstellerLookup { Id = 4, Name = "VW", },
            ];
        private readonly IList<LackierungLookup> _lackierungList =
            [
                new LackierungLookup { Id = 1, Name = "Blau", Zusatz = "Alkyd", Pigmentierung = "leicht", },
                new LackierungLookup { Id = 2, Name = "Grau", Zusatz = "Alkyd", Pigmentierung = "leicht", },
                new LackierungLookup { Id = 3, Name = "Schwarz", Zusatz = "Polyacryl", Pigmentierung = "nein", },
                new LackierungLookup { Id = 4, Name = "Weiss", Zusatz = "Polyacryl", Pigmentierung = "nein", },
                new LackierungLookup { Id = 5, Name = "Silber", Zusatz = "Alkyd", Pigmentierung = "leicht", },
            ];
        private readonly IList<Automobil> _automobilList =
            [
                new Automobil { Id = 1, HerstellerId = 4, Baujahr = 2015, LackfarbeId = 1, Kilometerstand = 50_000, },
                new Automobil { Id = 2, HerstellerId = 1, Baujahr = 2012, LackfarbeId = 2, Kilometerstand = 100_000, },
                new Automobil { Id = 3, HerstellerId = 1, Baujahr = 2019, LackfarbeId = 3, Kilometerstand = 30_000, },
                new Automobil { Id = 4, HerstellerId = 2, Baujahr = 2018, LackfarbeId = 4, Kilometerstand = 40_000, },
                new Automobil { Id = 5, HerstellerId = 3, Baujahr = 2020, LackfarbeId = 5, Kilometerstand = 50_000, },
            ];

        public DataWindow()
        {
            InitializeComponent();

            AutomobileDataGrid.ItemsSource = _automobilList;
        }

        private void AutomobileDataGrid_AutoGeneratingColumn( object sender, System.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e )
        {
            switch ( e.Column.SortMemberPath )
            {
                case nameof( Automobil.HerstellerId ):
                    e.Column = new DataGridComboBoxColumn
                    {
                        Header = e.Column.Header,
                        ItemsSource = _herstellerList,
                        DisplayMemberPath = nameof( HerstellerLookup.Name ),
                        SelectedValuePath = nameof( HerstellerLookup.Id ),
                        SelectedValueBinding = new Binding( nameof( Automobil.HerstellerId ) ) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, },
                    };
                    break;
                case nameof( Automobil.LackfarbeId ):
                    e.Column = new DataGridComboBoxColumn
                    {
                        Header = e.Column.Header,
                        ItemsSource = _lackierungList,
                        DisplayMemberPath = nameof( LackierungLookup.Name ),
                        SelectedValuePath = nameof( LackierungLookup.Id ),
                        SelectedValueBinding = new Binding( nameof( Automobil.LackfarbeId ) ) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, },
                    };
                    break;
                default:
                    break;
            }
        }
    }

    public class Automobil
    {
        public int? Id { get; set; }
        public int? HerstellerId { get; set; }
        public int? Baujahr { get; set; }
        public int? LackfarbeId { get; set; }
        public int? Kilometerstand { get; set; }
    }

    public class HerstellerLookup
    {
        public int Id { get; init; }
        public string Name { get; init; } = string.Empty;
    }

    public class LackierungLookup
    {
        public int Id { get; init; }
        public string Name { get; init; } = string.Empty;
        public string Zusatz { get; init; } = string.Empty;
        public string Pigmentierung { get; init; } = string.Empty;
    }
}
06.01.2024 - 00:07 Uhr

In der Spalte "Anschnittscheibe" steht der Wert "7778888" und du beklagst dich, dass in der ComboBox nichts angezeigt wird, wenn in der ComboBox.ItemsSource folgende Werte enthalten sind "0815", "0816", "4711" - ist das richtig so?

Dann pack doch mal diese Werte in die Liste für ComboBox.ItemsSource "0815", "0816", "4711", "7778888".

Ist der Wert nicht in der Liste, dann zeigt die ComboBox nichts an (sieht so aus wie in deinem Bild DataGridComboBoxColumn_04.jpg).

05.01.2024 - 19:59 Uhr

So sollte es passen

var dgComboBoxColAnschnittscheibe = new DataGridComboBoxColumn()  
{  
   Header = "Anschnittscheibe",  
   Width = new DataGridLength(150),  
   ItemsSource = new List<string>{ "0815", "0816", "4711" },  
   SelectedValueBinding = new Binding("Anschnittscheibe") { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, },  
   DisplayIndex = 2  
};  
e.Column = dgComboBoxColAnschnittscheibe;  
03.01.2024 - 15:23 Uhr

Zitat von Th69

Zur Info für oehrle: auch bei MVVM in einem ViewModel funktioniert dies (nur eben per Data Binding, anstatt direkt im Code ItemsSource zu setzen).

Genau das funktioniert bei mir nicht. Das ging nur im Code. Setzt man die ItemsSource per DataBinding im XAML dann bleibt die ComboBox leer und Snoop-WPF sagt: „Was für eine ItemsSource?“

03.01.2024 - 14:02 Uhr

Zitat von Th69

Ich verstehe nicht, was du als Ausschlußkriterium siehst.

Du hast tatsächlich Recht. Im Code-Behind im Event gesetzt funktioniert das wie gewünscht.

03.01.2024 - 11:23 Uhr

Zitat von Th69

Ich bin mir sicher, daß es auch mit der DataGridComboBoxColumn funktioniert, es kommt nur auf die passenden Datentypen an.

Diese Annahme wird weder durch die Dokumentation noch Snoop-Wpf gestützt.

03.01.2024 - 09:04 Uhr

Also im Code-Behind der View kommt eigentlich nur Code rein der zur Visualisierung benötigt wird. Der gesamte Rest nimmt Platz im ViewModel, DataServices, Services usw. In der View muss dir ja auch klar sein, von wo du die Daten beziehst und da das abhängig von den Daten ist, ist das auch im ViewModel bekannt.

Bei dem DataTemplate und DataEditTemplate beides Male die ComboBox eintragen, allerdings im DataTemplate mit IsEnabled="False".

03.01.2024 - 03:38 Uhr

Zitat von oehrle

Ne, der definierte Wert von Anschnittscheibe wird nicht angezeigt, bleibt leer. Eigentlich müßte da der Wert 7778888 angzeigt werden.

Wenn da der Wert 7778888 angezeigt werden soll, dann verstehe ich deine Zeichnung mit dem gelben und dem grünen Pfeil nicht.

"SAP-Nummer" soll als Auswahlkriterium in DataGridComboBox auswählbar sein.

Zu sehen sind die Nummern 8318555,8318556,8318557

Ich hätte jetzt vermutet, dass da diese SAP-Nummern stehen sollten.

Egal, so geht das auf jeden Fall nicht, denn das kann die DataGridComboBoxColumn laut Dokumentation nicht.

Um die Dropdownliste aufzufüllen, legen Sie zunächst die ItemsSource -Eigenschaft für die ComboBox fest, indem Sie eine der folgenden Optionen verwenden:

Somit bleibt nur noch die DataGridTemplateColumn übrig um das zu realisieren.

31.12.2023 - 14:01 Uhr

Zitat von GeneVorph

[…] warum da kein Brush drinne ist. […]

Wenn du Colors bemühst und die Eigenschaften vom Typ Color sind (macht ja auch Sinn) wieso erwartest du dann den Typ Brush?

Wenn du das haben möchtest dann nimm statt Colors die Klasse Brushes, denn die hat (wie der Name schon verkündet) Eigenschaften vom Typ Brush (also den du auch haben möchtest).

Wenn man in die Apfelkiste greift und sich wundert dass da keine Bananen drin sind. 🤔

30.12.2023 - 23:04 Uhr

Du weißt aber schon, dass die statischen Eigenschaften der statischenColors Klasse vom Typ Color sind?

Du weißt aber schon, dass die statischen Eigenschaften der statischenBrushes Klasse vom Typ Brush(bzw. konkret SolidColorBrush) sind?

Das casten in eine statische Klasse (z.B. Colors, Brushes) ist idR. immer falsch.

29.12.2023 - 12:32 Uhr

Ja, das Timing spielt halt auch eine große Rolle. Darum solltest du auch mit dem Debugger arbeiten und hier z.B. prüfen, welcher Wert in DataContext steckt. Dann wäre dir auch aufgefallen, dass der zu dem Zeitpunkt ebenfalls null ist.

Wenn du mal beschreiben könntest was du wirklich vorhast - also mehr Details als "ich will Daten mitnehmen" - dann könnte man dir wirklich Beispiel-Code geben.

29.12.2023 - 00:32 Uhr

Nun ja du kannst casten

var viewmodel = DataContext as MyClass;

sowie weitere viele Dinge die aber alles Grundlagen von C# sind

wenn ich MyClass in Window1 in Codebehind lade, das dieser dann leer ist.

Hmmm, es ist auch hilfreich die richtige Terminologie zu verwenden. Wenn du eine neue Instanz erzeugst, dann wird diese eben nicht geladen, sondern erzeugt und ist auch nicht leer sondern enthält die default Werte.

Problematisch ist, das man dann oft aneinander vorbei redet weil man es falsch beschreibt/benennt.

28.12.2023 - 20:57 Uhr

Lass es mich so erklären:

Du hast einen Korb und legst dort einen Apfel hinein. Wenn dein Bruder jetzt in diesen Korb hineinschaut, was sieht er? Genau, diesen einen Apfel.

Jetzt nimmt dein Bruder einen neuen Korb, der deinem Korb erstaunlich ähnlich ist, und schaut in diesen, was sieht er? Genau, gähnende Leere.

Wenn du erklären kannst, warum das so ist, dann hast du dein Problem erklärt.