输入的项目有时在数据网格中显示为空

Inputted Items sometimes appear empty in Data grid

我在 WPF UserControl 中有 ItemsControl 和 DataGrid。 this is how it looks like

当按下“添加到卡片”按钮时,一个 ViewModel 实例被添加到绑定到 DataGrid 的 ObservableCollection。

 <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>

问题是,有时当我添加新项目时,它看起来像一个空列。

我试图添加一个按钮来刷新数据网格,但是当按下它时,所有项目都会变成空白。

此外,我将 DataGrid 包装在带有水平滚动条的 ScrollViewer 中,但由于某种原因不起作用。

那就是View的ViewModel

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));
        }
    }

像 ItemsControl 的 ItemTemplates 一样的视图

 <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"/>

这是将项目添加到 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);
    }

将商品添加到购物车的方式不是线程安全的。

想象一下被调用的 AddToCart() 将更新您的 customerOrderingViewModelmealCardCustomerViewModel。然后想象一下,在调用 Execute 之前,其他某个线程更改了 customerOrderingViewModelmealCardCustomerViewModel。这可能会导致 Execute() 在您的订单中添加错误的(或 Null)餐点。 如果那是你出错的原因,下面的代码应该可以解决它:

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

如果您不需要 class 中的 customerOrderingViewModelmealCardCustomerViewModel 拥有 AddToCart(),您甚至可以完全保留这些变量。

旁注: 如果您不打算更改可观察集合而只是更改它们的内容,您可以简单地将它们声明为 public 字段而不是属性。只有当整个 ObservableCollection 对象发生变化时,属性的 setter 才会被访问,但如果其内容发生变化则不会。针对 ObservableCollection 内部更改的 PropertyChanged 通知由 ObservableCollection 实现处理。

问题在于对象中的图像现在不存在,因此它们为空。

由于某种原因,空值会导致转换器中出现无限循环,因此视图模型无法加载实体的属性,但集合可以读取计数已更改,从而显示空行。