2010-08-02 8 views
5

Tôi có một ứng dụng WPF chỉ đơn giản chứa một nút và một hộp văn bản để hiển thị một số đầu ra. Khi người dùng nhấp vào nút, một chuỗi bắt đầu vô hiệu hóa Nút, in nội dung vào hộp văn bản đầu ra, sau đó luồng dừng lại (tại thời điểm đó tôi muốn Nút được bật lại).WPF: Sự cố khi kiểm soát trạng thái đã bật/tắt của nút bằng cách sử dụng Lệnh ràng buộc và một chủ đề

Ứng dụng dường như vô hiệu hóa Nút đúng cách, cũng như cập nhật Hộp văn bản đúng cách. Tuy nhiên, nó luôn không thể kích hoạt lại Nút đúng cách khi chuỗi hoàn tất! Bất cứ ai có thể cho tôi biết những gì tôi đang làm sai?

Dưới đây là một đoạn XAML của tôi:

<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="*"/> 
    </Grid.RowDefinitions> 
    <Button Grid.Row="0" HorizontalAlignment="Center" Command="{Binding ExecuteCommand}">E_xecute</Button> 
    <Label Grid.Row="1" Content="Output Window:" HorizontalAlignment="Left"/> 
    <TextBlock Grid.Row="2" Text="{Binding Output}"/> 
</Grid> 

Đây là mã ViewModel của tôi (Tôi đang sử dụng thiết kế MVVM Josh Smith):

public class WindowViewModel : ViewModelBase 
{ 
    private bool _threadStopped; 
    private RelayCommand _executeCommand; 
    private string _output; 

    public WindowViewModel() 
    { 
     _threadStopped = true; 
    } 

    public string Output { get { return _output; } set { _output = value; OnPropertyChanged("Output"); } } 

    public ICommand ExecuteCommand 
    { 
     get 
     { 
      if (_executeCommand == null) 
      { 
       _executeCommand = new RelayCommand(p => this.ExecuteThread(p), p => this.CanExecuteThread); 
      } 
      return _executeCommand; 
     } 
    } 

    public bool CanExecuteThread 
    { 
     get 
     { 
      return _threadStopped; 
     } 
     set 
     { 
      _threadStopped = value; 
     } 
    } 

    private void ExecuteThread(object p) 
    { 
     ThreadStart ts = new ThreadStart(ThreadMethod); 
     Thread t = new Thread(ts); 
     t.Start(); 
    } 

    private void ThreadMethod() 
    { 
     CanExecuteThread = false; 
     Output = string.Empty; 
     Output += "Thread Started: Is the 'Execute' button disabled?\r\n"; 
     int countdown = 5000; 

     while (countdown > 0) 
     { 
      Output += string.Format("Time remaining: {0}...\r\n", countdown/1000); 
      countdown -= 1000; 
      Thread.Sleep(1000); 
     } 
     CanExecuteThread = true; 
     Output += "Thread Stopped: Is the 'Execute' button enabled?\r\n"; 
    } 
} 

Trả lời

1

Bạn sẽ cần phải giúp WPF biết rằng trạng thái thực thi của lệnh đã thay đổi. Cách đơn giản để làm điều này là:

CommandManager.InvalidateRequerySuggested() 

bên CanExecuteThread:

set 
{ 
    _threadStopped = value; 
    CommandManager.InvalidateRequerySuggested() 
} 

EDIT (bây giờ mà tôi có thời gian): các vấn đề thực tế là khả năng mà bạn không thông báo khi CanExecuteThread thay đổi sở hữu . Cần nâng PropertyChanged để cho WPF để phát hiện sự thay đổi:

public bool CanExecuteThread 
{ 
    get { return _threadStopped; } 
    set 
    { 
     if (_threadStopped != value) 
     { 
      _threadStopped = value; 
      this.OnPropertyChanged(() => this.CanExecuteThread); 
     } 
    } 
} 

Ở trên giả định lớp cơ sở ViewModel của bạn có một phương pháp OnPropertyChanged.

Điều đó nói rằng, tôi cũng muốn chỉ ra rằng bạn có thể đơn giản hóa mọi thứ bằng cách đơn giản bằng cách sử dụng BackgroundWorker:

public class WindowViewModel : ViewModel 
{ 
    private readonly BackgroundWorker backgroundWorker; 

    public WindowVieWModel() 
    { 
     backgroundWorker = new BackgroundWorker(); 
     backgroundWorker.DoWork += delegate 
     { 
      // do work here (what's currently in ThreadMethod) 
     }; 
     backgroundWorker.RunWorkerCompleted += delegate 
     { 
      // this will all run on the UI thread after the work is done 
      this.OnPropertyChanged(() => this.CanExecuteThread); 
     }; 
    } 

    ... 

    public bool CanExecuteThread 
    { 
     get { !this.backgroundWorker.IsBusy; } 
    } 

    private void ExecuteThread(object p) 
    { 
     // this will kick off the work 
     this.backgroundWorker.RunWorkerAsync(); 

     // this property will have changed because the worker is busy 
     this.OnPropertyChanged(() => this.CanExecuteThread); 
    } 
} 

Bạn có thể cấu trúc lại thêm này là thậm chí đẹp hơn, nhưng bạn sẽ có được ý tưởng.

+0

Tôi đã đặt dòng mã như bạn đã đề xuất, nhưng Nút vẫn xuất hiện bị tắt khi chuỗi hoàn tất. Chỉ khi tôi tập trung vào thứ gì đó trong Cửa sổ (tức là nhấp chuột hoặc phím tắt) thì nút đó đã được bật lại. (Chú ý: Tuy nhiên, tôi đã nhìn thấy điều này ngay cả trước khi giải pháp được đề xuất của bạn). Bất kỳ đề xuất nào khác sẽ được đánh giá cao. –

+0

Hãy thử thực hiện trên chuỗi giao diện người dùng qua cuộc gọi Dispatcher.Invoke –

+0

Cảm ơn, Kent! Điều đó đã làm nó! Tôi sẽ đăng câu trả lời cho người khác để xem giải pháp. –

0

Đây là câu trả lời, theo đề xuất của Kent Boogaart và nó hoạt động. Về cơ bản, tôi đã phải gọi CommandManager.InvalidateRequerySuggested trên thread UI bằng cách đặt nó trong một cuộc gọi Dispatcher gọi. Cũng lưu ý rằng tôi đã có thể loại bỏ Set accessor trên thuộc tính CanExecuteThread, vì nó không còn cần thiết với giải pháp này nữa. Cảm ơn, Kent!

public class WindowViewModel : ViewModelBase 
{ 
    private bool _threadStopped; 
    private RelayCommand _executeCommand; 
    private string _output; 
    private Dispatcher _currentDispatcher; 
    public WindowViewModel() 
    { 
     _threadStopped = true; 
     _currentDispatcher = Dispatcher.CurrentDispatcher; 
    } 

    public string Output { get { return _output; } set { _output = value; OnPropertyChanged("Output"); } } 

    public ICommand ExecuteCommand 
    { 
     get 
     { 
      if (_executeCommand == null) 
      { 
       _executeCommand = new RelayCommand(p => this.ExecuteThread(p), p => this.CanExecuteThread); 
      } 
      return _executeCommand; 
     } 
    } 

    private delegate void ZeroArgDelegate(); 

    public bool CanExecuteThread 
    { 
     get 
     { 
      return _threadStopped; 
     } 
    } 

    private void ExecuteThread(object p) 
    { 
     ThreadStart ts = new ThreadStart(ThreadMethod); 
     Thread t = new Thread(ts); 
     t.Start(); 
    } 

    private void ThreadMethod() 
    { 
     _threadStopped = false; 
     Output = string.Empty; 
     Output += "Thread Started: Is the 'Execute' button disabled?\r\n"; 
     int countdown = 5000; 

     while (countdown > 0) 
     { 
      Output += string.Format("Time remaining: {0}...\r\n", countdown/1000); 
      countdown -= 1000; 
      Thread.Sleep(1000); 
     } 
     _threadStopped = true; 
     _currentDispatcher.BeginInvoke(new ZeroArgDelegate(resetButtonState), null); 
     Output += "Thread Stopped: Is the 'Execute' button enabled?\r\n"; 
    } 

    private void resetButtonState() 
    { 
     CommandManager.InvalidateRequerySuggested(); 
    } 
}