2015-02-11 40 views
1

我將可觀察集合綁定到數據網格。通過異步調用從服務器獲取集合。集合模型包含一個名爲'BackgroundBrush'的類型爲'System.Windows.Media.Brush'的屬性,該屬性綁定到數據網格中模板列的背景顏色。 brush屬性可以是SolidColorBrush或LinearGradientBrush,取決於應用於該屬性的業務邏輯。類型爲Brush的綁定屬性在異步調用時拋出異常

將數據呈現到數據網格時,應用程序會拋出一個像這樣的異常「必須在DependencyObject的同一線程上創建DependencySource」。在調試問題

  1. 的問題是 '背景' 屬性

    事發現。註釋掉這個屬性綁定並使異步調用可以正常工作。

  2. 使服務調用同步工作正常,但我需要這是一個異步的。

  3. 請在Application.Current.Dispatcher.Invoke服務調用沒有任何區別

下面的示例應用程序代碼

型號

public class Model 
{ 
    public string Name { get; set; } 

    public string Email { get; set; } 

    public string Address { get; set; } 

    public Brush BackgroundBrush { get; set; } 
} 

視圖模型

private ObservableCollection<Model> _dataCollection; 

public ObservableCollection<Model> DataCollection 
{ 
    get { return _dataCollection; } 
    set 
    { 
     _dataCollection = value; 
     RaisePropertyChanged(() => DataCollection); 
    } 
} 

public RelayCommand LoadCommand { get; private set; } 

private async Task LoadData() 
{ 
    var list = await Task.Run(() => GetData()); 
    DataCollection = new ObservableCollection<Model>(list); 

} 

private ObservableCollection<Model> GetData() 
{ 
    return new ObservableCollection<Model>() 
    { 
     new Model() 
     { 
      Address = "a", 
      Email = "2", 
      Name = "3", 
      BackgroundBrush = new SolidColorBrush(Colors.SaddleBrown) 
     } 
    }; 
} 

查看

<Grid x:Name="LayoutGrid"> 
    <DataGrid ItemsSource="{Binding DataCollection}" AutoGenerateColumns="False"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
        <Border Background="{Binding BackgroundBrush}"> 
         <TextBlock Text="{Binding Name}"></TextBlock> 
         </Border> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
      </DataGridTemplateColumn> 
     </DataGrid.Columns> 
    </DataGrid> 
</Grid> 
+0

不要發佈您的代碼爲圖像。改用恰當的代碼塊。 – Clemens 2015-02-11 11:04:11

+0

@Clemens - 完成:) – Dennis 2015-02-11 11:08:07

回答

2

System.Media.BrushDependencyObject,並因此需要在Dispatcher線程創建的。

ObservableCollection經由INotifyCollectionChanged使用通知給UI,以使它的Observer模式實現,這意味着它也需要在Dispatcher螺紋構成。您不能從另一個線程(即異步)加載ObservableCollection的內容,而無需編寫自定義實現以在正確的線程上引發通知。

編輯:

爲您解決問題 - 建立在構造函數中ObservableCollection永不覆蓋從另一個線程的性能基準。

LoadData()GetData()如下:

private async Task LoadData() 
    { 
     var list = await Task.Run(() => GetData()); 
     list.ForEach(item => Dispatcher.Invoke(() => 
     { 
      DataCollection.Add(item); 
     })); 

    } 

    private List<Model> GetData() 
    { 
     var modelObject = 
      new Model() 
      { 
       Address = "a", 
       Email = "2", 
       Name = "3", 
      }; 
     Dispatcher.Invoke(() => 
     { 
      modelObject.BackgroundBrush = new SolidColorBrush(Colors.SaddleBrown); 
     }); 

     return new List<Model>(){ modelObject }; 
    } 
+0

謝謝。有什麼解決辦法嗎? – Dennis 2015-02-11 11:18:50

+0

請參閱上面的編輯。 – toadflakz 2015-02-11 11:36:35

+0

其工作..謝謝toadflakz..One更多的情況下考慮在現實世界的情況。在我原來的解決方案中,GetData()方法是一個業務層調用,它是一個不同的項目(除WPF項目外)。在這種情況下,分派器不起作用。對?。所以唯一的解決方法是將該邏輯操作從業務移動到視圖模型,並在主線程中完成。右圖? – Dennis 2015-02-11 11:51:22

1

這是因爲一切都內await Task.Run運行在不同的線程。所以您無法更改其中的綁定源DataCollection。由於ObservableCollection會觸發事件來更新綁定目標,並且它不會允許來自不同線程的更改。

所以你GetData函數返回一個正常的集合:

private IEnumerable<Model> GetData() 
{ 
    return new List<Model>() 
    { 
     new Model(){...} 
    }; 
} 

另一個問題是Brush。您的模型(線程安全)應該使用Color,並且您的viewModel(可綁定)應該使用Brush。所以你應該爲你的模型添加一個Color屬性,這個屬性可以在GetData的異步調用中設置。類似這樣的:

private Color _color; 
public Color Color 
{ 
    get{ return _color; } 
    set 
    { 
     _color = value; 
     Dispatcher.Invoke(()=>Brush = new SolidColorBrush(Color)); 
    } 
} 
public Brush Brush { get; set; } 

或者您不綁定到Model並遵循標準MVVM模式。

對於涉及依賴項屬性的部分或任何需要從調度程序調用的綁定源。例如

Dispatcher.Invoke(() => aThreadSafeFunction()); 

然而,在這種情況下,你可以先閱讀整個列表,然後將其轉換爲一個ObservableCollection:

private async Task LoadData() 
{ 
    //load thread-safe data asynchronously 
    var list = await Task.Run(() => GetData()); 
    //set binding source in the same thread 
    DataCollection = new ObservableCollection<Model>(list); 
} 
+0

沒有運氣。拋出相同的異常「必須在DependencyObject的同一個線程上創建DependencySource」。 – Dennis 2015-02-11 11:01:15

+0

是的,這是正確的。我添加了一些更多的信息。 – Bijan 2015-02-11 11:05:26

+0

謝謝。但刷子可以是SolidColorBrush或LinearGradientBrush,它不能用Color.Right?替代。 實際上,筆刷是通過模型數據從業務邏輯生成的。所以我必須將這個邏輯操作從業務移動到查看模型並在主線程中完成。右圖? – Dennis 2015-02-11 11:26:25

相關問題