Laden...

DataGrid - ohne manuelle Eingabemöglichkeit?

Erstellt von GeneVorph vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.447 Views
G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 5 Jahren
DataGrid - ohne manuelle Eingabemöglichkeit?

Hallo,

ich versuche mich derzeit am Umstieg von .Forms auf WPF und bin auf eine Schwierigkeit gestoßen, die ich trotz Recherche nicht klären kann:

das mir von Windows.Forms vertraute DGV (DataGridView) erlaubt die manuelle Eingabe/Manipulation von Daten - d.h. man kann einfach während der Runtime in eine Zelle klicken und Einträge hinzufügen, bzw. ändern.

Eine ähnliche Funktionalität konnte ich beim DataGrid unter WPF nicht finden. Ist das dort evtl. nicht vorgesehen? Alles, was ich bislang zusammentragen konnte, lässt mich lediglich darauf schließen, dass ich mehr als bisher in "Bindings" denken muss und streng die MVVM beachten muss.

Ich möchte das Problem erstmal ohne Einbezug einer tatsächlichen Datenbank angehen (WPF ist so schon verwirrend genug) und mit dem DataGrid erst ein wenig experimentieren.
Ich habe programmatisch 3 Spalten erstellt und würde nun gerne wissen, ob es
a) eine Möglichkeit gibt, sie zur Laufzeit zu befüllen (s. DGV) und
b) wie ich programmatisch einen Datensatz hinzufügen kann.

Vielen Dank,
beste Grüße
Vorph

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 5 Jahren

... but beware of CanUserAddRows and CanUserDeleteRows as they can appear a little magical.

Also, irgenwie ist das trotzdem nicht verständlich für mich - ich habe folgenden Code:


<Window x:Class="LearnGridViewWPF.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LearnGridViewWPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    
<Grid>
         
       <DataGrid IsReadOnly="False" IsEnabled="True"  AutoGenerateColumns="False" HorizontalAlignment="Left" Height="265" Margin="85,85,0,0" VerticalAlignment="Top" Width="590" CanUserAddRows="True">
           
            <DataGrid.Columns>
                <DataGridTextColumn Header="Spalte 1" Width="*"/>
                <DataGridTextColumn Header="Spalte 2" Width="*"/>
            </DataGrid.Columns>
        </DataGrid>

    </Grid>
</Window>

Offensichtlich reicht es nicht, einfach CanUserAddRows auf "true" zu setzen; daraus konnte man ja aus deem Artikel schließen.

Was ich insbesondere nicht verstehe: IEditableCollectionView.CanAddNew und IEditableCollectionView.CanRemove --> müssen diese programmatisch noch gesetzt werden? Wenn ja: wo? Sowohl im C#-Code, als auch in der XAML konnte ich nichts finden, zumindest nicht mit Hilfe von InteliSense (ohne InteliSense währe ich beim Programmieren eh aufgeschmissen, aber das ist ein anderes Thema...).

Wo liegt mein Denkfehler oder was übersehe ich?

L
136 Beiträge seit 2015
vor 5 Jahren

Hallo GeneVorph,

Dir fehlen alle Bindings...musst ja auch an Dein ViewModel binden 😁
-> Und demzufolge auch die Logik (z.B. ViewModel) und die dazu nötigen Methoden wie "AddMyNewUser"

Im Anhang mal ein MVVM-Beispielprojekt welches ich gut finde und auch mit ObservableCollection<> arbeitet.
-> Ist zwar mit ListView gelöst, kannst aber relativ einfach in Dein DataGrid, DataGridView ändern (View austauschen)

Gruss Lhyn

16.828 Beiträge seit 2008
vor 5 Jahren

GeneVorph, auch in WinForms hätte man schon mit Bindings arbeiten sollen - aber das machen leider die wenigsten 😉
Daher wirkt das alles erst mal als hohe Hürde. Wenn Du das Prinzip von Bindings dann aber verstanden hast - und die wirklich harte Trennung von UI und Daten, wie es sich gehört, dann läuft das 😉

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 5 Jahren

Danke für eure Antworten, Leute 😃

Ich werde mir das Beispielprojekt mal zu Gemüte führen - ich hatte ja schon erwähnt, dass ich zu wenig in Bindings denke.

Da gebe ich Abt völlig Recht - auch bei den Forms wäre das schon Pflicht gewesen...aber da konnte man sich rummogeln, auch wenn man es nicht ganz verstanden hatte. Nicht, dass das jemand falsch versteht: ich spreche mich vehement gegen das Herummogeln aus!

Ohne jetzt schon in den Beitrag geschnuppert zu haben eine letzte Frage, bezogen auf meinen letzten Post

Ich möchte das Problem erstmal ohne Einbezug einer tatsächlichen Datenbank angehen

Mal jetzt blöd gefragt: wenn ich rein die Funktionalität "testen" möchte und einfach ein paar Daten händisch in ein DataGrid binden möchte - woran binde ich da tatsächlich? Die User-Eingabe? Oder funktioniert das zwingend nur mit Datenbank oder programmatisch durch Objekt-Deklaration und...neee, kann ja nicht sein, sonst hätte man ja kein Property CanUserAddRows. Ich seh' schon, da werden einige Kopfschmerzen bei rumkommen.

Dennoch, nochmals vielen Dank!

W
955 Beiträge seit 2010
vor 5 Jahren

Normalerweise greift die UI auf einen Service zu der die Daten besorgt und vllt als List<Model> übergibt welches die UI dann in ein ObservableCollection<Model> kapselt. Du könntest also ein Service bauen der erst einmal zufällige Testdaten übergibt.

16.828 Beiträge seit 2008
vor 5 Jahren

Da hilft Dir eine sauberes Software Design - und Dependency Injection.

Dein (Beispiel) PersonService erhält ein IPersonRepository im Konstruktor.
Wenn Du testest, dann definierst Du im Dependency Injection Setup ein (Beispiel) PersonMockRepository (Beispiel), das Daten im lokalen Speicher mockt; wenn Du gegen eine echte Datenbank arbeitest, dann verwendest Du (Beispiel) das PersonMssqlRepository.

Das ist auch die Grundbasis für sauberes Testen.
[Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio

Letzten Endes gibt es aber viele Wege, wie man Daten mockt.

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 5 Jahren

Nur der Vollständigkeit halber: ich möchte etwaigen Hilfesuchenden hier meine Lösung für mein oben beschriebenes Problem zeigen. Ich kann keine Garantie übernehmen, dass dabei wirklich jedes Designpattern in Reinkultur angewandt wurde - ich bin selbst noch Anfänger!

Aber: so bekommt man ein DataGrid, in das man einfach Werte schreiben kann. Sozusagen die Basis, um von da aus sich an komplexere Sachen zu machen...

Code in der MainWindows.xaml

<Window x:Class="LearnGridViewWPF.MainWindow"
.
.
.
    <Grid>
        <DataGrid x:Name="ViewGrid" AutoGenerateColumns="False" Margin="0,0,0,174">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding ="{Binding ID}" Width="*"/>
                <DataGridTextColumn Header="Name" Binding ="{Binding Name}" Width="*"/>
            </DataGrid.Columns>
        </DataGrid>
        <Button x:Name="btnShowListContent" Content="Button" HorizontalAlignment="Left" Margin="160,345,0,0" VerticalAlignment="Top" Width="75" Click="btnShowListContent_Click"/>

    </Grid>
</Window>

Für das Binding habe ich eine Klasse "Person" erstellt:


 public class Person
    {
        public string ID { get; set; }
        public string Name { get; set; }
    }

Dann noch folgendes in MainWindows.xaml.cs


        public MainWindow()
        {
            InitializeComponent();

            List<Person> Persons = new List<Person>();

            ViewGrid.ItemsSource = Persons;

        }

Wird die App nun gestartet hat man ein leeres Datagrid, in dessen Zellen man strings eingeben kann. Diese werden bei Verlassen der Zellen automatisch gespeichert. Ich habe das etwas unkonventionell getestet, indem ich auf meinem MainWindow einen Button installiert habe "btnShowListContent" und die MainWindow.xaml.cs etwas manipuliert habe:

 List<Person> MasterList { get; set; }
        public MainWindow()
        {
            InitializeComponent();

            List<Person> Persons = new List<Person>();

            MasterList = Persons;

            ViewGrid.ItemsSource = Persons;

        }

        private void btnShowListContent_Click(object sender, RoutedEventArgs e)
        {
            string elementsInList = "";

            foreach (var element in MasterList)
            {
                elementsInList += element.Name + " ";
            }

            MessageBox.Show(elementsInList);
        }

Gibt man nun Einträge ein und klickt den Button, bekommt man in der MessageBox die Einträge für Name angezeigt.

Falls jemand noch weiter Überlegungen, Verbesserungsvorschläge oder wichtige Hinweise für mich hat (gerne auch auf Fehler aufmerksam machen), bedanke ich mich jetzt schon dafür!

vlg
Vorph

2.207 Beiträge seit 2011
vor 5 Jahren

Hallo GeneVorph,

ja, benutze DataBinding 😃

Setz den DataContext im "MainWindows.xaml.cs" auf eine Klasse (dein ViewModel) und binde an die Daten von dort.

Arbeite bei Buttons mit Commands (Kein Click-Event im Codebehind) und benutze den DataContext.

Gruss

Coffeebean