Question Why is my view not updating after Add/Delete operations on database?

TB007

New member
Joined
Aug 13, 2022
Messages
3
Programming Experience
Beginner
I have a fairly simple WPF app which primarily has 2 datagrid's in two different tabitem's.
The datagrid in 1st tabitem has the itemssource PendingBills which is an ICollectionView and it shows some filtered data as per some basic logic
The datagrid in 2nd tabitem has the itemssource AllBills which is also an ICollectionView and it shows some filtered data using the selected item of combo box SelectedCBItem.

C#:
<DataGrid ItemsSource="{Binding AllBills}" SelectedItem="{Binding SelectedInv}" SelectionMode="Single" SelectedIndex="{Binding SelectedIndex}" ...>

Now when I select a row from the datagrid in 2nd tabitem its different column data are shown in a GroupBox which has an ItemsControl with an ItemTemplate as shown below to basically do CRUD operations

C#:
<DataTemplate x:Key="UserGrid">
            <Border Background="Transparent" BorderBrush="White" BorderThickness="1" CornerRadius="5" >
                <Grid Margin="10">
                    ...
                    <TextBox Text="{Binding Party, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1"/>
                    <TextBox Text="{Binding BillNo, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="2"/>
                    <TextBox Text="{Binding BillDt, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="3"/>
                    <TextBox Text="{Binding Amt, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="4"/>
                    <TextBox Text="{Binding DueDt, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="5"/>
                    <TextBox Text="{Binding PaidOn, BindingGroupName=Group1, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="6"/>
                    <StackPanel Orientation="Horizontal" Grid.Row="10" Grid.ColumnSpan="2" HorizontalAlignment="Right" Margin="5,5,5,5">
                        <Button Content="Cancel" Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                        <Button Content="Delete" Command="{Binding DataContext.DeleteUserCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                        <Button Content="Save" Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                        <Button Content="Add" Command="{Binding DataContext.AddCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="4,0"/>
                    </StackPanel>
                </Grid>
            </Border>
</DataTemplate>
...
<GroupBox Grid.Row="4" Header="CRUD Form" BorderThickness="0" Foreground="LightPink">
                  <ItemsControl BindingGroup="{Binding UpdateBindingGroup, Mode=OneWay}"  VerticalAlignment="Top" Margin="5,5,5,5" Grid.Column="1"
                       ItemTemplate="{StaticResource UserGrid}"  ItemsSource="{Binding SelectedInv, Converter={StaticResource SelectedItemToItemsSource}}" />
                            <GroupBox.Style>
                                <Style TargetType="GroupBox">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding SelectedInv}" Value="{x:Null}">
                                            <Setter Property="Visibility" Value="Collapsed"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </GroupBox.Style>
</GroupBox>

My main ViewModel looks something like this

C#:
public class BillsViewModel : ViewModelBase
    {

        ...
        public ICollectionView PendingBills { get; private set; }


        private ObservableCollection<Bills> _allBills;
        ...

        public BillsViewModel()
        {
            ...
            _allBills = DatabaseLayer.GetAllBillsFromDB();

            ComboItems = new ObservableCollection<string>(_allBills.Select(b => b.Party).Distinct().OrderBy(b => b).ToList());

            PendingBills = new ListCollectionView(_allBills)
            {
                Filter = o => ((Bills)o).PaidOn=="" &&  (DateTime.ParseExact(((Bills)o).DueDt, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture) >= DateTime.Today.AddDays(-50) && DateTime.ParseExact(((Bills)o).DueDt, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture) < DateTime.Today.AddDays(50))
            };

            AllBills = new ListCollectionView(_allBills)
            {
                Filter = o => ((Bills)o).Party == SelectedCBItem
            };

            bills = new BillsBusinessObject();
            bills.BillChanged += new EventHandler(bills_BillChanged);
            UpdateBindingGroup = new BindingGroup { Name = "Group1" };
            CancelCommand = new RelayCommand(DoCancel);
            SaveCommand = new RelayCommand(DoSave);
            AddCommand = new RelayCommand(AddUser);
            DeleteUserCommand = new RelayCommand(DeleteUser);
        }
        ...

        private ICollectionView _allBillsCollection;
        public ICollectionView AllBills
        {
            get { return _allBillsCollection; }
            set
            {
                _allBillsCollection = value;
                SetValue(ref _allBillsCollection, value);
            }
        }

        public ObservableCollection<string> ComboItems { get; private set; }
        ...

        BillsBusinessObject bills;
        private ObservableCollection<Bills> _Bill;
        public ObservableCollection<Bills> Bill
        {
            get
            {
                _Bill = new ObservableCollection<Bills>(bills.GetBills());
                return _Bill;

            }
        }
        public int SelectedIndex { get; set; }
        object _SelectedInv;
        public object SelectedInv
        {
            get
            {
                return _SelectedInv;
            }
            set
            {
                if (_SelectedInv != value)
                {
                    _SelectedInv = value;
                    OnPropertyChanged("SelectedInv");
                }
            }
        }
        private BindingGroup _UpdateBindingGroup;
        public BindingGroup UpdateBindingGroup
        {
            get
            {
                return _UpdateBindingGroup;
            }
            set
            {
                if (_UpdateBindingGroup != value)
                {
                    _UpdateBindingGroup = value;
                    OnPropertyChanged("UpdateBindingGroup");
                }
            }
        }
        void bills_BillChanged(object sender, EventArgs e)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
            {
                OnPropertyChanged("Bill");
            }));
        }
        public RelayCommand CancelCommand { get; set; }
        public RelayCommand SaveCommand { get; set; }
        public RelayCommand AddCommand { get; set; }
        public RelayCommand DeleteUserCommand { get; set; }

        void DoCancel(object param)
        {
            UpdateBindingGroup.CancelEdit();
            if (SelectedIndex == -1)    //This only closes if new
                SelectedInv = null;
        }

        void DoSave(object param)
        {
            UpdateBindingGroup.CommitEdit();
            var bill = SelectedInv as Bills;
            if (SelectedIndex == -1)
            {
                bills.AddBill(bill);
                _allBills.Add(bill);
                OnPropertyChanged("Bill"); // Update the observablecollection from the data source
                MessageBox.Show("Data Saved Successfully.");
            }
            else
            {
                bills.UpdateBill(bill);
                MessageBox.Show("Data Updated Successfully.");
            }
                
            SelectedInv = null;
            PendingBills.Refresh();
            OnPropertyChanged("Cnt");
        
        }

        void AddUser(object param)
        {
            SelectedInv = null; // Unselects last selection. Essential, as assignment below won't clear other control's SelectedItems
            var bill = new Bills();
            SelectedInv = bill;
        }

        void DeleteUser(object parameter)
        {
            var bill = SelectedInv as Bills;

            if (SelectedIndex != -1)
            {
                bills.DeleteBill(bill);
                OnPropertyChanged("Bill"); // Update the observablecollection from the data source

                if (!_allBills.Select(a=>a.Party).Distinct().ToList().Contains(bill.Party))  //If the data source no longer has any such "Party" then remove it from combo box items
                {
                    ComboItems.Remove(bill.Party.ToString());
                }
            }
            else
            {
                SelectedInv = null; // Simply discard the new object
            }

            _allBills.Remove(bill);

            AllBills.Refresh();
            PendingBills.Refresh();
            MessageBox.Show("Data Deleted Successfully.");
        }
    }

Currently there are a couple of issues that I'm having
1) a) When I add a new entry in the database by first hitting the Add button then fill up the data in the TextBox's and hit the Save button, if the Party is already present in the database it automatically updates the datagrid in the 2nd tabitem but doesn't in the 1st tabitem.
b) If the Party is completely new then none of the datagrid's show the new data and neither the combo box add's this new Party to its items. It only reflects when I restarst the app as the data is updated in the actual database.
2) When I delete an entry by hitting the Delete button though both the datagrid's view are updated but if the deleted entries Party no longer exists now after the deletion then it should also disappear from the combo box items but that is not happening. Once again I need to restart to app get the effect.

How do I solve these issues ? I've been struggling with this for quite a long time now so can anyone help me fix this ?
My full project can be found here for testing/better understanding.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,698
Location
Chesapeake, VA
Programming Experience
10+
I am just looking on my phone, so I must be missing it, can you share the code for: SelectedItemToItemsSource ?
 

TB007

New member
Joined
Aug 13, 2022
Messages
3
Programming Experience
Beginner
Hi @Skydiver , here you go:

C#:
    public class SelectedItemToItemsSource : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null) return null;
            return new List<object>() { value };
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,698
Location
Chesapeake, VA
Programming Experience
10+
It's not clear to me why you even need a binding group, nor why you have a group box that shows either nothing or a list that contains a single item.

Anyway, it looks like you don't have anything that sends a notification back to the view that something has changed in the collection. It currently looks like you are trying to do that "notification", but forcing refreshes but your forced refreshes are not hitting all the view elements that are bound to the items and collections.
 

TB007

New member
Joined
Aug 13, 2022
Messages
3
Programming Experience
Beginner
why you have a group box that shows either nothing or a list that contains a single item.
Good point, I removed it.
it looks like you don't have anything that sends a notification back to the view that something has changed in the collection. It currently looks like you are trying to do that "notification", but forcing refreshes but your forced refreshes are not hitting all the view elements that are bound to the items and collections.
So how do I fix it ? Can you show me ?

Note: I'm just using System.Data.Sqlite and not entity framework or something else in my app.
 
Last edited:
Top Bottom