Search code examples
c#wpfmvvmdatagrid

Inputted Items sometimes appear empty in Data grid


I have ItemsControl and a DataGrid in a WPF UserControl. this is how it looks like

when the "Add to card" button is pressed a ViewModel instance is added to ObservableCollection bound to the DataGrid.

 <ItemsControl
            ItemsSource="{Binding Meals}"
            x:Name="MealList"                       
            Margin="5">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <components:MealCardCustomer
                        BorderBrush="OrangeRed"
                        BorderThickness="5px"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
    <ScrollViewer
        HorizontalScrollBarVisibility="Auto"
        VerticalScrollBarVisibility="Disabled">
        <DataGrid
            HorizontalAlignment="Stretch"
            IsReadOnly="True"            
            Background="Orange"
            x:Name="OrderedMeals"
            SelectionMode="Single"
            ItemsSource="{Binding OrderedMeals, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"    
            SelectedIndex="{Binding SelectedOrderedMeal, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"
            FontSize="26"
            Grid.Column="0"
            Grid.Row="0"
            Margin="5"
            AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Name, Mode=OneWay}" Header="Name" />
                <DataGridTextColumn Binding= "{Binding Price, Mode=OneWay}" Header="Price" />
                <DataGridTextColumn Binding="{Binding Ingredients, Mode=OneWay}" Header="Ingredients" />
            </DataGrid.Columns>
    </DataGrid>
    </ScrollViewer>

The problem is that sometimes when I add new items it appears like an empty column.

I tried to add a button which refreshes the data grid but when pressed it makes the all of the items blank.

Also I've wrapped the DataGrid in a ScrollViewer with a horizontal scroll which for some reason doesn't work.

That's the ViewModel of the View

private string? address;
    public string? Address
    {
        get { return address; }
        set { address = value; OnPropertyChaneg(nameof(Address)); }
    }

    private int selectedOrderedMeal = -1;
    public int SelectedOrderedMeal
    {
        get { return selectedOrderedMeal; }
        set { selectedOrderedMeal = value; OnPropertyChaneg(nameof(SelectedOrderedMeal)); }
    }

    private ObservableCollection<MealCardCustomerViewModel> meals;
    public ObservableCollection<MealCardCustomerViewModel> Meals
    {
        get { return meals; }
        set { meals = value; }
    }

    private ObservableCollection<MealCardCustomerViewModel> orderedMeals;
    public ObservableCollection<MealCardCustomerViewModel> OrderedMeals
    {
        get { return orderedMeals; }
        set { orderedMeals = value; OnPropertyChaneg(nameof(OrderedMeals)); }
    }

    public BaseCommand RemoveCommand { get; }
    public BaseCommand FinishOrderCommand { get; }
    public NavigateCommand NavigateToCustomerListOfOtders { get; }
    public BaseCommand LoadMealsCommand { get; }

    public CustomerOrderingViewModel(NavigationService customerListOfOrdersNavigationService, NavigationService helpNavigationService, IMealService mealService)
        : base(helpNavigationService, mealService)
    {
        Meals = new ObservableCollection<MealCardCustomerViewModel>();
        OrderedMeals = new ObservableCollection<MealCardCustomerViewModel>();

        RemoveCommand = new RemoveMeal(this);
        FinishOrderCommand = new FinishOrder(this, customerListOfOrdersNavigationService);
        NavigateToCustomerListOfOtders = new NavigateCommand(customerListOfOrdersNavigationService);

        LoadMealsCommand = new LoadMeals<CustomerOrderingViewModel>(this);
    }

    public static CustomerOrderingViewModel LoadViewModel(NavigationService customerListOfOrders, NavigationService helpNavigationService, IMealService mealService)
    {
        CustomerOrderingViewModel viewModel = new CustomerOrderingViewModel(customerListOfOrders, helpNavigationService, mealService);
        viewModel.LoadMealsCommand.Execute(null);

        return viewModel;
    }

    public override void LoadMealsList(List<Meal> meals)
    {
        Meals.Clear();
        foreach (var meal in meals)
        {
            Meals.Add(new MealCardCustomerViewModel(meal,this));
        }
    }

That the Views which act like ItemTemplates for the ItemsControl

 <Image            
        Source="{Binding MealImage, Converter ={StaticResource imageConverter}, Mode=TwoWay, TargetNullValue=DefaultImage}"
        Stretch="Uniform"/>

    <DockPanel
        Grid.Row="1"
        VerticalAlignment="Center"
        Margin="5">
        <TextBlock
            FontSize="20"
            Margin="5"
            Text="Name :"/>
        <TextBox
            Text="{Binding Name,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            FontSize="20"
            Margin="5"/>
    </DockPanel>

    <DockPanel
        Grid.Row="2"
        VerticalAlignment="Center"
        Margin="5">
        <TextBlock
            FontSize="20"
            Margin="5"
            Text="Price :"/>
        <TextBox
            Text="{Binding Price, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat={}{0:f2}}"
            FontSize="20"
            Margin="5"/>
    </DockPanel>

    <DockPanel
        Grid.Row="3"
        VerticalAlignment="Center"
        Margin="5">
        <TextBlock
            FontSize="20"
            Margin="5"
            Text="Ingredients:"/>
        <TextBox
            Text="{Binding Ingredients, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            FontSize="20"
            Margin="5" 
            TextWrapping="Wrap"
            VerticalScrollBarVisibility="Visible"
            HorizontalScrollBarVisibility="Visible"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            />
    </DockPanel>
    <Button
        Command="{Binding AddToCardCommand}"
        Background="OrangeRed"
        Grid.Row="4"
        Margin="10 5 10 5"
        Content="Add to cart"
        FontSize="20"/>

and that's the command that adds the item to the ObservableCollection

        private CustomerOrderingViewModel customerOrderingViewModel;
    private MealCardCustomerViewModel mealCardCustomerViewModel;

    public AddToCard(CustomerOrderingViewModel customerOrderingViewModel, MealCardCustomerViewModel mealCardCustomerViewModel)
    {
        this.customerOrderingViewModel = customerOrderingViewModel;
        this.mealCardCustomerViewModel = mealCardCustomerViewModel;
    }

    public override void Execute(object? parameter)
    {
        customerOrderingViewModel.OrderedMeals.Add(mealCardCustomerViewModel);
    }

Solution

  • The problem was with the images in the objects which are non existing right now and so they are null.

    For some reason the null value cause infinite loop in the converter and so the view models could not load the properties of the entity but the collection could read that the count was changed thus displaying the empty rows.