2010-10-13 52 views
1

我有一個實現嚮導功能的NavigationWindow和一組代表這些步驟的Page對象。多線程視圖模型

每個頁面使用單獨的視圖模型。

其中一些視圖模型從其構造函數中產生工作線程。我終止這些線程時視圖模型處置(他們實現IDisposable)。此外,我將這些視圖模型分配給Pages的構造函數中的Pages'DataContext,並在Unloaded事件中處理DataContext。我這樣做是因爲我需要停止工作線程。

只要我不想在嚮導中導航,這一切都可以正常工作。但如果我這樣做,頁面,因爲它已經被卸載之前,沒有一個DataContext了,並沒有顯示任何東西。

因此,爲了解決這個問題,我不需要在Unloaded上處理DataContext,而是指示視圖模型在其擁有的窗口被加載/卸載時啓動/停止它的線程。我認爲我需要在視圖模型上引入一些方法(比如說Start()和Stop()),以實現這一點。並從Pages的Initialized和Unloaded處理程序中調用這些方法。

但這是醜陋的。它太複雜了,頁面需要知道啓動/停止線程,否則它將無法工作。所以我正在尋找正確的MVVM方式來完成這個任務。

請幫助 康斯坦丁

回答

1

這聽起來像問題是,視圖模型依賴於視圖的生命週期 - 這自動意味着視圖將被通知的狀態轉換的視圖模型。目標是找到這些變化的最佳表現形式。

第一步是重新組織框架的互動:Start()Stop()是必要的概念,我同意,確實感到沉重。相反,讓我們考慮一下我們作爲狀態機所做的事情。我會假設你的線程正在進行某種傾聽,所以我們的狀態可能是ListeningIdleComplete。它們將分別對應於正在運行的線程,暫停的線程和準備被終止的線程。

代表國家實實在在地是一個枚舉:

public enum ListenerState 
{ 
    Idle, 

    Listening, 

    Complete 
} 

您將宣佈這種類型的屬性在你的視圖模型:

public class ListenerModel : ViewModel 
{ 
    private ListenerState _state; 

    public ListenerState State 
    { 
     get { return _state; } 
     set 
     { 
      _state = value; 

      RaisePropertyChanged("State"); 
     } 
    } 
} 

然後,你會聽的狀態變化並更新線程以匹配:

protected override void OnPropertyChanged(PropertyChangedEventArgs e) 
{ 
    if(e.PropertyName == "State") 
    { 
     // Manipulate thread for current state 
    } 
} 

現在,視圖只需通知視圖的生命週期事件模型(一些視圖模型不可能不知道從視圖之外的任何其他方式):

private void OnLoaded(object sender, RoutedEventArgs e) 
{ 
    ((ListenerModel) this.DataContext).State = ListenerState.Listening; 
} 

如果你想完全脫鉤從視圖模型視圖,您可以創建一個依賴屬性的爲國家控制:

public static readonly DependencyProperty ListenerStateProperty = 
    DependencyProperty.Register("ListenerState", typeof(ListenerState), typeof(YourControl), null); 

public ListenerState ListenerState 
{ 
    get { return (ListenerState) GetValue(ListenerStateProperty); } 
    set { SetValue(ListenerStateProperty, value); } 
} 

然後,在Loaded處理程序設置該屬性,而不是引用視圖模型的:

private void OnLoaded(object sender, RoutedEventArgs e) 
{ 
    this.ListenerState = ListenerState.Listening; 
} 

最後,你WOU ld將屬性綁定到標記中的視圖模型的屬性:

<local:YourControl ListenerState="{Binding State, Mode=TwoWay}" />