WPF / MVVM在我点击窗口之前不会更新。
WPF / MVVM在我点击窗口之前不会更新。
我有一个面板上有一个按钮,用于触发外部相机的图像捕捉。捕捉可能需要几秒钟时间,所以我希望在捕捉过程中禁用按钮。我还希望能够在我的程序运行控制脚本时阻止用户捕捉。这是我的ViewModel类:
public class CameraControlViewModel : ViewModelBase { public CameraControlViewModel() { } public CameraControlViewModel(DataModel dataModel) : base(dataModel) { dataModel.PropertyChanged += DataModelOnPropertyChanged; _captureImageCommand = new RelayCommand(captureImage); _capturedImage = new BitmapImage(); _capturedImage.BeginInit(); _capturedImage.UriSource = new Uri("Images/fingerprint.jpg", UriKind.Relative); _capturedImage.CacheOption = BitmapCacheOption.OnLoad; _capturedImage.EndInit(); } public ICommand CaptureImageCommand { get { return _captureImageCommand; } } public bool CanCaptureImage { get { return !dataModel.IsScriptRunning && !_captureInProgress; } } public bool IsCaptureInProgress { get { return _captureInProgress; } set { if (_captureInProgress != value) { _captureInProgress = value; OnPropertyChanged("IsCaptureInProgress"); OnPropertyChanged("CanCaptureImage"); } } } public int PercentDone { get { return _percentDone; } set { if (_percentDone != value) { _percentDone = value; OnPropertyChanged("PercentDone"); } } } public BitmapImage CapturedImage { get { return _capturedImage; } } private void DataModelOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) { string property = propertyChangedEventArgs.PropertyName; if (property == "IsScriptRunning") { OnPropertyChanged("CanCaptureImage"); } OnPropertyChanged(property); } private void captureImage(object arg) { IsCaptureInProgress = true; PercentDone = 0; // TODO: remove this placeholder. new FakeImageCapture(this); // TODO (!) } internal void captureComplete() { IsCaptureInProgress = false; } // Remove this placeholder when we can take images. private class FakeImageCapture { CameraControlViewModel _viewModel; int _count; Timer _timer = new Timer(); public FakeImageCapture(CameraControlViewModel viewModel) { this._viewModel = viewModel; _timer.Interval = 50; _timer.Elapsed += TimerOnTick; _timer.Start(); } private void TimerOnTick(object sender, EventArgs eventArgs) { ++_count; if (_count <= 100) { _viewModel.PercentDone = _count; } else { Application.Current.Dispatcher.Invoke((Action)_viewModel.captureComplete); _timer.Stop(); _timer = null; _viewModel = null; } } } private readonly ICommand _captureImageCommand; private volatile bool _captureInProgress; private BitmapImage _capturedImage; private int _percentDone; }
这是按钮的XAML代码:
点击"捕捉"按钮没有问题。按钮被禁用,并且其他地方显示一个进度条,显示(当前模拟的)图像捕捉进度。然而,当捕捉完成后,即使我在`captureComplete()`方法中设置了`CanCaptureImage`属性,按钮仍然不会恢复到“启用”状态。只有当我在窗口中的任何地方单击时,它才会这样做。然而,按钮实际上是启用的,因为我可以再次点击它来触发第二次捕捉。
我尝试在`captureComplete()`方法中使用`CommandManager.InvalidateRequerySuggested()`,但没有效果。有什么想法吗?
问题的原因是没有正确使用CanExecute属性来启用/禁用按钮。解决方法是使用RelayCommand的CanExecute谓词,并在调用CommandManager.InvalidateRequerySuggested()时确保按钮正确启用/禁用。具体的修改代码如下:
public CameraControlViewModel(DataModel dataModel) : base(dataModel) { dataModel.PropertyChanged += DataModelOnPropertyChanged; _captureImageCommand = new RelayCommand(captureImage, captureImage_CanExecute); _capturedImage = new BitmapImage(); _capturedImage.BeginInit(); _capturedImage.UriSource = new Uri("Images/fingerprint.jpg", UriKind.Relative); _capturedImage.CacheOption = BitmapCacheOption.OnLoad; _capturedImage.EndInit(); } private bool captureImage_CanExecute(object arg) { return !dataModel.IsScriptRunning && !_captureInProgress; }
通过添加CanExecute谓词并返回CanCaptureImage的值,可以解决问题。这样做与之前的方法效果一样。谢谢!