Mode = TwoWay返回Null

时间:2017-11-08 12:34:52

标签: c# wpf xaml

经过很长时间后,我终于找到了造成这个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

经过很多时间在电脑上花了很多时间来理解为什么会出现这种情况我想出来了。

似乎nullComboBox模式)不知道如何映射来自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

  1. 当用户更改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

  2. 所有小组都包含在team中,DataGridLoadRounds

  3. 在方法DataGrid的最后,我将ItemSource的当前List<Team>保存在名为LoadRounds的属性上,只需执行以下操作:

    Round

  4. 这样,如果Team等于SelectedRoundSaved,我会阻止重新加载SelectedRoundSaved = Clone(SelectedRound);

    Rounds方法允许我克隆对象,并具有以下结构:

    SelectedRoundSaved

    它使用SelectedRound库。

    这些信息根本不是必需的,但正如我所说,我将添加您要求的所有信息,感谢您的关注。

1 个答案:

答案 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;
}