WPF数据绑定,属性值更改UI不刷新其内容[重复]

Posted

技术标签:

【中文标题】WPF数据绑定,属性值更改UI不刷新其内容[重复]【英文标题】:WPF data binding, on properties values changed the UI don't refresh its content [duplicate] 【发布时间】:2021-12-13 05:30:45 【问题描述】:

我知道有很多问题可能看起来像这样,但我在这里和其他论坛上真的找不到任何人回答我的问题。

所以,我对 WPF 比较陌生,我正在测试数据绑定,但我遇到了一个问题,即当 ObservableCollection 的值发生更改时,数据没有得到更新。

我会举个例子。

Main.xaml

<Window x:Class="TestWPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TestWPF"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid x:Name="MyGrid">

</Grid>
</Window>

Main.xaml.cs

using System.Windows;

namespace TestWPF

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window

    public MainWindow()
    
        InitializeComponent();

        Stand stand = new Stand("Best Seller Stand");
        stand.cars.Add(new Car()
        
            ID = "1",
            Brand = "BMW",
            CarNumber = 165,
            HaveRadio = true
        );
        stand.cars.Add(new Car()
        
            ID = "2",
            Brand = "Toyota",
            CarNumber = 421,
            HaveRadio = true
        );
        stand.cars.Add(new Car()
        
            ID = "4",
            Brand = "FIAT",
            CarNumber = 312,
            HaveRadio = false
        );
        stand.cars.Add(new Car()
        
            ID = "3",
            Brand = "Ferrari",
            CarNumber = 12,
            HaveRadio = true
        );

        MyGrid.Children.Add(stand.GetCatalog());
    
  

汽车.cs

using System;

namespace TestWPF

public class Car : IComparable, IComparable<int>

    public string ID  get; set; 
    public string Brand  get; set; 
    public int CarNumber  get; set; 
    public bool HaveRadio  get; set; 
    public void GerateRandomCarNumber()
    
        CarNumber = new Random().Next(int.MinValue, int.MaxValue);
    
    public int CompareTo(int other)
    
        return CarNumber.CompareTo(other);
    

    public int CompareTo(object obj)
    
        Car other = null;
        if (obj is Car)
            other = obj as Car;
        return CarNumber.CompareTo(other.CarNumber);
    
  

Stand.cs

using System.Collections.Generic;
using System.Linq;

namespace TestWPF

public class Stand

    public Stand(string name)
    
        Name = name;
    
    public string Name  get; set; 
    public SortedSet<Car> cars  get; set;  = new SortedSet<Car>();
    public Car BestChoice
    
        get
        
            return cars.First();
        
    
    public StandCatalog Catalog  get; set;  = null;
    public StandCatalog GetCatalog()
    
        if (Catalog == null)
            Catalog = new StandCatalog(this);
        return Catalog;
    
  

StandCatalog.xaml(用户控件)

<UserControl x:Class="TestWPF.StandCatalog"
         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" 
         xmlns:local="clr-namespace:TestWPF"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Vertical">
    <Label Name="StandName" Content="Binding Model.Name" Margin="10"/>
    <Label Name="CarBrand"  Content="Binding Model.BestChoice.Brand" Margin="10"/>
    <DataGrid AutoGenerateColumns="False" ItemsSource="Binding CatalogCar">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="Binding ID"/>
            <DataGridTextColumn Header="Brand" Binding="Binding Brand"/>
            <DataGridTextColumn Header="Car Number" Binding="Binding CarNumber"/>
            <DataGridCheckBoxColumn Header="Have Radio" Binding="Binding HaveRadio"/>
        </DataGrid.Columns>
    </DataGrid>
    <Button Content="Gerate Random Number" Click="btn_GerateRandomNumber"/>
</StackPanel>
</UserControl>

StandCatalog.xaml.cs

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace TestWPF

/// <summary>
/// Interaction logic for StandCatalog.xaml
/// </summary>
public partial class StandCatalog : UserControl

    public Stand Model  get; init; 
    public ObservableCollection<Car> CatalogCar  get; set; 
    public StandCatalog()
    
        InitializeComponent();
        this.DataContext = this;
    

    public StandCatalog(Stand model) : this()
    
        Model = model;
        CatalogCar = new ObservableCollection<Car>(Model.cars);
    

    private void btn_GerateRandomNumber(object sender, RoutedEventArgs e)
    
        foreach (var item in Model.cars)
        
            item.GerateRandomCarNumber();
        
    
  

所以我得到了这个应用程序:

但是当我点击按钮生成随机数时,数据网格不会刷新,标签 (Name="CarBrand") 也不会改变...... 当元素更改其值时,数据绑定不会刷新 UI 吗?

我知道值发生了变化,因为当我重新排序数据网格时,我得到了这个:

谁能帮帮我?

另一个问题,我将 Stand 类用作 StandCatalog(视图/控制器)的模型,同时使用 SortedSet 和 ObservableCollection 的最佳方法是什么?还是应该在模型中使用 SortedSet?

【问题讨论】:

您尚未为您的属性实现 INotifyPropertyChanged。当属性更改时,UI 不会收到通知,因为没有反馈。实现 INotifyPropertyChanged 并在所有属性设置器中调用 PropertyChanged,它应该可以工作。 【参考方案1】:

选项 1:INotifyPropertyChanged

Car 类应实现INotifyPropertyChanged 接口,以便在属性更改时通知目标。

public class Car : IComparable, IComparable<int>, INotifyPropertyChanged

    private int _carNumber;

    public string ID  get; set; 
    public string Brand  get; set; 

    public int CarNumber
    
        get => _carNumber;
        set
        
            if (_carNumber == value) return;

            _carNumber = value;
            OnPropertyChanged();
        
    

    public bool HaveRadio  get; set; 

    public void GerateRandomCarNumber()  CarNumber = new Random().Next(int.MinValue, int.MaxValue); 
    public int CompareTo(int other)  return CarNumber.CompareTo(other); 

    public int CompareTo(object obj)
    
        Car other = null;
        if (obj is Car)
            other = obj as Car;
        return CarNumber.CompareTo(other.CarNumber);
    

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 

选项 2:DependencyProperty

CarNumber 属性定义为DependencyProperty。基础架构将处理这些更改。

public class Car : DependencyObject, IComparable, IComparable<int>

    public static readonly DependencyProperty
        CarNumberProperty = DependencyProperty.Register("CarNumber", typeof(int), typeof(Car));

    public string ID  get; set; 
    public string Brand  get; set; 

    public int CarNumber
    
        get => (int)GetValue(CarNumberProperty);
        set => SetValue(CarNumberProperty, value);
    

    public bool HaveRadio  get; set; 

    public void GerateRandomCarNumber()  CarNumber = new Random().Next(int.MinValue, int.MaxValue); 
    public int CompareTo(int other)  return CarNumber.CompareTo(other); 

    public int CompareTo(object obj)
    
        Car other = null;
        if (obj is Car)
            other = obj as Car;
        return CarNumber.CompareTo(other.CarNumber);
    

【讨论】:

See here 为什么在视图模型中通常不使用选项 2。

以上是关于WPF数据绑定,属性值更改UI不刷新其内容[重复]的主要内容,如果未能解决你的问题,请参考以下文章

是否可以“刷新”WPF 数据绑定

WPF 控件模板不根据依赖属性绑定值更改

模型更改时刷新 ViewModel 的所有属性的数据绑定的好方法

在 WPF 中使用数据绑定时,OxyPlot 不刷新

wpf MVVM框架基础

设置DataContext后WPF依赖属性两种方式绑定不起作用[重复]