Laden...

Daten werden nach einen Command Button nicht im Datagrid angezeigt, MVVM Binding

Letzter Beitrag vor einem Jahr 17 Posts 710 Views
Daten werden nach einen Command Button nicht im Datagrid angezeigt, MVVM Binding

Hallo

Bin gerade dabei mich in das MVVM einzuarbeiten und habe da ein Problem.

Ich starte das Projekt und die Daten werden ganz normal in das DataGrid eingetragen. In das DataGrid möchte ich eine andere Ansicht anzeigen lassen, wenn gewollt. Die erste ansicht ist eine Artikelansicht und die zweite eine Skuansicht(ein Artikel kann mehrere Sku haben). Beide benutzen die selbe Klasse.

Wenn ich aber den SkuEbeneCommand button auslöse, wird die Skuansicht nicht angezeigt. Die Daten sind aber auch da aber werden nicht angezeigt.

Was muss ich ändern ???

XAML:

<Window.DataContext>
        <local:MainDispoViewModel />
</Window.DataContext>
    
<Grid Grid.Column="0">
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
            <Button x:Name="skuButton" Content="SKU Ebene der Artikel" Margin="15,0,5,0" Command="{Binding SkuEbeneCommand}" />
        </StackPanel>
  </Grid>    
  <Grid Grid.Column="0" >
                    <GroupBox
                            Name="GroupBoxArticle"
                            Margin="10,0,10,0"
                            Padding="4"
                            Header="{Binding HeaderGroupBox}">
                        <DataGrid 
                                x:Name="dataGrid"
                                materialDesign:DataGridAssist.CellPadding="5"
                                materialDesign:DataGridAssist.ColumnHeaderPadding="1"
                                ItemsSource="{Binding FilterCollection}"
                                AutoGenerateColumns="False"
                                Background="White"
                                FontSize="16"
                                IsReadOnly="True"
                                ScrollViewer.CanContentScroll="True"
                                ScrollViewer.HorizontalScrollBarVisibility="Visible"
                                ScrollViewer.VerticalScrollBarVisibility="Visible"
                                SelectionMode="Single"
                                SelectionUnit="FullRow" >                            
                            <DataGrid.Columns>
                                <DataGridTextColumn x:Name="articleColumn" Width="auto" Header="Artikel" Binding="{Binding mention_article_nr_1}"/>
                                <DataGridTextColumn x:Name="panEuColumn" Width="auto" Header="EU" Binding="{Binding is_paneu}"/>
                                <DataGridTextColumn x:Name="skuColumn" Width="auto" Header="Sku" Binding="{Binding sku}"/>
                                <DataGridTextColumn x:Name="bezeichnungColumn" Width="400" Header="Bezeichnung" Binding="{Binding name}"/>
                                etc..
                            </DataGrid.Columns>
                        </DataGrid>
                    </GroupBox>
                </Grid>
            </Grid> 

//MainDispoViewModel

//MainDispoViewModel
class MainDispoViewModel : BaseViewModel
    {
        AzManagerRepository azManagerRepository = new AzManagerRepository();
        private ObservableCollection<ArtikelOderSkuEbene> _artikelOderSkuEbenes = new ObservableCollection<ArtikelOderSkuEbene>();
        public ObservableCollection<ArtikelOderSkuEbene> artikelOderSkuEbenes
        {
            get => _artikelOderSkuEbenes;
            set
            {
                if (_artikelOderSkuEbenes != value)
                {
                    _artikelOderSkuEbenes = value;
                    //this.RaisePropertyChanged();
                    this.RaisePropertyChanged(nameof(_artikelOderSkuEbenes));
                }
            }
        }

        private static ICollectionView _filterCollection;
        public static ICollectionView FilterCollection
        {
            get { return _filterCollection; }
            set
            {
                _filterCollection = value;
            }
        }

        public DelegateCommand SkuEbeneCommand { get; set; }

        public MainDispoViewModel()
        {
            this.SkuEbeneCommand = new DelegateCommand(RefreshSku);

            //False für Artikelansicht
            bool artikelOderSku = false;
            artikelOderSkuEbenes = azManagerRepository.GetArtikelOderSku(artikelOderSku);
            FilterCollection = CollectionViewSource.GetDefaultView(artikelOderSkuEbenes);
            HeaderGroupBox = "Artikel (" + artikelOderSkuEbenes.Count.ToString() + ")";
        }

        //Command Action
        private void RefreshSku(Object obj)
        {
            //var d = obj as ObservableCollection<ArtikelOderSkuEbene>;
            this.artikelOderSkuEbenes.Clear();
            //True für Artikelansicht
            artikelOderSkuEbenes = azManagerRepository.GetArtikelOderSku(true);
        }

        //Filterauswahl
        private bool FilterByLagernd(object listings)
        {
            var listingDetail = listings as ArtikelOderSkuEbene;

            if(lagerndCheckBox == true || lagerndCheckBox == false)
            {
                if(lagerndCheckBox == true )
                {
                    if (listingDetail != null && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0))
                        return true;
                }
                else
                {
                    if (listingDetail != null && (listingDetail.WdpBestand >= 0 || listingDetail.CslBestand >= 0))
                        return true;
                }
            }

            return false; 
        }

        private bool FilterByFlag2(object listings)
        {
            var listingDetail = listings as ArtikelOderSkuEbene;

            if (flag2CheckBox == true || flag2CheckBox == false)
            {
                if (flag2CheckBox == true)
                {
                    if (listingDetail.flag_2 == "Y")
                        return true;
                }
                else
                {
                    if (listingDetail != null && (listingDetail.flag_2 == "Y" || listingDetail.flag_2 == "N"))
                        return true;
                }
            }

            return false;
        }

        private bool FilterByActiveOnly(object listings)
        {
            var listingDetail = listings as ArtikelOderSkuEbene;

            if (activeOnlyCheckBox == true || activeOnlyCheckBox == false)
            {
                if (activeOnlyCheckBox == true)
                {
                    if (listingDetail.ActiveCount > 0)
                        return true;
                }
                else
                {
                    if (listingDetail != null && (listingDetail.ActiveCount >= 0))
                        return true;
                }
            }

            return false;
        }

        private bool _lagerndCheckBox;
        public bool lagerndCheckBox
        {
            get { return _lagerndCheckBox; }
            set
            {
                _lagerndCheckBox = value;
                FilterCollection.Filter = FilterByLagernd;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }

        private bool _flag2CheckBox;
        public bool flag2CheckBox
        {
            get { return _flag2CheckBox; }
            set
            {
                _flag2CheckBox = value;
                FilterCollection.Filter = FilterByFlag2;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }

        private bool _activeOnlyCheckBox;
        public bool activeOnlyCheckBox
        {
            get { return _activeOnlyCheckBox; }
            set
            {
                _activeOnlyCheckBox = value;
                FilterCollection.Filter = FilterByActiveOnly;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }

        

Hallo und willkommen,

zum einen muß bei RaisePropertyChanged(...) eine (öffentliche) Eigenschaft angegeben werden (denn dessen Getter wird anschließend von der View aus aufgerufen)

this.RaisePropertyChanged(nameof(artikelOderSkuEbenes)); // statt _artikelOderSkuEbenes

und zum anderen löscht du in RefreshSku mittels

 this.artikelOderSkuEbenes.Clear();

zwar die Liste, diese wird aber mit folgendem Code sofort wieder neu zugewiesen:

artikelOderSkuEbenes = azManagerRepository.GetArtikelOderSku(true);

d.h. entweder ist die erste Zeile überflüssig (dann wird der GC die alte Liste entsorgen) oder aber du fügst die neuen Daten mittels

artikelOderSkuEbenes.AddRange(azManagerRepository.GetArtikelOderSku(true));

hinzu (aber dann muß wiederum RaisePropertyChanged(nameof(artikelOderSkuEbenes)) dafür aufgerufen werden.

Edit: Du solltest die Eigenschaft ArtikelOderSkuEbenes(also mit einem Großbuchstaben am Anfang) benennen.

Edit2: Warum ist FilterCollection als static deklariert?

Hi

Danke für die Antwort. Ich habe mit this.artikelOderSkuEbenes.Clear(); die Daten gelöscht weil beim DataGrid nichts passiert.
Die Alten Daten bleiben und die neue, sind nicht da. Habe ich jetzt so umgeändert und keine Veränderung.

AddRage gibt es bei ObservableCollection nicht. Static habe ich vergessen zu löschen.

Leider werden die neuen Daten immer noch nicht beim Datagrid angezeigt.

Kann das eventuell hieran liegen weil das Object immer null ist ?

class DelegateCommand : ICommand
    {
        //Externe Aufrufe
        readonly Action<object> execute;
        readonly Predicate<object> canExecute;

        //Aufruf Variablen werden gesetzt
        public DelegateCommand(Predicate<object> canExecute, Action<object> execute) =>
            (this.canExecute, this.execute) = (canExecute, execute);
        //Aufruf Variablen werden gesetzt
        public DelegateCommand(Action<object> execute) : this(null, execute) { }

        public event EventHandler CanExecuteChanged;
        //Event von aussen aufrufen
        public void RaiseCanExecuteChanged() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);

        public bool CanExecute(object parameter) => this.canExecute?.Invoke(parameter) ?? true;

        public void Execute(object parameter) => this.execute?.Invoke(parameter);
    }

Hast du denn das nameof(...) jetzt geändert? Dann sollte das DataGrid auch aktualisiert werden.

Sehe aber gerade, daß du ja das DataGrid an FilterCollection gebunden hast, dann mußt du auch die Eigenschaft per RaisePropertyChanged(nameof(FilterCollection))aktualisieren lassen.

Es kann aber sein, daß du jedesmal

FilterCollection = CollectionViewSource.GetDefaultView(ArtikelOderSkuEbenes);

aufrufen mußt, nachdem du ArtikelOderSkuEbenes geändert hast (besonders, wenn du es immer wieder neu zuweist).

PS: Sorry wegen AddRange - diese kann man aber als Erweiterungsmethode erstellen: ObservableCollection<T>.AddRange

Morgen

Ja alles so geändert, die Datagrid Daten ändern sich nicht. Die alten Daten bleiben sichtbar.

beim aufruf werden die neuen Daten aber schon geholt aber nicht angezeigt.

private ObservableCollection<ArtikelOderSkuEbene> _artikelOderSkuEbenes = new ObservableCollection<ArtikelOderSkuEbene>();
        public ObservableCollection<ArtikelOderSkuEbene> ArtikelOderSkuEbenes
        {
            get => _artikelOderSkuEbenes;
            set
            {
                if (_artikelOderSkuEbenes != value)
                {
                    _artikelOderSkuEbenes = value;
                    //this.RaisePropertyChanged();
                    this.RaisePropertyChanged(nameof(ArtikelOderSkuEbenes));
                }
            }
        }
  
public MainDispoViewModel()
        {
            this.SkuEbeneCommand = new DelegateCommand(RefreshSku);

            //this.SkuEbeneCommand = new DelegateCommand
            //(
            //    (o) => artikelOderSkuEbenes != null,
            //    (o) => { this.artikelOderSkuEbenes = RefreshSku(); }
            //);

            //False für Artikelansicht
            bool artikelOderSku = false;
            ArtikelOderSkuEbenes = azManagerRepository.GetArtikelOderSku(artikelOderSku);
            FilterCollection = CollectionViewSource.GetDefaultView(ArtikelOderSkuEbenes);
            HeaderGroupBox = "Artikel (" + ArtikelOderSkuEbenes.Count.ToString() + ")";
        }

        //Command Action
        private void RefreshSku(Object obj)
        {
            //this.artikelOderSkuEbenes.Clear();
            //True für Artikelansicht
            ArtikelOderSkuEbenes = azManagerRepository.GetArtikelOderSku(true);
        }      
        

Ach ja was ich vergessen habe zu erwähnen, ich habe ja eine HeaderGroupBox. Dort ändert sich die Anzahl auch nicht in der GroupBox.

Wenn ich z.b eine CheckBox Checke, obwohl die anzahl dann weniger ist. Die FilterCollection funktioniert.

private bool _lagerndCheckBox;
        public bool lagerndCheckBox
        {
            get { return _lagerndCheckBox; }
            set
            {
                _lagerndCheckBox = value;
                FilterCollection.Filter += FilterByName;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }
        
private bool FilterByName(object listings)
        {
            var listingDetail = listings as ArtikelOderSkuEbene;

            if (lagerndCheckBox == true)
                return listingDetail != null && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);
            else if (activeOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0;
            else if (fbaOnlyCheckBox == true)
                return listingDetail.FulfillmentChannel_id == 2;
            
            else if (lagerndCheckBox == true && activeOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0 && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);
            
            else if (lagerndCheckBox == true && fbaOnlyCheckBox == true)
                return listingDetail.FulfillmentChannel_id == 2 && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);
            
            else if (activeOnlyCheckBox == true && fbaOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0 && listingDetail.FulfillmentChannel_id == 2;
            
            else if (lagerndCheckBox == true && activeOnlyCheckBox == true && fbaOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0 && listingDetail.FulfillmentChannel_id == 2 && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);

            return false;
        }
        
 private string _headerGroupBox;
        public string HeaderGroupBox
        {
            get { return _headerGroupBox; }
            set
            {
                _headerGroupBox = value;
                RaisePropertyChanged(nameof(HeaderGroupBox));
            }
        }

Du hast noch nicht alles umgesetzt, was ich geschrieben habe.

Evtl. mußt du gedanklich mal einen Schritt zurück machen und ersteinmal direkt an

ItemsSource="{Binding ArtikelOderSkuEbenes}"

binden, um nur den Button zu testen.

Und wenn das funktioniert, dann änderst du es wieder auf FilterCollection und fügst in dessen Setter das nameof(...) hinzu und rufst diesen dann in RefreshSku auf (wie im Konstruktor).

Das weitere Problem mit HeaderGroupBox können wir dann danach angehen.

Auf jeden Fall ist das += bei

FilterCollection.Filter += FilterByName;

schon mal verdächtig, denn dadurch wird jedesmal ein weiterer Filter hinzugefügt.

Hi

Was genau habe ich nicht umgesetzt ?

Geht immer noch nicht, Daten bleiben und werden nicht überschrieben.

ItemsSource="{Binding ArtikelOderSkuEbenes}"

private ObservableCollection<ArtikelOderSkuEbene> _artikelOderSkuEbenes = new ObservableCollection<ArtikelOderSkuEbene>();
        public ObservableCollection<ArtikelOderSkuEbene> ArtikelOderSkuEbenes
        {
            get => _artikelOderSkuEbenes;
            set
            {
                if (_artikelOderSkuEbenes != value)
                {
                    _artikelOderSkuEbenes = value;
                    this.RaisePropertyChanged(nameof(ArtikelOderSkuEbenes));
                }
            }
        }
        
public MainDispoViewModel()
        {
            this.SkuEbeneCommand = new DelegateCommand(RefreshSku);

            //False für Artikelansicht
            ArtikelOderSkuEbenes = azManagerRepository.GetArtikelOderSku(false);
            //FilterCollection = CollectionViewSource.GetDefaultView(ArtikelOderSkuEbenes);
            //HeaderGroupBox = "Artikel (" + ArtikelOderSkuEbenes.Count.ToString() + ")";
        }

        //Command Action
        private void RefreshSku(Object obj)
        {
            ArtikelOderSkuEbenes = azManagerRepository.GetArtikelOderSku(true);
            //FilterCollection = CollectionViewSource.GetDefaultView(ArtikelOderSkuEbenes);
            //HeaderGroupBox = "Artikel (" + ArtikelOderSkuEbenes.Count.ToString() + ")";
        }

So sollte der Code funktionieren. Wird denn der Getter nach dem Setter aufgerufen (und dann stehen auch dort die geänderten Daten drin)?

Ansonsten zeige mal die Klasse BaseViewModel- ist dort INotifyPropertyChanged mittels RaisePropertyChanged korrekt implementiert?

Hi 😄

Danke ich hab es jetzt.

Also beim ersten Aufruf wurde der Setter und dann der Getter Aufgerufen. Bei den Command hat er nur den Setter Aufgerufen.

Im BaseViewModel habe ich dann eine Änderung vorgenommen, statt //this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(propertyName)));

habe ich die genommen: handler(this, new PropertyChangedEventArgs(propertyName));

public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
                //this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(propertyName)));
            }
        }
    }

Jetzt funktioniert auch die HeaderGroupBox. Ich Danke Dir war echt lange anmachen.

Kann ich Dir noch eine Frage stellen, gibt es die Möglichkeit die Daten aus der Datenbank über Dapper Automatisch zu Aktualisieren ????

Choco

Puh, ich dachte schon, WPF wäre kaputt. 😉

Und ich hoffe, du verstehst auch, was am Code falsch war.

Meinst du bei Dapper, daß automatisch bei Änderungen an den Tabellendaten das DataGrid aktualisiert wird?

PS: Deinen Code kann man noch verkürzen zu

protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Der null-conditional Operator ?. behandelt schon intern die Abfrage auf null.

Und durch das CallerMemberNameAttribute braucht man beim Aufruf den Eigenschaftennamen nicht mehr angeben (das ersetzt der Compiler dann automatisch), so daß man in den Settern nur noch

RaisePropertyChanged();

benötigt.

Ja ich habe alles ganz gut verstanden, danke nochmal.

Meinst du bei Dapper, daß automatisch bei Änderungen an den Tabellendaten das DataGrid aktualisiert wird?

Ja genau das meine ich und was müsste ich noch ändern ?

Choco

Hinweis von Abt vor einem Jahr

Bitte die Editor-Funktionen für Code-Formatierung und Zitate verwenden. Hab jetzt jeden Deiner Beiträge editiert, muss nicht sein.

So funktionieren Datenbank- bzw ORM-Systeme nicht, man benötigt immer einen Trigger, um die Daten neu zu laden.

Du könntest im Hintergrund intervallmäßig die Daten abfragen (z.B. ob sich die Anzahl der Datensätze verändert hat) und dann die Daten neu laden. Um jedoch einzelne Tabellenwerte auf Änderung zu überprüfen, gibt es keine automatische Vorgehensweise.

Warum benötigst du das denn?

Ja ich habe zur Zeit, viel Zeit auf der Arbeit und probiere einiges aus. Das war jetzt eher neugier.

Ich hätte nochmal eine letzte Frage, ich habe ganz viele CheckBox Filter. Gibt es eine einfachere Weise das Filtern in MVVM zu erstellen als was ich gemacht habe. Weil das sehr viel Code wird und wenn es einfacher geht warum nicht ?

Es sind noch mehr und Funktionieren tut es auch.

private bool FilterByName(object listings)
        {
            var listingDetail = listings as ArtikelOderSkuEbene;

            if (lagerndCheckBox == true)
                return listingDetail != null && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);
            else if (activeOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0;
            else if (fbaOnlyCheckBox == true)
                return listingDetail.FulfillmentChannel_id == 2;
            
            else if (lagerndCheckBox == true && activeOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0 && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);
            
            else if (lagerndCheckBox == true && fbaOnlyCheckBox == true)
                return listingDetail.FulfillmentChannel_id == 2 && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);
            
            else if (activeOnlyCheckBox == true && fbaOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0 && listingDetail.FulfillmentChannel_id == 2;
            
            else if (lagerndCheckBox == true && activeOnlyCheckBox == true && fbaOnlyCheckBox == true)
                return listingDetail.ActiveCount > 0 && listingDetail.FulfillmentChannel_id == 2 && (listingDetail.WdpBestand > 0 || listingDetail.CslBestand > 0);

            return false;
        }

        private bool _lagerndCheckBox;
        public bool lagerndCheckBox
        {
            get { return _lagerndCheckBox; }
            set
            {
                _lagerndCheckBox = value;
                FilterCollection.Filter = FilterByName;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }

        private bool _flag2CheckBox;
        public bool flag2CheckBox
        {
            get { return _flag2CheckBox; }
            set
            {
                _flag2CheckBox = value;
                FilterCollection.Filter = FilterByName;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }

        private bool _activeOnlyCheckBox;
        public bool activeOnlyCheckBox
        {
            get { return _activeOnlyCheckBox; }
            set
            {
                _activeOnlyCheckBox = value;
                FilterCollection.Filter = FilterByName;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }

        private bool _fbaOnlyCheckBox;
        public bool fbaOnlyCheckBox
        {
            get { return _fbaOnlyCheckBox; }
            set
            {
                _fbaOnlyCheckBox = value;
                FilterCollection.Filter = FilterByName;
                HeaderGroupBox = "Artikel (" + FilterCollection.Cast<ArtikelOderSkuEbene>().Count().ToString() + ")";
            }
        }

Choco

Wenn ich das richtig erfasse, so sind bisher in FilterByName die unteren Codezeilen überflüssig, da du ja in den ersten 3 Abfragen schon die Flags einzeln abfragst und sofort einen Filter zurückgibst (z.B. wenn lagerndCheckBox == trueist).

Gibt es eine Priorität bei den Checkboxen oder sollen die Flags einfach alle ver'und'ed werden?

Für letzteres so in der Art

bool b = true;
if (flag1)
    b = b && condition1;
if (flag2)
    b = b && condition2;
// ...

return b;

PS: Und für die beiden Zeilen

FilterCollection.Filter = FilterByName;
HeaderGroupBox = "...";

kannst du eine eigene Methode erstellen und diese jeweils in den Settern aufrufen (Codeduplikation vermeiden).

Morgen

Die Flags sind alle miteinander verbunden.

Ich muss ja alle vier Konditionen berücksichtigen ?

Die Methoden sind erstellt, danke.

Hi

Ich hab es verstanden und Funktioniert jetzt alles besstens 😄.

Danke nochmal.