WPF ComboBox绑定并不总是有效(属性从异步调用中分配)

时间:2018-04-23 16:52:48

标签: c# wpf asynchronous mvvm

我有一个对话框,它在代码隐藏中分配了DataContext。单击按钮(UX中的某个位置)时,将执行异步命令。在该命令中,对话框初始化然后打开。

现在的问题是,有时候ComboBox是空的。有没有人看到,这怎么可能出错?

我可以在打开对话框之前打印异步数据库访问中的值,并且值始终存在。

对话框中还有其他字段,这些字段绑定到ActiveUser对象。该对象不是从源自异步调用的数据设置的。与ComboBox ItemsSource不同,这些值始终存在。所以我假设它与异步调用有关。

在命令中创建和打开对话框:

EditUser = AsyncCommand.Create(async (choosenUser) =>
{
    // create a dialog, which has its DataContext assigned in the constructor in the code-behind
    EditUserDialogView dialog = new EditUserDialogView();
    // assign a property (non-async)
    ((EditUserDialogViewModel)dialog.DataContext).ActiveUser = (DbUser)choosenUser;
    // get the list of UserTypes async
    List<UserType> userTypeList = await DataAccessService.GetUserTypesAsync();

    // Debug output -> usertypes are always printed correctly, so are available at this point
    foreach (UserType ut in userTypeList)
    {
        Log.Info("UT: "+ ut.UserTypeName);
    }

    // assign UserTypes to property bound to combobox ItemsSource
    ((EditUserDialogViewModel)dialog.DataContext).UserTypeComboBoxList = userTypeList;


    // open the dialog
    if (dialog.ShowDialog() == true)
    {

    }
    else
    {

    }
});

在对话框中绑定:

 <ComboBox IsSynchronizedWithCurrentItem="True" 
       ItemsSource="{Binding UserTypeComboBoxList, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
       DisplayMemberPath="UserTypeName"/>

AsyncCommand按照此处的建议实现: https://msdn.microsoft.com/en-us/magazine/dn630647.aspx?f=255&MSPPError=-2147217396

(唯一的区别是,lambda的参数是CommandParameter(此处为choosenUser),而不是CancellationToken。)

以下是ViewModel中对话框的属性定义:

 public List<UserType> UserTypeComboBoxList { get; set;}

我想通了,我怎么能解决这个问题。当我更改属性以引发NotifyPropertyChanged事件时,它将始终正确显示ComboBox,包含值。

public List<UserType> UserTypeComboBoxList
{
    get { return userTypeComboBoxList; }
    set
    {
        userTypeComboBoxList = value;
        NotifyPropertyChanged(nameof(UserTypeComboBoxList));
    }
}

由于所有值都应该在我打开对话框时初始化,我不明白为什么提升此事件会改变任何内容。

2 个答案:

答案 0 :(得分:0)

这是竞争条件。因为,数据库调用(填充comboBox)被调用asynchronously所以如果在对话框初始化之前,异步调用完成,则comboBox正确显示;如果没有,那么您会看到您将新对象分配给属性UserTypeComboBoxList(没有NotifyingPropertyChanged),这将更改属性的引用,在这种情况下,XAML绑定它的松散轨道属性引用,直到没有明确通知后面的属性定义正在进行的操作。

所以后来的Property定义代码是正确的。实现相同行为的另一种方法是使用以下(如果在程序执行期间可以更改绑定数据):

  1. 使用提供通知的ObservableCollection 何时将对象添加/删除到集合

  2. 而不是初始化属性的新对象 您的操作中UserTypeComboBoxList,在构造函数中初始化,     和Clears&amp; Add每次更新的值。

答案 1 :(得分:0)

问题似乎是,一旦我创建了对话框并且调用了InitializeComponent(),C#就开始组装最终的View类(异步)并初始化绑定。因此,我对ViewModel属性的赋值要么是幸运的,要么是初始赋值,因为它尚未绑定,有时它将是已经绑定的空初始值的更新。

可以通过在将ViewModel分配给View之前初始化ViewModel来防止这种情况。