ユーザーが Excel ファイルを選択できるアプリがあり、その Excel ファイルはOleDbDataAdapter
別のスレッドを使用して読み取られ、読み取りが完了すると、ViewModel の Command の CanExecute プロパティが true に更新され、[保存] ボタンが有効になります。
私の問題は、コマンドの PropertyChanged イベントが発生し、CanExecute が true として評価されたとしても、ユーザーがアプリケーションと対話するために何かを行う (クリックする、テキストボックスを選択するなど) まで、UI のボタンが有効にならないことです。 )
問題を示すサンプル コードを次に示します。SaveCommand
とにバインドされた 2 つのボタンに接続し、テストするために呼び出され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 レコードがあり、アプリケーションが 1、2 分間フリーズしました。