【问题标题】:MVVM C# WPF - UI not updating when changing items in observablecollectionMVVM C#​​ WPF - 更改 observablecollection 中的项目时 UI 不更新
【发布时间】:2023-03-08 21:30:01
【问题描述】:

这两天我一直在查看有关此问题的 Stackoverflow 帖子,但我似乎无法理解为什么我的代码无法正常工作

当我更改 ObservableCollection 中的项目时,我似乎无法更新 UI 中的数据网格。

我知道ObservableCollection 不会触发PropertyChanged 事件,如果其中的项目发生更改。

似乎其他人已通过将INotifyPropertyChanged 添加到模型并在属性更改时调用OnPropertyChanged 来成功完成此操作。我已经实现了这一点,并检查了PropertyChanged 事件是否正在发生。

向集合中添加新项目时 UI 会更新。

我意识到这可能是线程问题,但我真的不明白如何检查或修复它。

我对编程还很陌生,并试图围绕 MVVM 进行思考。

有什么建议吗?

型号:

public class ModelObj : INotifyPropertyChanged
{
    public string Name { get; set; }
    public string IpAddress { get; set; }

    private DateTime timer;

    public DateTime Timer
    {
        get { return timer; }
        set
        {
            timer = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

视图模型:

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<ModelObj> ModelObjects { get; } = new ObservableCollection<ModelObj>();

    private IUdpDataService _udpDataService;

    public MainViewModel(IUdpDataService udpDataService)
    {
        _udpDataService = udpDataService;
    }

    public void StartUdpDataService()
    {
        _udpDataService.StartBroadCasting();
        _udpDataService.ReceivedDataEvent += ParseReceivedData;
    }

    private void ParseReceivedData(string receivedData)
    {
        // This object contains all the information in the received data packet. 
        UdpPacket udpPacket = new UdpPacket(receivedData);

        // This object only contains the object name, IpAddress and a time variable.
        ModelObj modelObj = new ModelObj
        {
            Name = udpPacket.Name,
            IpAddress = udpPacket.IpEthernet,
            Timer = DateTime.Now,
        };

        App.Current.Dispatcher.Invoke((Action)delegate
        {
            UpdateList(modelObj);
        });
    }

    private void UpdateList(ModelObj modelObj)
    {
        var testObj = ModelObjects.FirstOrDefault(x => x.Name == modelObj.Name);
        if (testObj != null)
        {
            testObj = modelObj
        }
        else
        {
            ModelObjects.Add(modelObj);
            testObj = modelObj;
        }
    }
}

查看:

public partial class MainWindow : Window
{
    private MainViewModel _viewModel;

    public MainWindow(MainViewModel viewModel)
    {
        InitializeComponent();
        _viewModel = viewModel;
        DataContext = _viewModel;
        Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        _viewModel.StartUdpDataService();
    }

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
            Settings.Default.Save();
            base.OnClosing(e);
    }
}

XAML:

     <DataGrid Grid.Row="0" ItemsSource="{Binding Path=ModelObjects}"
              IsReadOnly="True"
              Background="white"
              RowHeaderWidth ="0" 
              AutoGenerateColumns="False">

        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="auto" MinWidth="150"/>
            <DataGridTextColumn Header="IP address" Binding="{Binding IpAddress}" Width="*"/>
            <DataGridTextColumn Header="Timer" Binding="{Binding Timer, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
        </DataGrid.Columns>
    </DataGrid>

【问题讨论】:

  • 如果您更改单元格中的值,..PropertyChanged 不会触发,直到您正在编辑的单元格失去焦点(单击另一个单元格),您可能需要实现 INotifyPropertyChanging 和 @987654333 @

标签: c# wpf user-interface mvvm inotifypropertychanged


【解决方案1】:

您应该设置现有对象的Timer 属性:

private void UpdateList(ModelObj modelObj)
{
    var testObj = ModelObjects.FirstOrDefault(x => x.Name == modelObj.Name);
    if (testObj != null)
    {
        testObj.Timer = modelObj.Timer
    }
    else
    {
        ModelObjects.Add(modelObj);
    }
}

您当前正在获取对现有对象的引用,然后将保存此引用的testObj 变量设置为对传递给UpdateList 方法的新ModelObj 对象的引用。这不会更新 the ModelObjects 集合中对象的 Timer 属性。

【讨论】:

  • 他不是只更新 Timer,因为我知道他想替换整个对象(所以新的 ipAddress 和其他字段也会在那里)
  • 只有 Timer 属性会引发 PropertyChanged 事件。此外,他正在根据名称将 modelObj 映射到 testObj,因此这不可能改变。
  • 谢谢!愚蠢的错误,但是 oldObj = newObj 会“移动”属性值,然后触发 propertychanged 事件。
  • @AndréNicolaysen 此代码不一样,它不会像您的初始代码那样复制 IpAddress。但是目的地错了
【解决方案2】:
testObj = modelObj

没有效果。您只需将值放入变量中。打电话

    if (testObj != null)
    {
        ModelObjects.Replace(testObj,modelObj)
    }
    else
    {
        ModelObjects.Add(modelObj);
    }

如果您替换整个对象,则根本不需要实现 INotifyPropertyChanged

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    相关资源
    最近更新 更多