按照作者说的,Jounce里的工作流也受到Caliburn的很大启发(Prism里没有类似概念)。
因为还没仔细研究过Caliburn,不知道Caliburn和CM的区别有多大。CM里面的工作流叫做Coroutine,整体使用和里面的Action息息相关。
相对来说来Jounce里工作流的定义和使用就比较简单。
此处涉及的类和接口有:
工作项的接口IWorkflow,定义了执行工作的方法Invoke和工作完成后的回调Invoked。
工作流控制台WorkflowController,定义了2个静态重载方法Begin用于启动工作流,可以传入的参数是IEnumerable<IWorkflow>或者IWorkflow(最终也会转化成IEnumerable<IWorkflow>)。
程序会调用第一个IWorkflow的Invoke方法,在回调Invoked后再调用第二个IWorkflow的Invoke,依次。
然后是几个实现了接口IWorkflow的类,其中
WorkflowAction(可以手动触发Invoked执行);
WorkflowBackgroundWorker(另起线程执行,线程完成后调用Invoked);
WorkflowDelay(传入TimeSpan,设置计时器,在计时完成后调用Invoked);
WorkflowEvent(传入注册事件的委托,在事件触发后调用Invoked);
WorkflowRoutedEvent(和WorkflowEvent类似,路由事件)。
来实际操作下看看,结构:
这次的操作代码都写在MainPage的后台代码中,看下Xaml:
<UserControl x:Class="JounceWorkflow.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Margin="5" Content="启动"
x:Name="StartButton"
Click="StartButton_Click"/>
<Button Margin="5" Content="继续Action"
x:Name="ContinueActionButton"
Click="ContinueActionButton_Click"
Visibility="Collapsed"/>
<Button Margin="5" Content="继续Event"
x:Name="ContinueEventButton"
Visibility="Collapsed"/>
</StackPanel>
<ProgressBar Margin="2" Grid.Row="1" x:Name="ProgressBar" Height="20"/>
<TextBox Margin="5" Text="点击启动按钮启动工作流。"
Grid.Row="2" x:Name="OutTextBox"/>
</Grid>
</UserControl>
后台代码:
[ExportAsView("MainPage", IsShell = true)] public partial class MainPage : UserControl { private WorkflowAction _workflowAction; public MainPage() { InitializeComponent(); this.ProgressBar.Maximum = int.MaxValue; _workflowAction = new WorkflowAction( () => { this.OutInformation("进入 WorkflowAction 执行。"); this.OutInformation("点击 继续Action按钮 继续。"); this.ContinueActionButton.Visibility = Visibility.Visible; }); } //启动工作流。 private void StartButton_Click(object sender, RoutedEventArgs e) { WorkflowController.Begin(this.Workflow(), ex => JounceHelper.ExecuteOnUI(() => MessageBox.Show(ex.Message))); } //提供工作流枚举。 private IEnumerable<IWorkflow> Workflow() { this.StartButton.IsEnabled = false; this.OutInformation("工作流已启动。"); this.OutInformation("开始 WorkflowAction。"); yield return _workflowAction; this.OutInformation("完成 WorkflowAction。"); this.OutInformation("开始 WorkflowBackgroundWorker。"); yield return new WorkflowBackgroundWorker( backgroundWorker => { var mod = (int)Math.Ceiling(int.MaxValue / 100); for (var x = 0; x < int.MaxValue; x++) { if (x % mod == 0) backgroundWorker.ReportProgress(x / mod, x); } }, (backgroundWorker, progressChangedEventArgs) => { this.ProgressBar.Value = (int)progressChangedEventArgs.UserState; }); this.OutInformation("完成 WorkflowBackgroundWorker。"); this.OutInformation("开始 WorkflowDelay,延迟5秒。"); yield return new WorkflowDelay(TimeSpan.FromSeconds(5)); this.OutInformation("完成 WorkflowDelay。"); this.OutInformation("开始 WorkflowRoutedEvent。"); yield return new WorkflowRoutedEvent( () => { this.OutInformation("进入 WorkflowRoutedEvent 执行。"); this.OutInformation("点击 继续Event 按钮继续。"); this.ContinueEventButton.Visibility = Visibility.Visible; }, routedEventHandler => this.ContinueEventButton.Click += routedEventHandler, routedEventHandler => this.ContinueEventButton.Click -= routedEventHandler, routedEventArgs => { this.ContinueEventButton.Visibility = Visibility.Collapsed; }); this.OutInformation("完成 WorkflowRoutedEvent。"); this.OutInformation("完成整个工作流。"); this.StartButton.IsEnabled = true; } private void ContinueActionButton_Click(object sender, RoutedEventArgs e) { this.ContinueActionButton.Visibility = Visibility.Collapsed; _workflowAction.Invoked(); } private void OutInformation(string information) { this.OutTextBox.Text += "\r\n" + information; } }
为了便于理解,很多方法都直接用匿名委托来写了,yield这东西平常真的用的很少,这里刚好练练手。
还可以看到,输出方式的多样性,可以直接写在定义工作流的方法里,或者传入参数的Action委托里,或者回调方法里。
本例中的WorkflowAction和WorkflowRoutedEvent虽然都用Button.Click来控制继续执行,但是机制是完全不一样的。
其他的就不解释了。
PS:
1.虽然WorkflowController提供了Public的构造函数,但是并没有提供Public的实例启动方法,因此就算在外部实例了WorkflowController也无法启动工作流执行。这种Public的方法无实际用处的情况还在另外几个类里出现了。
2.可以留意下另起线程执行时,UI线程是否灵活机动。
3.在实际应用中尝试自己实现下IWorkflow接口。