2014-05-02 34 views
2

說明混合使用任務和調度的暫停任務

我創建在WPF我自己的搜索控制。該控件是一個UserControl,其中包含一個帶搜索參數的區域(例如:搜索特定ID,名稱...)和一個顯示結果的GridView

在我的控制中,我有一個dependency property類型ICommand其中我綁定命令來執行我的搜索查詢。

public static readonly DependencyProperty SearchCommandProperty = 
      DependencyProperty.Register("SearchCommand", typeof(ICommand), typeof(SearchControl)); 

在某個窗口中使用我的控制:

<customControls:SearchControl SearchCommand="{Binding SearchItemsCommand}" 
          SearchResult="{Binding SearchResult}" /> 
  • SearchItemsCommand在我的ViewModel在哪裏可以找到我的搜索查詢的Command。 在這個命令中,你可以找到我的查詢來檢索結果。我的ICollection包含搜索查詢的結果。

代碼的命令

的視圖模型

private DelegateCommand searchItemsCommand; 
     public DelegateCommand SearchItemsCommand 
     { 
      get 
      { 
       if (this.searchItemsCommand== null) 
        this.searchItemsCommand= new DelegateCommand(this.SearchItemsCommandExecuted); 

       return this.searchItemsCommand; 
      } 
     } 

private ICollection<VoucherOverviewModel> voucherResults; 
private void SearchItemsCommandExecuted() 
     { 
      using (DbContext context = new DbContext()) 
      { 
       var query = (from v in context.Vouchers 
          join vt in context.VoucherTransactions on new 
                     { 
                      voucherID = v.VoucherID, 
                      type = VoucherTransactionType.Out 
                     } equals new 
                       { 
                        voucherID = vt.VoucherID, 
                        type = vt.Type 
                       } 
          join vtype in context.VoucherTypes on v.VoucherTypeID equals vtype.VoucherTypeID 
          join c in context.Customers on vt.CustomerID equals c.CustomerID 
          join pos in context.PointOfSales on v.PointOfSaleID equals pos.PointOfSaleID 
          select new VoucherOverviewModel() 
            { 
             PointOfSaleID = v.PointOfSaleID, 
             PointOfSaleName = pos.Name, 
             VoucherID = v.VoucherID, 
             VoucherCode = v.Code, 
             VoucherTypeID = v.VoucherTypeID, 
             VoucherTypeDescription = vtype.Code, 
             CustomerID = c.CustomerID, 
             CustomerName = c.Name, 
             Value = vt.Value, 
             UsedValue = context.VoucherTransactions 
                  .Where(x => x.VoucherID == v.VoucherID && 
                   x.Type == VoucherTransactionType.In) 
                  .Sum(x => x.Value), 
             CreateDate = vt.Date, 
             ValidFrom = v.ValidFrom, 
             ValidUntil = v.ValidUntil, 
             ParentVoucherID = v.ParentVoucherID, 
             Comment = v.Comment, 
            }); 

       foreach (ISearchParameter searchParameter in this.SearchParameters) 
       { 
        if (!searchParameter.Value.IsNullOrDefault()) 
        { 
         switch ((FilterVoucherParameterKey)searchParameter.Key) 
         { 
          case FilterVoucherParameterKey.CustomerID: 
           query = query.Where(x => x.CustomerID == (int)searchParameter.Value); 
           break; 
          case FilterVoucherParameterKey.VoucherID: 
           query = query.Where(x => x.VoucherCode.Contains((string)searchParameter.Value)); 
           break; 
          case FilterVoucherParameterKey.PointOfSale: 
           query = query.Where(x => x.PointOfSaleID == (byte)searchParameter.Value); 
           break; 
          case FilterVoucherParameterKey.Type: 
           query = query.Where(x => x.VoucherTypeID == (byte)searchParameter.Value); 
           break; 
         } 
        } 
       } 

      this.voucherResults = query.ToList(); 
      } 
     } 

定製控制

public static readonly DependencyProperty SearchCommandProperty = 
     DependencyProperty.Register("SearchCommand", typeof(ICommand), typeof(SearchControl)); 

public ICommand SearchCommand 
     { 
      get 
      { 
       return (ICommand)this.GetValue(SearchCommandProperty); 
      } 
      set 
      { 
       this.SetValue(SearchCommandProperty, value); 
      } 
     } 

這是我的依賴項屬性,以便我可以將SearchItemsCommand綁定到我的自定義控件。 然後我有另一個ICommand來執行綁定的命令,並在我的自定義控件中顯示加載元素。 當你點擊一個按鈕時,這個LocalSearchCommand將被執行。

private DelegateCommand localSearchCommand; 
public DelegateCommand LocalSearchCommand 
    { 
     get 
     { 
      if (this.localSearchCommand == null) 
       this.localSearchCommand = new DelegateCommand(this.LocalSearchCommandExecuted); 

      return this.localSearchCommand; 
     } 
    } 

    private void LocalSearchCommandExecuted() 
    { 
loadingElement.Visible = true; 
Task.Factory.StartNew(() => 
         { 
          this.Dispatcher.Invoke((Action)(() => this.SearchCommand.Execute(null))); 
         }) 
         .ContinueWith(t => 
         { 
          if (t.IsCompleted) 
          { 
           t.Dispose(); 
          } 
         }); 
    } 

問題

我希望在執行查詢時與用戶進行交互以顯示加載元素。爲了顯示這個元素,我必須將其設置爲visible。 現在的問題是,當我將它設置爲可見並想要執行搜索命令時,我的整個UI凍結。從數據庫中提取結果並在GridView中生成結果後,然後才顯示我的加載元素。我明白爲什麼發生這種情況,我試圖用Task來解決這個問題。

loadingElement.Visible = true; 
Task.Factory.StartNew(() => 
         { 
          this.Dispatcher.Invoke((Action)(() => this.SearchCommand.Execute(null))); 
         }) 
         .ContinueWith(t => 
         { 
          if (t.IsCompleted) 
          { 
           t.Dispose(); 
          } 
         }); 

我必須使用DispatcherTask執行SearchCommand,因爲它是由UI線程擁有。 但由於使用Dispatcher類,我遇到了和以前一樣的問題。我的加載元素僅在查詢已執行時才顯示,因爲Dispatcher在UI線程上執行搜索命令。 在不使用Dispatcher類的,它給了我下面的錯誤:

The calling thread cannot access this object because a different thread owns it. 

我上線這個錯誤:

return (ICommand)this.GetValue(SearchCommandProperty); 

即使有一個空SearchItemsCommandExecuted方法,發生錯誤。

我已經嘗試過

  • 我嘗試設置TaskTaskScheduler

    TaskScheduler.FromCurrentSynchronizationContext()

  • 我用了很多BeginInvokeInvoke組合。

  • 我試着在Task中設置加載元件Visibility

但是以上都沒有奏效。

我怎樣才能解決我的問題,從而使加載部件所示,當執行查詢時。我錯過了什麼明顯的東西?

在此先感謝!

Loetn

+0

爲什麼你需要在'Dispatcher'中調用'SearchCommand'? –

+0

@SriramSakthivel因爲它給了我下面的錯誤:'調用線程不能訪問這個對象,因爲一個不同的線程擁有它.'使用'Dispatcher'它執行命令,沒有它給了我這個錯誤。 – Loetn

+1

因此,您需要在'SearchCommand'中發佈代碼。這會有所幫助。 –

回答

0

我解決我的問題與this blog幫助。

我所要做的就是編輯我的Dependency property SearchCommandgetter,以便它使用Dispatcher

public ICommand SearchCommand 
     { 
      get 
      { 
       return (ICommand)this.Dispatcher.Invoke(
         System.Windows.Threading.DispatcherPriority.Background, 
         (DispatcherOperationCallback)delegate { return this.GetValue(SearchCommandProperty); }, 
         SearchCommandProperty); 

       // Instead of this 
       // return this.GetValue(SearchCommandProperty); 
      } 
      set 
      { 
       this.SetValue(SearchCommandProperty, value); 
      } 
     } 

這是我Command method

private void LocalSearchCommandExecuted() 
     { 
      this.loadingElement.Visible = true; 
      Task.Factory.StartNew(() => 
          { 
           this.SearchCommand.Execute(null); 
          }) 
          .ContinueWith(t => 
          { 
           if (t.IsCompleted) 
           { 
            this.Dispatcher.BeginInvoke((Action)(() => this.loadingElement.Visible= false)); 
            t.Dispose(); 
           } 
          }); 
     } 

感謝所有幫助!

1

的問題是,你正在創建一個新Task有線程池線程,但使用Dispatcher.Invoke,這在UI線程上運行你的命令,因此爲什麼你的UI被凍結。

您需要卸載你的SearchCommand工作提高到一個後臺線程,然後更新與UI線程部分繼續您的UI(不要嘗試更新內部SearchCommand您的UI):

那麼,就說明我的加載部件。我明白爲什麼會發生這種情況,我試圖用Task解決它。

loadingElement.Visible = true; 
Task.Factory.StartNew(() => 
{ 
    return this.SearchCommand.Execute(null); 
}) 
.ContinueWith(t => 
{ 
    MyUIElement = t.Result; // Update your UI here. 
}, TaskScheduler.FromCurrentSynchronizationContext()); 
+0

誰是'任務'?在繼續任務中總是完成。 –

+0

對不起,它是't'。 –

+0

仍然錯! 't.Result'從哪裏來? –

0

編輯:沒有捕獲你的第一個命令綁定到第二個。所以以下幾乎不會奏效。望着它...

編輯2:我以爲你想從你的viewmodel開始後臺操作。現在,我想不出另一種方式,而不是使您的loadItem.Visible屬性成爲依賴項屬性,將後臺操作移到您的視圖模型,從那裏指定綁定到loadingItem.Visible的屬性,並從您的目錄中刪除異步內容用戶控件。

要開始在後臺線程查詢,然後將結果以您的UI線程:

private void LocalSearchCommandExecuted(object obj) 
    { 
     //can also be your loadingItem. 
     VisibleElement.Visibility = Visibility.Collapsed; 
     //get the ui context 
     var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     Task.Factory.StartNew(() => 
     { 
      //do your query on the background thread 
      LongRunningOperation(); 
     }) 
        //this happens on the ui thread because of the second parameter scheduler 
           .ContinueWith(t => 
           { 

            if (t.IsCompleted) 
            { 
             VisibleElement.Visibility = Visibility.Visible; 
             //assign the result from the LongRunningOperation to your ui list 
             _list = new List<string>(_tempList); 
             //if you need to... 
             RaisePropertyChanged("SearchResults"); 
            } 
           }, scheduler); 

    } 

    private void LongRunningOperation() 
    { 
     //assign your result to a temporary collection 
     //if you do not do that you will get an exception: An ItemsControl is inconsistent with its items source 
     _tempList = new List<string>(); 
     for (int i = 0; i < 100; i++) 
     { 
      _tempList.Add("Test" + i); 
      Thread.Sleep(10); 
     } 
    }