我有一个对话框,它在代码隐藏中分配了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));
}
}
由于所有值都应该在我打开对话框时初始化,我不明白为什么提升此事件会改变任何内容。
答案 0 :(得分:0)
这是竞争条件。因为,数据库调用(填充comboBox)被调用asynchronously
所以如果在对话框初始化之前,异步调用完成,则comboBox
正确显示;如果没有,那么您会看到您将新对象分配给属性UserTypeComboBoxList
(没有NotifyingPropertyChanged
),这将更改属性的引用,在这种情况下,XAML
绑定它的松散轨道属性引用,直到没有明确通知后面的属性定义正在进行的操作。
所以后来的Property定义代码是正确的。实现相同行为的另一种方法是使用以下(如果在程序执行期间可以更改绑定数据):
使用提供通知的ObservableCollection 何时将对象添加/删除到集合
而不是初始化属性的新对象
您的操作中UserTypeComboBoxList
,在构造函数中初始化,
和Clears
&amp; Add
每次更新的值。
答案 1 :(得分:0)
问题似乎是,一旦我创建了对话框并且调用了InitializeComponent(),C#就开始组装最终的View类(异步)并初始化绑定。因此,我对ViewModel属性的赋值要么是幸运的,要么是初始赋值,因为它尚未绑定,有时它将是已经绑定的空初始值的更新。
可以通过在将ViewModel分配给View之前初始化ViewModel来防止这种情况。