內容摘要:
這是我在某個客戶那邊講課的時候遇到一個小問題,在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; } } }
ViewModel:
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(); } } }