我有一个应用程序,用户可以在其中选择一个 Excel 文件,该 Excel 文件是使用OleDbDataAdapter
另一个线程上的一个读取的,一旦读取完成,它会将我的 ViewModel 中的命令的 CanExecute 属性更新为 true,因此启用了“保存”按钮。
我的问题是,即使命令的 PropertyChanged 事件被引发并且 CanExecute 被评估为 true,UI 上的按钮也永远不会启用,直到用户执行某些操作与应用程序交互(单击它,选择一个文本框等) )
这是一些显示问题的示例代码。只需将其连接到绑定到SaveCommand
和的两个按钮SelectExcelFileCommand
,然后创建一个带有调用列的 excel 文件ID
来Sheet1
测试它。
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
_saveCommand = new RelayCommand(Save, () => (FileContents != null && FileContents.Count > 0));
// This runs after ReadExcelFile and it evaluates as True in the debug window,
// but the Button never gets enabled until after I interact with the application!
Debug.WriteLine("SaveCommand: CanExecute = " + _saveCommand.CanExecute(null).ToString());
return _saveCommand;
}
}
private void Save() { }
private ICommand _selectExcelFileCommand;
public ICommand SelectExcelFileCommand
{
get
{
if (_selectExcelFileCommand == null)
_selectExcelFileCommand = new RelayCommand(SelectExcelFile);
return _selectExcelFileCommand;
}
}
private async void SelectExcelFile()
{
var dlg = new Microsoft.Win32.OpenFileDialog();
dlg.DefaultExt = ".xls|.xlsx";
dlg.Filter = "Excel documents (*.xls, *.xlsx)|*.xls;*.xlsx";
if (dlg.ShowDialog() == true)
{
await Task.Factory.StartNew(() => ReadExcelFile(dlg.FileName));
}
}
private void ReadExcelFile(string fileName)
{
try
{
using (var conn = new OleDbConnection(string.Format(@"Provider=Microsoft.Ace.OLEDB.12.0;Data Source={0};Extended Properties=Excel 8.0", fileName)))
{
OleDbDataAdapter da = new OleDbDataAdapter("SELECT DISTINCT ID FROM [Sheet1$]", conn);
var dt = new DataTable();
// Commenting out this line makes the UI update correctly,
// so I am assuming it is causing the problem
da.Fill(dt);
FileContents = new List<int>() { 1, 2, 3 };
OnPropertyChanged("SaveCommand");
}
}
catch (Exception ex)
{
MessageBox.Show("Unable to read contents:\n\n" + ex.Message, "Error");
}
}
private List<int> _fileContents = new List<int>();
public List<int> FileContents
{
get { return _fileContents; }
set
{
if (value != _fileContents)
{
_fileContents = value;
OnPropertyChanged("FileContents");
}
}
}
编辑
我尝试使用 Dispatcher 以更高的优先级发送 PropertyChanged 事件,并将 PropertyChanged 调用移到异步方法之外,但是这两种解决方案都无法正确更新 UI。
如果我删除线程,或者在调度程序线程上启动从 Excel 读取的进程,它确实有效,但是这两种解决方案都会导致应用程序在读取 excel 文件时冻结。在后台线程上阅读的重点是用户可以在文件加载时填写表单的其余部分。这个应用程序使用的最后一个文件有近 40,000 条记录,并且使应用程序冻结了一两分钟。