You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WPF MVVM中BindingList元素属性变更后更新AvgPricePerRoom

解决WPF MVVM中DataGrid修改属性后实时更新计算属性的问题

嘿,刚上手WPF和MVVM碰到这个问题太正常了!我来给你捋清楚怎么实现实时更新AvgPricePerRoom的逻辑,一步步来:

第一步:确保HouseViewModel实现INotifyPropertyChanged

BindingList能感知元素属性变化的前提是,元素本身会触发属性变更通知。所以你的HouseViewModel必须实现INotifyPropertyChanged接口,并且在YearBuiltPriceRooms这三个可编辑属性的setter里触发PropertyChanged事件:

public class HouseViewModel : INotifyPropertyChanged
{
    private int _yearBuilt;
    public int YearBuilt
    {
        get => _yearBuilt;
        set
        {
            if (_yearBuilt != value)
            {
                _yearBuilt = value;
                OnPropertyChanged(nameof(YearBuilt));
            }
        }
    }

    private decimal _price;
    public decimal Price
    {
        get => _price;
        set
        {
            if (_price != value)
            {
                _price = value;
                OnPropertyChanged(nameof(Price));
            }
        }
    }

    private int _rooms;
    public int Rooms
    {
        get => _rooms;
        set
        {
            if (_rooms != value)
            {
                _rooms = value;
                OnPropertyChanged(nameof(Rooms));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

第二步:在MainViewModel中监听HouseList的变化

接下来要在MainViewModel里处理两个关键场景:

  • 切换选中的StreetViewModel时,订阅/取消订阅其HouseList的事件
  • HouseList中的元素添加、删除,或者元素属性变更时,通知AvgPricePerRoom更新

具体代码实现如下:

public class MainViewModel : INotifyPropertyChanged
{
    private StreetViewModel _selectedStreet;
    public StreetViewModel SelectedStreet
    {
        get => _selectedStreet;
        set
        {
            if (_selectedStreet != value)
            {
                // 取消订阅之前街道的HouseList事件,避免内存泄漏
                if (_selectedStreet != null)
                {
                    _selectedStreet.HouseList.ListChanged -= HouseList_ListChanged;
                }

                _selectedStreet = value;
                OnPropertyChanged(nameof(SelectedStreet));

                // 订阅新选中街道的HouseList事件
                if (_selectedStreet != null)
                {
                    _selectedStreet.HouseList.ListChanged += HouseList_ListChanged;
                    // 切换街道后立即计算一次平均值
                    OnPropertyChanged(nameof(AvgPricePerRoom));
                }
            }
        }
    }

    public double AvgPricePerRoom
    {
        get
        {
            // 边界情况处理:没有选中街道、房屋列表为空、房间数为0的情况
            if (SelectedStreet?.HouseList == null || !SelectedStreet.HouseList.Any())
                return 0;

            var validHouses = SelectedStreet.HouseList.Where(h => h.Rooms > 0);
            return validHouses.Any() ? validHouses.Average(h => (double)h.Price / h.Rooms) : 0;
        }
    }

    private void HouseList_ListChanged(object sender, ListChangedEventArgs e)
    {
        // 只要列表有任何变化(元素增删、属性修改),就通知UI更新平均值
        OnPropertyChanged(nameof(AvgPricePerRoom));
    }

    // INotifyPropertyChanged接口实现
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

第三步:确认StreetViewModelHouseList类型

最后要保证StreetViewModel里的HouseListBindingList<HouseViewModel>类型,这样才能触发ListChanged事件:

public class StreetViewModel
{
    // 初始化BindingList,确保它能感知元素属性变化
    public BindingList<HouseViewModel> HouseList { get; } = new BindingList<HouseViewModel>();

    // 你的其他街道属性...
}

为什么这样能生效?

当你在DataGrid里修改某个房屋的PriceRooms时:

  1. HouseViewModel的属性setter会触发PropertyChanged事件
  2. BindingList会捕获到这个事件,进而触发自身的ListChanged事件(ListChangedTypeItemChanged
  3. MainViewModel中订阅的HouseList_ListChanged方法会被调用,触发AvgPricePerRoom的属性变更通知
  4. UI上绑定AvgPricePerRoom的TextBox就会自动更新显示最新的计算值

内容的提问来源于stack exchange,提问作者Sebastian

火山引擎 最新活动