经过很长时间后,我终于找到了造成这个bug的问题。在显示出现问题的代码之前,我需要解释一下情况。
绑定和属性结构
在我的应用程序中,ComboBox
绑定为ItemSource
Rounds
列表以及SelectedItem
用户从列表中选择的Round
ComboBox
具有以下结构:
<ComboBox ItemsSource="{Binding Rounds}" DisplayMemberPath="RoundName" SelectedItem="{Binding SelectedRound, Mode=TwoWay}" />
你可以看到我的模态TwoWay
这允许我在用户更改所选的SelectedRound
时自动更新属性Item
。
这是班级Round
:
public class Round
{
public int Id { get; set; }
public string Link { get; set; }
public bool Selected { get; set; }
public string RoundName { get; set; }
}
这是ComboBox
使用的属性:
//List of rounds available
private List<Round> _rounds;
public List<Round> Rounds
{
get { return _rounds; }
set
{
_rounds = value;
OnPropertyChanged();
}
}
//Selected round on ComboBox
private Round _selectedRound;
public Round SelectedRound
{
get { return _selectedRound; }
set
{
_selectedRound = value;
OnPropertyChanged();
}
}
这两个属性都实现了OnPropertyChanged()
。
属性定价的工作原理
在应用程序中有一个名为LoadRounds()
的方法,每次用户按下按钮时都会调用该方法,此方法具有以下指令:
public void LoadRounds(Team team)
{
//Fill the source of ComboBox with the rounds of the new team
Rounds = team.Rounds.ToList(); //<- Create a copy, so no reference
//Get the selected round
SelectedRound = Rounds?.FirstOrDefault(x => x.Id == team.CurrentRound.Id);
}
SelectedRound
取自名为team
的{{1}}属性,特别是每个CurrentRound
都有一个回合,因此对于练习示例:
team
因此[Rounds id available in Rounds property]
37487
38406
38405
37488
37486
...
[CurrentRound id of team]
38405
将包含SelectedRound
Round
38405,Id
查询效果良好。
问题
我在linq
上设置breakpoint
,_selectedRound = value;
是value
项目(38405)的第一个开火时间,但也有第二个开火时间(不应该)具有值Round
。
经过很多时间在电脑上花了很多时间来理解为什么会出现这种情况我想出来了。
似乎null
(ComboBox
模式)不知道如何映射来自TwoWay
的{{1}},所以基本上是这样的:
SelectedRound
我还使用堆栈调用窗口来查看是否有任何方法再次调用setter属性,但是没有调用setter的外部方法,所以我猜是ItemSource
模式触发了再次设定。
我该如何解决这种情况?我知道这篇文章有点复杂,我可以回答所有问题,并在需要时提供更多详细信息。
感谢所有人,祝你有个美好的一天。
更新#1
这是我的1. [Item Source updated with new Rounds]
2. [SelectedRound updated from the new `Rounds` available]
3. [SelectedRound setter called again with a null value]
实施:
TwoWay
更新#2
当用户更改INotifyPropertyChanged
上的选择时会调用方法public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
,LoadRounds
包含所有DataGrid
,因此我获得了{{1}用户在DataGrid
上选择,然后调用方法teams
。
所有小组都包含在team
中,DataGrid
是LoadRounds
。
在方法DataGrid
的最后,我将ItemSource
的当前List<Team>
保存在名为LoadRounds
的属性上,只需执行以下操作:
Round
这样,如果Team
等于SelectedRoundSaved
,我会阻止重新加载SelectedRoundSaved = Clone(SelectedRound);
。
Rounds
方法允许我克隆对象,并具有以下结构:
SelectedRoundSaved
它使用SelectedRound
库。
这些信息根本不是必需的,但正如我所说,我将添加您要求的所有信息,感谢您的关注。
答案 0 :(得分:1)
您确定此订单是否正确?
1. [Item Source updated with new Rounds]
2. [SelectedRound updated from the new `Rounds` available]
3. [SelectedRound setter called again with a null value]
组合框最初绑定后,我希望订单(换成#2和#3的顺序)
1. [Item Source updated with new Rounds]
2. [SelectedRound setter called again with a null value]
3. [SelectedRound updated from the new `Rounds` available]
此行为遵循我对组合框的期望。
当您更新ItemSource
ComboBox转储其项目并使用新集合重新加载时。因为ComboBox是一个选择器,所以它必须检查它的SelectedItem
。如果在新集合中找不到SelectedItem
,则会将其SelectedItem
更新为null
。所有这一切都是因为OnPropertyChanged();
setter中的Rounds
调用而发生的。
(注意:只有在加载和绑定组合框后才会看到此行为)
现在有很多方法可以解决这个问题,但最简单的IMO只是改变操作的顺序:
public void LoadRounds(Team team)
{
//Fill the source of ComboBox with the rounds of the new team
var newRounds = team.Rounds.ToList(); //<- Create a copy, so no reference
//Get the selected round
SelectedRound = newRounds.FirstOrDefault(x => x.Id == team.CurrentRound.Id);
Rounds = newRounds;
}