内容摘要:
这是我在某个客户那边讲课的时候遇到一个小问题,在ViewModel中创建的一个Timer,并不会被自动停止,即便使用该ViewModel的View已经被关闭了。这个问题的原因在于Timer的特殊工作机制,它是运行在一个独立的工作线程的,除非明确地停止他,或者整个程序关闭了,它才会停止。这一讲中,我通过实例重现了这个问题,然后提供了一个可行的解决方法。
视频地址:
http://www.tudou.com/programs/view/uO4b2j0N4L8/
示例代码:
备注:该范例使用了MvvmLight作为MVVM框架,请自行安装
Model:
using System;
using System.Diagnostics;
using System.Linq;
namespace SilverlightApplicationSample
{
public class DataService
{
public static Customer[] GetCustomers()
{
Debug.WriteLine(string.Format("[{0}]正在调用数据服务",DateTime.Now));
var rnd = new Random();
return Enumerable.Range(1, rnd.Next(100)).Select(x => new Customer()
{
CompanyName = "Company " + x.ToString()
}).ToArray();
}
}
public class Customer
{
public string CompanyName { get; set; }
}
}
using System;
using System.Windows.Threading;
using GalaSoft.MvvmLight;
namespace SilverlightApplicationSample
{
/// <summary>
/// 使用MVVMLight实现的MVVM ViewModel
/// </summary>
public class CustomerWindowViewModel : ViewModelBase
{
/// <summary>
/// 这个方法也不会自动调用
/// </summary>
public override void Cleanup()
{
base.Cleanup();
timer.Stop();
}
DispatcherTimer timer = null;
public CustomerWindowViewModel()
{
//正常情况下的绑定
//Customers = DataService.GetCustomers();
//使用定时器调用服务
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (o, a) =>
{
Customers = DataService.GetCustomers();
};
timer.Start();
}
private Customer[] _Customers;
public Customer[] Customers
{
get { return _Customers; }
set
{
if (_Customers != value)
{
_Customers = value;
RaisePropertyChanged("Customers");
}
}
}
}
}
View:
<controls:ChildWindow x:Class="SilverlightApplicationSample.CustomerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Width="400"
Height="300"
Title="CustomerWindow"
xmlns:local="clr-namespace:SilverlightApplicationSample">
<controls:ChildWindow.DataContext>
<local:CustomerWindowViewModel></local:CustomerWindowViewModel>
</controls:ChildWindow.DataContext>
<Grid x:Name="LayoutRoot"
Margin="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Customers}"
DisplayMemberPath="CompanyName">
</ListBox>
<Button x:Name="CancelButton"
Content="Cancel"
Click="CancelButton_Click"
Width="75"
Height="23"
HorizontalAlignment="Right"
Margin="0,12,0,0"
Grid.Row="1" />
<Button x:Name="OKButton"
Content="OK"
Click="OKButton_Click"
Width="75"
Height="23"
HorizontalAlignment="Right"
Margin="0,12,79,0"
Grid.Row="1" />
</Grid>
</controls:ChildWindow>
Page:
using System.Windows;
using System.Windows.Controls;
namespace SilverlightApplicationSample
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new CustomerWindow();
window.Closed += (o, a) =>
{
var vm = window.DataContext as CustomerWindowViewModel;
vm.Cleanup();
};
window.Show();
}
}
}