2010-10-13 51 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}" />