Laden...

Forenbeiträge von Alf Ator Ingesamt 785 Beiträge

08.04.2025 - 16:49 Uhr

Das ist dann sowas wie OneDrive/Teams/Sharepoint.

17.03.2025 - 09:05 Uhr

Zitat von Abt

Wir haben im Rahmen eines Microsoft AI Platform Use Cases mit Klemmbausteinen (technische Beschreibung hier)  ..

Was für ein tolles Projekt 😄

27.02.2025 - 09:36 Uhr

Hallo AnnaBauer21

Ich kann aus dem was ich hier sehe, nur raten. Versuch doch mal bei

new Binding { RelativeSource = tagData.Item1, Path = tagData.Item2};

einen BindingMode mit anzugeben.

Gruss
Alf

27.01.2025 - 12:21 Uhr

Zitat von Abt

.. Google hat da ne recht nette Seite für https://developers.google.com/optimization und auch nen NuGet Package https://www.nuget.org/packages/Google.OrTools

Das ist perfekt, vielen Dank!

(https://developers.google.com/optimization/scheduling/job_shop)

24.01.2025 - 20:35 Uhr

Hallo Community

Aktuell arbeite ich an einer Ressourcen-Planung, bei der ich an einem Problem mit exponentiellem Rechenaufwand zu knabbern habe.

Es geht darum n Batches mit Tasks auf m Resourcen aufzuteilen. Die Tasks haben einen Start-Zeitpunkt und eine Duration und dürfen sich nicht überschneiden. Die Tasks pro Batch folgen zeitlich aufeinander und haben eine feste Reihenfolge.

Kennt jemand einen Algorithmus, der sich darauf anwenden lässt? Lässt sich der Aufwand für dieses Problem überhaupt reduzieren? Ich habe leider noch nichts passendes gefunden.

Task
- Start
- Duration
- End

Batch
- Tasks
        
Resource
- Tasks

Beim Einsortieren der Tasks auf die Ressourcen entstehen zwischen drin immer wieder Lücken, diese möchte ich nutzen um das Ergebnis zu optimieren.

foreach batch in batches
    foreach batchTask in batch
        foreach resourceTask in resource
            if batchTask.Start > resourceTask.End && batch.End < nextResourceTask.Start
                resource.Insert(behind resourceTask)

Im Pseudocode sieht man gut, wie sich der Aufwand exponentiell erhöht.

Hier ein kleines Beispiel für die bessere Vorstellbarkeit:

[Batch 1]
- Anton (Resource A) füllt einen Eimer mit Farbe (2 min) und bring in zu Bernd (Resource B)
- B nimmt diesen Eimer und streicht damit eine Wand (25 min)
- A wäscht den benutzen Eimer (4 min)

[Batch 2]
- Anton bringt ein Kabel und Nagel-Schellen (1 min) zu Carlos (Resource C)
- C verlegt das Kabel (35 min)

[Batch 3]
- Anton belädt den Bauaufzug mit Ziegeln und lässt sie nach oben fahren (15 min)
- Daniel (Resource D) deckt damit das Dach (55 min)

Es sollen 18 Wände gestrichen und 12 Kabel verlegt werden. Das Dach braucht 7 Fuhren um gedeckt zu werden.

Gruss
Alf

23.01.2025 - 16:26 Uhr

Alternativ könnte man auch ein TabControl benutzen. Aber probier den Link von Th69 auf jeden Fall aus.

22.01.2025 - 09:54 Uhr

Hallo bizwormsunterayri

Wir helfen euch hier gerne. Posted einfach eure konkreten Probleme hier im Forum.

Schaut euch dafür auch: https://mycsharp.de/forum/threads/26594/hinweis-wie-poste-ich-richtig an.

Versucht eure Probleme möglichst konkret zu beschreiben und packt auch euren Code dazu.

Zitat von bizwormsunterayri

zB beim Coden eines Würfels ...

public int RollDice() => Random.Shared.Next(1, 6);

Zitat von bizwormsunterayri

... einer Auswahl der Figuren nach den Farben (Gelb,grün,blau,rot)

Wie ist das gemeint?

Gruss
Alf

20.01.2025 - 09:19 Uhr

Hallo Salman

Ich vermute, dass die Methode NextLevel nicht aufgerufen wird. Den Code für den diesen Aufruf zeigst du leider nicht.
Hast du dich denn mal mit dem Debugger drangehängt? Dann müsste man das doch easy sehen.

Gruss
Alf

17.01.2025 - 15:25 Uhr

Verstehe ich nicht, da wird doch nach mehreren Spalten sortiert:

"You can always add to multiple SortDescriptor to the SortCollection of your default view like this -"

ListCollectionView lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(myCollection);
lcv.SortDescriptions.Add(new SortDescription(…));

"I don't know how to write it in VB, but i can show you in c# how it's made:"

YourListView = CollectionViewSource.GetDefaultView(tempListView
.OrderBy(x => x.FirstSorting)
.ThenBy(y => y.SecondSorting));

Machst du MVVM?

15.01.2025 - 09:19 Uhr

Hallo lutzeslife

Man kann doch mit CollectionViewSource nach mehreren Spalten sortieren. Siehe hier: https://stackoverflow.com/questions/8104015/how-to-implement-multiple-levels-of-sorting-on-a-collectionviewsource

Hast du das schon ausprobiert?

Gruß
Alf

19.11.2024 - 11:06 Uhr

Top, danke dir.

Bei mir hat Resharper noch mit rein gespielt. Dort kann man auch zusätzliche Dictionaries anlegen. Ich habe das aber nicht weiter verfolgt.

18.11.2024 - 12:15 Uhr

Stimmt, danke.

Ich muss meine Beschreibung korrigieren und das Problem besser erklären:

  • Ich habe fälschlicherweise Warning geschrieben, aber Message gemeint.
  • "Typo in string" wird im tool tip angezeigt.
  • Wörter die nicht in meinem 'custom dictionary' vorkommen, sollen nach wie vor als Message angezeigt und blau unterkringelt werden. Deswegen hilft severity an der Stelle leider nicht.
18.11.2024 - 11:01 Uhr

Hallo zusammen

Wie im angehängten Screenshot zu sehen, zeigt Visual Studio (2022) Warnings für Wörter an, die er nicht kennt. Mit exclusion.dic und .editorconfig kann man solche Wörter aus den Warnings raus nehmen (siehe erstes Wort). Allerdings wird für da excluded word immer noch blau unterkringelt und "Typo in string" angezeigt. Kennt jemand eine Lösung dafür?
Meine Idee wäre es ein zusätzliches spelling dictionary zu benutzen, dass meine Wörter enthält. Ich habe aber keine Möglichkeit gefunden ein eigenes custom spelling dictionary in die Solution einzubinden. Weiss jemand, ob und wie das geht?

Vielen Dank für eure Unterstützung schon mal.

16.10.2024 - 10:57 Uhr

Hallo tombadil28

Erstmal offtopic: Bitte verwende Code-Tags für dein Listing. Siehe auch: [Hinweis] Wie poste ich richtig?

Zum Thema:

In der Doku zum QuickGrid steht, welchen Typ Items erwartet: IQueryable<TGridItem>. Wenn du also private IQueryable<Match> matches; verwendest, sollte der Fehler behoben sein

Siehe hier: https://learn.microsoft.com/de-de/aspnet/core/blazor/components/quickgrid?view=aspnetcore-8.0&tabs=visual-studio

Gruss
Alf

04.09.2024 - 09:23 Uhr

Hallo ThaKilla

Du kannst ein DTO (data transfer object) verwenden um deine Daten zu serialisieren. Das ist einfach eine Klasse mit den entsprechenden Properties. Das DTO füllst du mit den Daten aus dem DataGridView/DataSet und serialisierst es dann. Wenn du mehrere Dateien haben willst, serialisierst du einfach mehrere DTOs. Ist wirklich einfach.

Gruss
Alf

15.08.2024 - 12:16 Uhr

Zitat von AmpelB

Ich sehe gerade, bei dir sind die Bilder als byte[] drin. Wie hast du das denn gemacht?

Bei deinem 2. Bild, habe ich Byte (WPF) ausgewählt, statt Icon (Windows Forms).

15.08.2024 - 10:21 Uhr

Habe mal update gemacht. Bei mir sieht es so aus. Beim einbinden von der Resource kannst du einen Typ auswählen.

15.08.2024 - 09:26 Uhr

Ist es eventuell einfach unter Images?

15.08.2024 - 09:14 Uhr

Zitat von visionmaster

Dann bleibt wohl wirklich nur der von Dir vorgeschlagene Weg. Ich werde es versuchen...

Dann kannst du dir auch mal AutoIt anschauen.

23.07.2024 - 09:43 Uhr

Zitat von bigeddie

ch hoffe ihr könnt mir etwas weiter helfen.

Wenn es nicht zwingend notwendig ist, dann arbeite nicht direkt mit Office.Interop. Siehe hier:

https://mycsharp.de/forum/posts/3842033

Zitat von Palladin007

Lieber mit Hilfe von DocumentFormat.OpenXml, oder - mein Favorit - Du nutzt das Framework ClosedXml, was das durchaus komplexe OpenXml-ObjektModel deutlich vereinfacht.

Gruss
Alf

20.03.2024 - 11:51 Uhr

Hallo Ralf2022

Ich vermute das TCPServerStopp nicht aufgerufen wird. Setze doch mal einen Breakpoint und prüfe das. [Artikel] Debugger: Wie verwende ich den von Visual Studio?

Hast du den TCP/IP-Server asynchron gestartet?

Gruss
Alf

25.01.2024 - 09:17 Uhr

Zitat von CSharpNewbie2022

Zitat von Alf Ator

Du könntest ein Haupt-ViewModel benutzen, dass die anderen ViewModels hält und mit Daten füttert.
Ich habe dir mal ein minimales Beispiel angehängt.

Ich hatte jetzt doch lust etwas wenig zu schlafen und mich daran zu setzen Dein Beispiel ist doch dahingegen anders als mein Fall, da ich mehrere Views habe, es ist ähnlich zu dem Fall bei dem Verlinkten anderen Beitrag.

Das Beispiel lässt sich doch supereinfach auf deinen Fall erweitern. Ich dachte das reicht aus.

Jetzt frage ich mich ok, Text zu Zahl aus der DB ist Teil des Models/Businessmodels, Laden des Inhalts der Zahlen aus einer Datei und Speichern in eine Datei auch auch?

Ja. Schau dir dazu das Repository-Pattern an.

Was passiert dann im ViewModel, werden dort nur die Klassen DB und File laden als Objekte erstellt und die Methoden genutzt + Button Commands und Alle Binding Variablen?

Ja.

Versuche ich soviel in Modell zu legen, wie geht?

Es geht darum, Geschäftslogic und UI(-Logic) voneinander zu trennen.

Die Methoden des Modells spucken als return values dann strings oder listen aus und diese werden im Viewmodell an die Binding variablen übergeben?

Ja.

Ich hab das Beispiel etwas erweiter.

24.01.2024 - 11:53 Uhr

Du könntest ein Haupt-ViewModel benutzen, dass die anderen ViewModels hält und mit Daten füttert.
Ich habe dir mal ein minimales Beispiel angehängt.

22.01.2024 - 16:12 Uhr

Okay, sorry für den unverständlichen Beitrag 😦

Der Name CellViewModel ist etwas ungünstig gewählt. Ich hatte mich an deinem Beispielcode orientiert. 
RowViewModel wäre besser. Du kannst im RowViewModel' für jede Spalte, die im DataGrid angezeigt werden soll ein Property anlegen. Für 50 Spalten also 50 Properties.

Die Lösung passt eventuell nicht zu deinem UseCase. Was sollte denn mit den angezeigten Daten gemacht werden? Sollen die nur angezeigt, oder bearbeitet werden? Ändert sich das Excel-Dokument regelmässig, oder bleibt es gleich?

22.01.2024 - 14:27 Uhr

Hallo Caveman

Wie Abt schon gemeint hat, ObservableCollection nur einmal zuweisen und binden.


        public ObservableCollection<Model> Items { get; } = new ObservableCollection<Model>();

Statt das Model im View zu verwenden könntest du auch ein ViewModel verwenden. Siehe Bild.

Edit:

Das wäre dann übrigens auch die Lösung für dein Problem mit List<List<T>>

            <DataGrid.Columns>
                <DataGridTextColumn Header="Header" Binding="{Binding CellValue}" />
            </DataGrid.Columns>
            <behav:Interaction.Triggers>
                <behav:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=TestDataGrid}" >
                    <behav:InvokeCommandAction Command="{Binding ColumnGeneratingEvent, Mode=OneWay}" PassEventArgsToCommand="True" />
                </behav:EventTrigger>
            </behav:Interaction.Triggers>

Entweder Autogenerate Columns, oder Columns fest definieren. Beides gleichzeitig kommt nicht so gut.

16.01.2024 - 10:39 Uhr

Servus Caveman

Das sieht doch schonmal ganz gut aus. Von mir noch ein paar Anmerkungen.

ObservableCollection verwende ich eher im UI-Context. Wurde aber schon erwähnt. Die ObservableCollection kann dann im ViewModel gefüllt werden. Je nach Komplexität kann der User dann noch in ein UserViewModel konvertiert werden.

Erstellen und Anzeigen vom Window mache ich nicht im App-Konstruktor, sondern:

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            
            // hier
        }
    }

Du hast View und ViewModel als Singleton erstellt. Sofern es dafür keine speziellen Gründe gibt, würde ich die transient machen. ViewModel würde ich generell nicht als Singleton machen, sonst hast du auf einmal Daten auf der View, die da nicht sein sollten.

Gruss
Alf

11.01.2024 - 09:57 Uhr

Okay, ich verstehe das Problem.

Die Width von der ersten Column ist auf Auto gesetzt. Durch das aufklappen des Expanders wird die Column breiter.
Um das zu verhindern, kannst du die Column-Width fest auf 200 setzten.

Was mit dem Expander nicht geht, ist dass der Expander die Column 'verlässt'. Dafür müsstest du dann z.B. ein Popup verwenden.

Du könntest das gewünschte Verhalten evt. auch mit Grid.ColumnSpan="3" erreichen.

Gruss
Alf

09.01.2024 - 17:54 Uhr

Hallo CSharpNewbie2022

Stell doch den ZIndex vom Expander testweise mal auf 1.

Gruss
Alf

Edit:
Oder so:

<Rectangle Fill="LightGray" Width="200" Height="100" Grid.ZIndex="2" />
<Ellipse Fill="LightBlue" Width="100" Height="100"  Grid.ZIndex="3"/>
<Expander HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="myExpander" Grid.ZIndex="4" ExpandDirection="Right">
09.01.2024 - 14:37 Uhr

Hatte dein Projekt mal aufgemacht um das anzuschauen und hab ganz, ganz grob sowas wie MVVM und Schichten reingemacht.

09.01.2024 - 12:03 Uhr

Les die Frage von BlonderHans nochmal genau. Zwischen 'TblProgramminfo' und 'TblScheibendaten' besteht keine (für uns erkennbare) Beziehung. Wie ist da die Verbindung?

02.01.2024 - 10:08 Uhr

Hallo Andi153

In der Doku steht, dass ServiceBase in der Assembly "System.ServiceProcess.ServiceController.dll" ist.

Gruss
Alf

02.01.2024 - 09:45 Uhr

Zitat von mezzo80

Als nächstes möchte ich aber in Window1 im Codebehind direkt auf den String von FirstName zugreifen.

Hallo mezzo80

Da liegt schon der Fehler. Mit MVVM wird das anders gelöst. Was genau hast du vor?

Gruss
Alf

13.12.2023 - 17:26 Uhr

Hallo ill_son

Bin mir nicht sicher ob ich den Aufbau richtig verstanden habe. Ich denke es fehlen noch Informationen um die Fragen zu beantworten.

Wo werden denn die Channel-Objekte (??) gehalten und wo werden sie erzeugt? Im Repository?

wer ist dann für den Aufruf von Dispose() zuständig?  Ich würde jetzt sagen das Repository.

Dann ja.

Sollte der Channel dann ein Disposed-Event haben?

Nur wenn es benötigt wird.

Gruss
Alf

13.12.2023 - 09:59 Uhr

Du hast ja einen Link bezüglich eines Schichtmodels gepostet. Aber ist das nicht bereits das, was MVVM macht? Auf mich macht es den Eindruck, als wäre das so. Ich kann mich da aber auch irren.

Einfache Antwort: MVVM ist im Schichtenmodel im UI-Teil einzuordnen.

(Natürlich ist das Thema komplexer, aber das spar ich mir hier mal.)

12.12.2023 - 14:11 Uhr

Hallo StayCalm

Zitat von StayCalm

Die dritter GroupBox "Combine" hat einen Button und ein Label. Wenn man in der dritten GroupBox den Button „Combine“ klickt, soll im Label der String aus der Person-TextBox mit dem String des Animal-TextBox zusammengeführt werden.

Wenn es sich bei dem Zusammenführen von Text um komplexe Geschäftslogik handelt, dann sollte sich diese in der entsprechenden Schicht befinden:
https://mycsharp.de/forum/threads/111860/artikel-drei-schichten-architektur

Dein CombineViewModel bekommt dann ein ICommand-Property, dass vom MainViewModel aus gesetzt werden kann, und vom Button ausgelöst wird.

// So ähnlich könnte das aussehen. Habe es ohne Testen runtergeschrieben.
public MainViewModel(CombinationService combinationService)
{
  this.combineViewModel = new CombineViewModel
  {
    CombineCommand = new RelayCommand<string>(() => this.CombinationResult = combinationService.Combine(this.personViewModel.Name, this.animalViewModel.type));
  };
}

Gruss
Alf

12.12.2023 - 13:03 Uhr

Hallo JanBauer

CameraCaptureUI tut wohl nicht mit WinForms, man soll wohl MediaCapture verwenden:
https://learn.microsoft.com/en-us/uwp/api/windows.media.capture.mediacapture?view=winrt-22621

Da gibt es auch ein Beispiel.

Gruss
Alf

07.12.2023 - 09:50 Uhr

Hö? Was machst du? Du brauchst nur ein Grid. Und warum ist da immer noch md und sm?

https://mudblazor.com/features/breakpoints#breakpoints

07.12.2023 - 08:46 Uhr

Hallo Bronstein

Du hast die Breite so konfiguriert: md="4"; mach das mal weg.

Gruss
Alf

22.11.2023 - 07:49 Uhr

Hallo pollito

Es gibt fertige Komponenten zu kaufen, die das können.

Alternativ in Pdf oder Html konvertieren und anzeigen.

Gruss
Alf

20.11.2023 - 12:27 Uhr

Hallo joerg55

Das ist nicht ohne weiteres möglich.

Man kann zwar CheckedListBox überschreiben:

public class CheckedListBoxWithCustomHeight : CheckedListBox
{
    private int itemHeight = 40;
    [Browsable(true)]
    [Category("Behavior")]
    public override int ItemHeight { get => this.itemHeight; set => this.itemHeight = value; }
}

Aber die Schrift selbst wird nicht verschoben.

17.11.2023 - 09:28 Uhr

Schwer zu sagen, ohne den relevanten Code zu kennen.

Grundsätzlich werden die Daten ins ViewModel geladen und nicht direkt auf der View geändert. Das ViewModel hat ICommand-Properties, die an die Buttons in der View gebunden sind (https://www.c-sharpcorner.com/UploadFile/851045/command-design-pattern-in-C-Sharp/).

Wird der Command ausgelöst, wird eine Methode im ViewModel ausgeführt, die die Daten lädt. Mit CommunityToolkit.Mvvm wird die entsprechende Methode einfach mit [RelayCommand] attributiert. Das Command-Property wird generiert und nicht händisch implementiert.

Das ViewModel informiert dann die View über das INotifyPropertyChanged-Event. Mit CommunityToolkit wird das Property mit [ObservableProperty] attributiert, anstatt das NotifyPropertyChanged-Event händisch auszulösen.

Beispiel:

    internal partial class MainViewModel : ObservableRecipient
    {
        [ObservableProperty]
        private ObservableCollection<string>? data;

        [RelayCommand]
        void LoadData()
        {
            Data = new ObservableCollection<string>
            {
                "Data A",
                "Data B",
                "Data C"
            };
        }
    }
    <StackPanel>
        <ListView ItemsSource="{Binding Data}" />
        <Button Content="Load Data" Command="{Binding LoadDataCommand}" />
    </StackPanel>

Benutzt du denn ein ViewModel pro Page oder eines für alle?

16.11.2023 - 10:05 Uhr

Möglicherweise der Virenscanner auf dem anderen Rechner.

16.11.2023 - 10:00 Uhr

Weil es hier noch keine Antwort gibt:
Wenn die Daten programatisch geändert werden, soll das nicht im DataGrid gemacht werden, sondern an dem gebunden Property. Das muss natürlich entsprechend mit INotifyPropertyChanged ausgestattet sein.

14.11.2023 - 10:50 Uhr

Hallo TomSchmitz

Ich glaube nicht dass es eine Library gibt, die das Verschmelzen automatisch macht.

Wirst also mit der passenden Library Header und Footer auslesen und neues Pdf generieren müssen.

Über die Forensuche findest du die Libraries, die du verwenden kannst.

Gruss
Alf

11.11.2023 - 18:32 Uhr

Hallo Timm

CanExecute kann also true, false und Null sein.

CanExecure ist eine Art Methode, die true oder false zurück gibt. Kann bei deiner ICommand-Implementierung aber auch null sein, dann wird also keine Methode übergeben und CanExecute gibt dann immer true zurück.

     <Button x:Name="btn_speichern" Content="Datensatz speichern" HorizontalAlignment="Left" Margin="25,350,0,0" VerticalAlignment="Top" Height="48" 		 Width="150" />

     <DataGrid x:Name="DataGrid1"  Margin="25,100,25,100" AlternatingRowBackground="Aqua" AutoGenerateColumns="false" VerticalGridLinesBrush="#FFF8F4F4" HorizontalGridLinesBrush="#FFFAF7F7">
     ...
     </DataGrid>
     ...
     <Button x:Name="Btn_OK" Content="OK" HorizontalAlignment="Left" Margin="325,65,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.1,-0.564" Width="124"  />

Im Xaml-Code fehlen die Bindings. Beim Button das Command-Binding und beim DataGrid das ItemsSource-Binding.

    // Deine Verson mit new (); ist erst ab Sprachversion C#9 oder höher verfügbar
    // Ich nutze die Community Version Visual Studio 2022; keine ausstehenden Updates; ist die Sprachversion bei mir verfügbar?
    

Die C#-Version hängt mit der .NET-Version zusammen, die im Projekt eingestellt ist.

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version

         //Button wurde gedrückt DelegateCommand wird ausgeführt; Warum muss ein neues Object vm erzeugt werden???
         this.LoadCommand = new DelegateCommand((vm) => LoadData(vm));

'vm' ist in diesem Fall das ViewModel, dass vom Command (per CommandParameter-Binding) übergeben wird. Es wird also kein neues Objekt erzeugt.

Das beantwortet auch die Frage:

         //Welche Daten soll das object boxedMainViewModel aufnehmen
     private void LoadAllMitarbeiter()
     {
         this.AllMitarbeiter.Clear();

         foreach (var mitarbeiter in this.mitarbeiterService.GetAll())
         {
             this.AllMitarbeiter.Add(mitarbeiter);
         }
     }
     
    //Den Part verstehe ich nicht, wozu dient der Mitarbeiterservice?

     public MitarbeiterOverviewViewModel(MitarbeiterService mitarbeiterService)
     {
        this.mitarbeiterService = mitarbeiterService;

Der MitarbeiterService stellt die Verbindung zur Geschäftslogik her, von dort kommen die Mitarbeiter-Daten und da gehen sie wieder hin, wenn sie gespeichert werden sollen. Alles was mit Datenbank zu tun hat, wird dahinter versteckt und hat in der UI nichts mehr zu suchen. 
Schau dir dafür das hier an: https://mycsharp.de/forum/threads/111860/artikel-drei-schichten-architektur

        SqlConnection connection = new SqlConnection("Data Source=....;initial Catalog = Company_Test;Integrated Security=True");
        connection.Open();

         /// Die Datenbankverbindung stelle ich zukünftig über die neue Klasse ADO.Net Entity Data Model her??? Aber warum brauche ich dann noch die 		
         /// SQL connection???

Kein Plan wo die Daten aktuell herkommen. Für die UI ist das auch nicht wichtig. Wie ich schon geschrieben hatte, muss der Datenbank-Kram aus der UI raus und hinter den MitarbeiterService.

Mein MainWindow (Model) sieht zurzeit wie folgt aus:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }           
    }

Wo wird das ViewModel dem DataSource vom Window zugewiesen?

Nach meinem derzeitigen Verständnis wird das Button-Click Event durch ein Command-Binding ersetzt. check
Dazu wird der OK-Button per Datenbindung mit dem ViewModel verbunden. check
Das ViewModel enthält die Logik, was mit dem View passieren soll wenn z.B. ein Button geklickt wird, eine Änderung etc. erfolgt. check

Wird nun der OK-Button geklickt erzeugt es einen neuen DelegateCommand, dieser wiederum ruft die Methode LoadData auf usw..

Der DelegateCommand wird schon im Konstuktor vom ViewModel erzeugt und enthält eine Verknüpfung auf die LoadData-Methode.

Ich hoffe, dass es bis hierhin richtig verstanden und wiedergegeben habe. Ich glaube es wäre für mich sehr hilfreich wenn wir Schritt für Schritt vorgehen.

Ich habe die letzten Tage soviel gelesen, dass ich den Wald vor lauter Bäume nicht mehr sehe.

Es wäre ja langweilig, wenn es einfach wäre. 😃

09.11.2023 - 10:56 Uhr

Hallo Timm

Das MainViewModel braucht noch ein Property für die Mitarbeiter-Liste:

public ObservableCollection<MitarbeiterViewModel> AllMitarbeiter { get; } = new();

Im View wird das DataGrid daran gebunden:

<DataGrid ItemsSource="{Binding AllMitarbeiter}" />

Ausserdem wird der Load-Button an den DelegateCommand gebunden:

<Button Content="Load Mitarbeiter" Command="{Binding LoadCommand}" CommandParameter="{Binding}" />

Der CommandParameter ist der DataContext, also das MainViewModel.

Des Weiteren weiß ich nicht wie ich den Konstruktor des DelegateCommand füllen muss, damit dieser dann das Auslesen aus der Datenbank durchführt.

Der execute-Parameter beim DelegateCommand ist die Methode, die beim Button-Click ausgeführt wird.

public MainViewModel()
{
    // Load_data → LoadCommand
    this.LoadCommand = new DelegateCommand((vm) => LoadData(vm));
}

...

private void LoadData(object boxedMainViewModel)
{
    var mainViewModel = boxedMainViewModel as MainViewModel;
    mainViewModel.LoadAllMitarbeiter();
}

...

private void LoadAllMitarbeiter()
{
    this.AllMitarbeiter.Clear();

    foreach (var mitarbeiter in this.mitarbeiterService.GetAll())
    {
        this.AllMitarbeiter.Add(mitarbeiter);
    }
}

In  this.mitarbeiterService.GetAll()werden die Daten aus der Datenbank geladen.

Zum Testen reicht da auch sowas:

internal IEnumerable<Mitarbeiter> GetAll()
{
    yield return new Mitarbeiter("Guybrush", "Threepwood", "Monkey Island");
    yield return new Mitarbeiter("Elaine", "Marley", "Melee Island");
}

Beim DelegateCommand ist mir nicht klar warum als parameter "null" übermittelt wird. Nach meinem Verständnis kann ein Button doch nur geklickt also True oder nicht geklickt False liefern.

Der canExecute-Parameter zeigt nicht an, ob der Button gedrückt wurde, sondern ob er gedrückt werden darf. Wenn nein, dann wird er in der UI deaktiviert angezeigt. Den kannst du erstmal ignorieren.

Auch ist mir nicht ganz klar, wo der nachfolgende Programmcode hingehört:

    SqlConnection connection = new SqlConnection("Data Source=TIMM\\TESTSQL;initial Catalog = Company_Test;Integrated Security=True");
    connection.Open();

In den MitarbeiterService, bzw. noch besser in das MitarbeiterRepository, dass vom MitarbeiterService benutzt wird (https://dotnettutorials.net/lesson/repository-design-pattern-csharp/).

Der Rest kann weg.

Habe mir anscheinend ein komplexes Thema ausgesucht, für meine ersten Gehversuche im Bereich MVVM Pattern.

Ja, der Einstieg ist nicht ganz einfach. Die Mühe lohnt sich aber, zum einen für stabilere Programme, zum anderen entwickelt es das eigene Programmier-Verständnis ungemein.

Gruss
Alf

PS: Es folgt noch ein zweiter Beitrag mit ein paar Anmerkungen.

08.11.2023 - 14:32 Uhr

Ja, diese Anforderung wird mit MVVM quasi miterledigt.

Es gibt mehrere Möglichkeiten das zu machen. Hier mal ein ganz einfaches Beispiel:

<ListView ItemsSource="{Binding AllMitarbeiter}">
    <ListView.ItemTemplate>
        <DataTemplate DataType="local:Mitarbeiter">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Vorname}" Width="100" />
                <TextBlock Text="{Binding Nachname}" Width="100" />
                <TextBlock Text="{Binding Geburtsort}" Width="100" />
                <Button Content="Details" Width="60" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
08.11.2023 - 09:42 Uhr

Hallo CSharpNewbie2022

Wie du schon richtig erkannt hast, ist es wichtig, Daten und UI voneinander zu trennen. Bei WPF gibt es dafür das MVVM-Pattern.

https://mycsharp.de/forum/threads/118261/artikel-mvvm-und-databinding

https://www.c-sharpcorner.com/UploadFile/ptmujeeb/wpf-mvvm-pattern-a-simple-tutorial-for-absolute-beginners/

Ausserdem empfehle ich dir noch diesen Artikel:
https://mycsharp.de/forum/threads/111860/artikel-drei-schichten-architektur

Gruss
Alf

01.11.2023 - 10:09 Uhr

Guten Morgen

Ich nehme mal an, dass auf Knopfdruck gespeichert werden soll.

Statt dem ClickEvent kannst du das Command-Pattern verwenden.

Dafür bindest du ein Command an dein ViewModel:

<Button Content="Mitarbeiter speichern" Command="{Binding SaveCommand}" CommandParameter="{Binding AllMitarbeiter}" />
public RelayCommand<ObservableCollection<MitarbeiterViewModel>> SaveCommand { get; private set; }

Im Konstruktor vom ViewModel kann der Command instantiiert werden:

public MitarbeiterOverviewViewModel(MitarbeiterService mitarbeiterService)
{
    this.mitarbeiterService = mitarbeiterService;
    SaveCommand = new RelayCommand<ObservableCollection<MitarbeiterViewModel>>(SaveAll, CanSaveAll);
}

Eine Implementierung für RelayCommand gibt es hier: https://www.c-sharpcorner.com/UploadFile/20c06b/icommand-and-relaycommand-in-wpf/

Die Methode SaveAll, die vom Command aufgerufen wird, bekommt als Parameter die Liste mit den Mitarbeitern.

private void SaveAll(ObservableCollection<MitarbeiterViewModel>? allMitarbeiterViewModel)
{
    if (allMitarbeiterViewModel == null) return;
    var allMitarbeiter = allMitarbeiterViewModel.Select(ToMitarbeiter);
    mitarbeiterService.SaveAll(allMitarbeiter);
}

Beachte hier, dass ich in meinem Beispiel-Projekt nicht das Model Mitarbeiter direkt im UI verwende, sondern ein MitarbeiterViewModel. Beim Landen und Speichern wird jeweils übersetzt. (Beachte, dass deine ViewModels INotifyPropertyChanged implementieren.)

private Mitarbeiter ToMitarbeiter(MitarbeiterViewModel viewModel)
{
    return new Mitarbeiter(viewModel.Vorname, viewModel.Nachname, viewModel.Geburtsort);
}

Der MitarbeiterService hält ein MitarbeiterRepository, über das die Kommunikation mit der Datenbank stattfindet.
Statt über den MitarbeiterService kann das MitarbeiterRepository auch direkt im ViewModel verwendet werden.

Wichtig ist, dass ab hier die UI von der Datenbank und Logik-Schicht getrennt ist: https://mycsharp.de/forum/threads/111860/artikel-drei-schichten-architektur

Den UI-Kram kannst du schonmal ausprobieren. Ziel ist, dass auf Knopfdruck SaveAll mit deinen Mitarbeitern aufgerufen wird. Meine Implementierung im MitarbeiterService:

internal void SaveAll(IEnumerable<Mitarbeiter> allMitarbeiter)
{
    foreach (var mitarbeiter in allMitarbeiter)
        Debug.WriteLine(mitarbeiter);
}

Wenn das funktioniert, kann das Repository implementiert werden. Bei konkreten Fragen meldest du dich wieder hier.

Gruss
Alf