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