Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MVVM C#WPF-在observablecollection中更改项目时未更新UI_C#_Wpf_User Interface_Mvvm_Inotifypropertychanged - Fatal编程技术网

MVVM C#WPF-在observablecollection中更改项目时未更新UI

MVVM C#WPF-在observablecollection中更改项目时未更新UI,c#,wpf,user-interface,mvvm,inotifypropertychanged,C#,Wpf,User Interface,Mvvm,Inotifypropertychanged,我已经看了两天关于这个问题的Stackoverflow帖子了,我似乎不明白为什么我的代码不起作用 当我在可观察集合中更改项目时,我似乎无法更新UI中的数据网格 我知道,ObservableCollection不会触发PropertyChanged事件(如果其中的项目发生更改) 其他人似乎已经成功地做到了这一点,他们将INotifyPropertyChanged添加到模型中,并在属性更改时调用OnPropertyChanged。我已经实现了这个,并且检查了PropertyChanged事件是否正在

我已经看了两天关于这个问题的Stackoverflow帖子了,我似乎不明白为什么我的代码不起作用

当我在
可观察集合
中更改项目时,我似乎无法更新UI中的数据网格

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

其他人似乎已经成功地做到了这一点,他们将
INotifyPropertyChanged
添加到模型中,并在属性更改时调用
OnPropertyChanged
。我已经实现了这个,并且检查了
PropertyChanged
事件是否正在发生

将新项目添加到集合时,UI将更新

testObj = modelObj
我意识到这可能是一个线程问题,但我真的不知道如何检查或修复它

我对编程相当陌生,并试图对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);
    }
}
     <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>
视图模型:

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

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

您应该设置现有对象的
计时器
属性:

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
对象的引用。这不会更新ModelObjects集合中对象的
Timer
属性

testObj = modelObj
没有效果。您只需在变量中输入一个值。召唤

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

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

他并不是只更新Timer,据我所知,他希望替换整个对象(因此新的ipAddress和其他字段也将在其中),只有Timer属性引发PropertyChanged事件。此外,他正在根据名称将modelObj映射到testObj,因此这不太可能更改。谢谢!愚蠢的错误,但oldObj=newObj会“移动”属性值,然后触发propertychanged事件。@AndréNicolaysen此代码不同,它不会像初始代码那样复制IpAddress。但是,如果更改单元格中的值,则目标是错误的,
。只有在正在编辑的单元格失去焦点(单击另一个单元格)时,才会触发PropertyChanged
,您可能希望实现
INotifyPropertyChanged
INotifyPropertyChanged