2017-06-02 29 views
0

我有一個程序,其中有一個加載圖像,當發生長時間的行爲時。我想使用一個ContentControl,該行爲包含Button,或者顯示加載Image。我已將Trigger設置爲IsLoading屬性,因此它可以交換Content如何等待觸發器在WPF中採取行動

查看:

<Window x:Class="UIHangsTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:UIHangsTest" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.DataContext> 
     <local:VM /> 
    </Window.DataContext> 
    <Grid> 
     <ContentControl> 
      <ContentControl.Style> 
       <Style TargetType="ContentControl" > 
        <Setter Property="Content"> 
         <Setter.Value> 
          <Button Content="shiny disappearing button" Command="{Binding DoCommand}" IsDefault="True" /> 
         </Setter.Value> 
        </Setter> 
        <Style.Triggers> 
         <DataTrigger Binding="{Binding IsLoading}" Value="True" > 
          <Setter Property="Content"> 
           <Setter.Value> 
            <Image /> 
           </Setter.Value> 
          </Setter> 
         </DataTrigger> 
        </Style.Triggers> 
       </Style> 
      </ContentControl.Style> 
     </ContentControl> 
    </Grid> 
</Window> 

使用此,如果您需要運行在UI(調度)線程的長時間運行的任務。
視圖模型:

namespace UIHangsTest 
{ 
    using System; 
    using DevExpress.Mvvm; 
    using System.Threading; 
    using System.Windows; 
    using System.Windows.Threading; 

    public class VM: ViewModelBase 
    { 
     public VM() 
     { 
      DoCommand = new DelegateCommand(Do,() => true); 
      IsLoading = false; 
     } 

     private void Do() 
     { 
      // I have set some content control's trigger to show a waiting symbol if IsLoading is true. 
      IsLoading = true; 
      var handle = new ManualResetEventSlim(false); 
      // shows 1.     
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
      // I'd like to wait for the UI to complete the swapping of ContentControl's Content 
      Application.Current.Dispatcher.Invoke(new Action(() => { 
       handle.Set(); 
       // shows 1 
       Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
       }), DispatcherPriority.Background); 

      handle.Wait(); 
      // for 1 second, the empty image should be visible, but it's the half clicked Button. 
      Thread.Sleep(1000); 
      IsLoading = false; 
     } 

     public DelegateCommand DoCommand { get; private set; } 

     // not working: 
     // public bool IsLoading { get; set; } 

     // working: 
     public bool IsLoading 
     { 
      get 
      { 
       return _isLoading; 
      } 
      set 
      { 
       _isLoading = value; 
       RaisePropertyChanged("IsLoading"); 
      } 
     } 
    } 
} 

我不知道,那DO命令將在UI線程上運行。而且,如果我更改了代碼,那麼在後臺線程上運行長效操作,如果您沒有更改屬性(_'!l),它仍然不會將Content更改爲空Image

用這個,如果你能在一些後臺線程中運行長時間運行的任務:

private void Do() 
{ 
    IsLoading = true; 
    // shows 1 
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
    Task.Factory.StartNew(() => 
    { 
     // shows 5 
     Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
     //this operation is performed on a background thread... 
     Thread.Sleep(1000); 
    }).ContinueWith(task => 
    { 
     IsLoading = false; 
    }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

編輯:

第一種方法的工作原理。因此,如果您只更改IsLoading設置爲RaisePropertyChanged("IsLoading"),那麼它會正常工作。無需將長時間運行的任務放在後臺線程(我首先想避免的)中。在我的具體情況下,Thread.Sleep(1000)是一個長時間運行的進程,它還需要在UI(調度程序)線程上運行,因爲可能會通過創建Dialog窗口來向用戶通知異常情況。

回答

2

你應該在這種情況下執行長時間運行的操作,即Thread.Sleep,在後臺線程:

private void Do() 
{ 
    IsLoading = true; 
    Task.Factory.StartNew(() => 
    { 
     //this operation is performed on a background thread... 
     Thread.Sleep(1000); 
    }).ContinueWith(task => 
    { 
     IsLoading = false; 
    }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

的UI線程不能睡眠和更新您在同一時間視圖。

您還需要在IsLoading setter方法,以提高PropertyChanged事件:

private bool _isLoading; 
public bool IsLoading 
{ 
    get { return _isLoading; } 
    set { _isLoading = value; RaisePropertyChanged(); } 
} 
+0

感謝您的回答@ MM8,但'Image'沒有顯示。我更接近解決方案。請檢查更新的問題 – ntohl

+1

您還需要在IsLoading屬性的setter中引發PropertyChanged事件。看到我編輯的答案。 – mm8

+0

哦。我真笨。謝謝。我從最小的例子中跳過了一些簡單的代碼 – ntohl