Laden...

C# WPF .Net 4 Datagrid Probleme beim Speichern per Button

Erstellt von dark-cms vor 13 Jahren Letzter Beitrag vor 13 Jahren 5.385 Views
D
dark-cms Themenstarter:in
6 Beiträge seit 2011
vor 13 Jahren
C# WPF .Net 4 Datagrid Probleme beim Speichern per Button

Hallo Forenuser,

dies ist mein erster Eintrag hier und ich hoffe auf gute Hilfe bei meinem Problem:

ich habe ein Anwendung, bei der aus einer Datenbank Bestellungen ausgelesen werden die einen bestimmten externen Schlüssel haben.

Das ganze wird entsprechend an eine Datagrid gebunden.

So sieht das XAML aus

<UserControl x:Class="JTL_Amazon.J2A_Grid"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
       mc:Ignorable="d" 
       d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
      <DataGrid AutoGenerateColumns="False" Height="Auto" Name="dataGrid1" VerticalAlignment="Top">
        <DataGrid.Columns x:Uid="toprow">
          <DataGridTextColumn Header="Kunde" IsReadOnly="True" />
          <DataGridTextColumn Header="Bestellnummer" IsReadOnly="True"/>
          <DataGridTextColumn Header="Lieferdatum" IsReadOnly="True"/>
          <DataGridTextColumn Header="Logistiker" IsReadOnly="True"/>
          <DataGridTextColumn Header="TrackingID" IsReadOnly="True" />
          <DataGridTextColumn Header="Erstellt am" IsReadOnly="True" />
          <DataGridCheckBoxColumn Header="Exportieren" />
        </DataGrid.Columns>
      </DataGrid>
    </ScrollViewer>
  </Grid>
</UserControl>

und so der bereich wo die daten eingebunden werden:

int counter = 0;
((DataGridTextColumn)JTL2Amazon_Grid.dataGrid1.Columns[0]).Binding = new Binding("Kunde");
((DataGridTextColumn)JTL2Amazon_Grid.dataGrid1.Columns[1]).Binding = new Binding("AmazonID");
((DataGridTextColumn)JTL2Amazon_Grid.dataGrid1.Columns[2]).Binding = new Binding("VersandDatum");
((DataGridTextColumn)JTL2Amazon_Grid.dataGrid1.Columns[3]).Binding = new Binding("Logistiker");
((DataGridTextColumn)JTL2Amazon_Grid.dataGrid1.Columns[4]).Binding = new Binding("TrackingID");
((DataGridTextColumn)JTL2Amazon_Grid.dataGrid1.Columns[5]).Binding = new Binding("Erstellt");
((DataGridCheckBoxColumn)JTL2Amazon_Grid.dataGrid1.Columns[6]).Binding = new Binding("Versenden") { NotifyOnSourceUpdated = true, NotifyOnTargetUpdated = true };
gridDataList = new ObservableCollection<order_details>();
while (dr.Read())
{
 counter++;
 string kunde = String.Format("{0} {1}", dr["cVorname"], dr["cName"]);
 string bestellung = dr["cInetBestellNr"].ToString();
 string lieferdatum = dr["dLieferdatum"].ToString();
 string logistik = dr["cname"].ToString();
 string dr1 = dr["cIdentCode"].ToString();
 string dr2 = dr["dErstellt"].ToString();
 gridDataList.Add(new order_details(kunde, bestellung, lieferdatum, logistik, dr1, dr2));
}
JTL2Amazon_Grid.dataGrid1.ItemsSource = gridDataList;

soweit funktioniert das ganze auch, man erhält eine saubere liste, wo am ende eine chackbox ist, die man auch auswählen kann.

Das speichern soll hier über einen anderen button laufen der sich im hauptfenster befindet, das funktioniert aber nur indirekt, den wenn man als benutzer eine checkbox auswählt und anhakt, dann direkt auf speichern geht, dann wird diese checkbox als nicht angewählt gesehen.

erst der zweite klick bringt den richtigen wert.

ich such schon den ganzen abend nach einer lösung aber irgendwie finden tu ich dazu nichts.

die klasse die für den aufbau der collection da ist hab ich bereits per interface IEditableObject erweitert und dort per console den status der bearbeitung ausgegeben. und dort wird wie folgt gearbeitet

vorgehensweise bei Bestätigung der entertaste mit Enter

klick auf die checkbox
-BeginEdit
wert Ändern
Enter drücken
-EndEdit

Würde ich jetzt auf Enter klicken würde alles passen (das kann aber so nicht bleiben, da ich mit dem DAU rechnen muss)

da kann das ganze nämlich so geschehen, das nach dem er die checkbox auswählt direkt auf senden geht. und da fehlt dann der schritt der die Bestätigung des wertes , bzw wird das erst nach dem Clickevent ausgeführt.

für mich persönlcih wäre es am schönsten wenn auch der doppelklcik bei einem druck auf eine checkbox weg wäre und man direkt die checkbox ändern könnte ohne vorher das feld zu aktivieren.

ich hoffe ihr könnt helfen

1.044 Beiträge seit 2008
vor 13 Jahren

Hallo dark-cms,

so wie du es machst, ist es falsch. In WPF gibt es sog. Bindings. Du setzt die Bindings im Code. Das ist wirklich die schlechteste Idee! Davon kann ich dir nur abraten! Befasse dich lieber mit den Grundlagen des Data-Bindings. Hier findest du einen guten Einstieg.

Wie ich sehe, arbeitest du direkt mit den Daten. Auch das ist eine schlechte Idee. Erstell dir Klassen, die eine Tabelle repräsentieren sollen. In den Klassen schreibst du deine Validierungs-Regeln und Sachen wie z.B. INotifyPropertyChanged. Stichwort: O/R-Mapper, Model.

zero_x

5.299 Beiträge seit 2008
vor 13 Jahren

jo, zustimm. Ich hab sogar gebastelt, wies evtl. aussehen könnte:

<UserControl x:Class="JTL_Amazon.J2A_Grid"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
       mc:Ignorable="d"
       d:DesignHeight="300" d:DesignWidth="300"
    xmlns:dm="clr-namespace:MyProgram.DataModel" 
  DataContext="{x:Type dm:Orderlist}" >
  <Grid>
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
      <DataGrid AutoGenerateColumns="False" Height="Auto" Name="dataGrid1" ItemsSource="{Binding}">
        <DataGrid.Columns x:Uid="toprow">
          <DataGridTextColumn Header="Kunde" IsReadOnly="True" Binding="{Binding Path= Kunde }" />
          <DataGridTextColumn Header="Bestellnummer" IsReadOnly="True" Binding="{Binding Path= Bestellnummer }"/>
          <DataGridTextColumn Header="Lieferdatum" IsReadOnly="True" Binding="{Binding Path= Lieferdatum }"/>
          <DataGridTextColumn Header="Logistiker" IsReadOnly="True" Binding="{Binding Path= Logistiker }"/>
          <DataGridTextColumn Header="TrackingID" IsReadOnly="True"  Binding="{Binding Path= TrackingID }"/>
          <DataGridTextColumn Header="Erstellt am" IsReadOnly="True" Binding="{Binding Path= Erstellt }"/>
          <DataGridCheckBoxColumn Header="Exportieren"  Binding="{Binding Path= Exportieren }"/>
        </DataGrid.Columns>
      </DataGrid>
    </ScrollViewer>
  </Grid>
</UserControl>

dA musste halt den DataContext dieses UserControls auf eine geeignete Auflistung von Orders setzen, hier wird angenommen, MyProgram.DataModel.Orderlist sei ein geeigneter Datentyp.

Der frühe Apfel fängt den Wurm.

D
dark-cms Themenstarter:in
6 Beiträge seit 2011
vor 13 Jahren

das binding hatte ich zwischenzeitlich schon geändert.

die daten werden wie folgt verarbeitet

gridDataList = new ObservableCollection<order_details>();
diese ist allgemein verfügbar wird aber im code neu aufgebaut

order details ist folgende Klasse

    class order_details : INotifyPropertyChanged 
    {
        public order_details(string Kunde, string order_id, string ship_date, string shipper, string idcode, string created)
        {
            this.Kunde = Kunde;
            this.AmazonID = order_id;
            this.VersandDatum = ship_date.Replace(" 00:00:00", "");
            this.Logistiker = shipper;
            this.TrackingID = idcode;
            this.Erstellt = created;
        }
        public event PropertyChangedEventHandler PropertyChanged; 
        private bool _Versenden;
        public string Kunde { get; set; }
        public string AmazonID { get; set; }
        public string VersandDatum { get; set; }
        public string Logistiker { get; set; }
        public string TrackingID { get; set; }
        public string Erstellt { get; set; }
        public bool Versenden { get {return _Versenden;} 
            set 
            {
                if(_Versenden == value)
                {
                    return;
                }
                _Versenden = value;
                OnPropertyChanged("Versenden");

            } 
        }
        public bool changed = false;
        protected void OnPropertyChanged(string propertyName)  
        {  
            PropertyChangedEventHandler handler = PropertyChanged;  
            if (handler != null)  
            {  
                handler(this, new PropertyChangedEventArgs(propertyName));  
            }  
        }  

    }

entsprechend hab ich eine sammlung von bestellungen die dann an das datagrid angehangen werden.

das gescheiht im grunde nach einer datumsauswahl direkt aus der datenbank heraus -> das beispiel im ersten post , oder per drag n drop aus einer xml datei heraus.

Ich hab leider das problem das ich aus dem php-bereich komme und da ist diese Klassengeschichte leider nicht so komplex.

unabhängig von strukturellen fehlern sehe ich da aber noch nichts, was mir erklärt wieso , bzw warum dieser fehler auftaucht.

könnte mir das evtl einer erklären?

1.044 Beiträge seit 2008
vor 13 Jahren

Hallo dark-cms,

die Klasse entspricht nicht den Naming Guidelines. order_details implementiert zwar von INotifyPropertyChanged, ist aber immer noch nicht richtig umgesetzt. Im Setter aller Properties bzw. an die Property, an denen gebunden wird, muss _OnPropertyChanged _aufgerufen werden. Der Grund ist, dass WPF dann nicht mitbekommt, ob sich ein Wert einer Property verändert hat oder nicht.

Verstehe ich das richtig, dass du Daten aus einer XML-Datei anzeigen möchtest? Du hast die Möglichkeit in WPF direkt an eine XML-Datei bzw. an den Inhalt zu binden.

zero_x

D
dark-cms Themenstarter:in
6 Beiträge seit 2011
vor 13 Jahren

aso also muss ich entsprechend alle set {} erweitern

sodass statt

{get; set;}

zu


{
  get {return _Versenden;} 
  set 
  {
    if(_Versenden == value)
    {
      return;
    }
    _Versenden = value;
    OnPropertyChanged("Versenden");
  } 
}

wird, hab ich das entsprechend richtig verstanden?

das mit der xml ist im grunde eine ähnliche geschichte
ich hab ein zweites programm, da kann ich bestellungen markieren und als xml exportieren, aus dieser xml brauch ich aber auch nur diese x werte und unabhänig davon wird dort die checkbox automatisch vorbelegt mit true.

da ich ja davon ausgehen kann, das jemand sich entsprechend schon die mühe gemacht hat nur die bestellungen auszuwählen die er auch entsprechend versenden will.

1.044 Beiträge seit 2008
vor 13 Jahren

Hallo dark-cms,

so ist es richtig.

zero_x

D
dark-cms Themenstarter:in
6 Beiträge seit 2011
vor 13 Jahren

ne ist es immer noch nicht.

ich denke der fehler ist eher was anderes

ich hatte das schon bei dem grid von devexpress.

wenn ich einen klick auf die checkbox mache ist diese zelle im editmodus.

dann kann ich mit einem einzelnen klick an oder ausschalten.

druck ich dann einen button direkt nach bearbeiten also das feld ist noch aktiv, dann wird diese änderung erst nach dem klick übernommen.

mal davon ab das eure hilfe den code jetzt verbessert hat, fehlt da immernoch der schritt zwischen dem klick und dem beenden das editmodus.

ich glaub wenn man ein grid anlegt mit text kann man das besser verfolgen.
mir scheint das so, das diese feld was grade bearbeitet wird erst dann geschlossen wird wenn die ereignisse des clicks schon längst erledigt sind.

gibt es da nicht irgendeinen commit befehl der das evtl erledigt, sodas das feld das gradbearbeitet wird seinen focus verliert und die bearbeitung gespeichert wird?

5.299 Beiträge seit 2008
vor 13 Jahren

Die Datentypen sind noch tw falsch. Für ein Datum ("Erstellt") unbedingt DateTime nehmen. Eine ID ist meist int, fast nie string.
Ich kann mir auch kaum vorstellen, dass Kunde und Logistiker Strings sind. Das sollten eigentlich komplexe Datensätze sein, mit mehreren Properties, auf die verwiesen wird - also IDs.

Der frühe Apfel fängt den Wurm.

D
dark-cms Themenstarter:in
6 Beiträge seit 2011
vor 13 Jahren

bestellnummer ist eine xxx-xxxx-xxxx-xxx (nur zahlen aber mit trennzeichen)
lieferdatum kann auch leer sein aber das kann ich ja so noch abfangen
logistiker ist der name des unternehmens Trackingid ist abhängig vom unternehmen auch ein string.

lediglich das erstelltdatum ist definitiv immer ein datum

ich weisse nochmal drauf hin lediglich die checkbox wird in dem fall bearbeitet.

beim speichern werden nur die datensätze in eine txt datei geschrieben wo die checkbox aktiv ist.

die art des datentypes ist in diesem fall egal, da ich das ganze auch in einen string klatschen könnte und am ende würde bei klick auf den speichern button der letzte wert nicht stimmen und ich bin der meinung bool für eine checkbox ist auch richtig.

diese grid ist nur dafür da das man aus einer masse von daten (egal wie sie darggestellt werden), die datensätze auswählen kann die dann auch gedruckt werden.

Edit--
testweise ihab ich eine trackingid die da heist rizzel
W 123 456 7890 DE ist die trackingid der Post express value packs

5.299 Beiträge seit 2008
vor 13 Jahren

wenn ich einen klick auf die checkbox mache ist diese zelle im editmodus.

dann kann ich mit einem einzelnen klick an oder ausschalten.

druck ich dann einen button direkt nach bearbeiten also das feld ist noch aktiv, dann wird diese änderung erst nach dem klick übernommen.

Vlt. kannst du am Binding was drehen, UpdateSourceTrigger.OnPropertyChanged oder sowas?

Ansonsten hast du natürlich recht: Wenn die Datenbanken, oder wo die Daten herkommen nix anderes anliefern als Strings, und du kannst das nicht ändern, kannst dus eben nicht ändern. Und wenn diese Interaktion die einzige ist, die du mit dem Kram zu tun hast, ist das sogar - naja, nicht schön - aber verschmerzbar.
Üblicherweise haben solche Interaktionen ja die starke Tendenz sich auszuweiten, und dann rächen sich schlampig gefasste Deklarationen ("alles string") recht schnell.

Der frühe Apfel fängt den Wurm.

D
dark-cms Themenstarter:in
6 Beiträge seit 2011
vor 13 Jahren

ich hab die lösung gefunden

beim klick auf den button folgendes ausführen

JTL2Amazon_Grid.dataGrid1.CommitEdit(DataGridEditingUnit.Row, true);

und schon gehts

seltsam den ich hatte ehemals schon
JTL2Amazon_Grid.dataGrid1.CommitEdit();

verwendet und das hat nicht reagiert, und funktioniert im vergleich auch nicht

auf der anderen seite hab ich noch folgenden tip erhalten:
folgendes als style im xaml

        <Style TargetType="{x:Type dg:DataGridCell}">
          <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
        </Style>

und das im code

 private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DataGridCell cell = sender as DataGridCell;
            if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
            {
                if (!cell.IsFocused)
                {
                    cell.Focus();
                }
                DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
                if (dataGrid != null)
                {
                    if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
                    {
                        if (!cell.IsSelected)
                            cell.IsSelected = true;
                    }
                    else
                    {
                        DataGridRow row = FindVisualParent<DataGridRow>(cell);
                        if (row != null && !row.IsSelected)
                        {
                            row.IsSelected = true;
                        }
                    }
                }
            }
        }    

        static T FindVisualParent<T>(UIElement element) where T : UIElement
        {
            UIElement parent = element;
            while (parent != null)
            {
                T correctlyTyped = parent as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }

                parent = VisualTreeHelper.GetParent(parent) as UIElement;
            }
            return null;
        } 

das macht die checkbox zu einer einzelklicklösung

ich dank euch vielmals für die hilfe und @ErfinderDesRades ausweiten wird sich da nix, ich persönlich als php-programmier muss sogar sagen das ich diese strikte struktur eigentlich ziemlich super finde. entsprechend versuch ich auch dieses entsprechen zu verwenden.