今年由于项目原因需要用到 WF+WCF+WPF 因此准备认真学习下WF,准备每天写下一篇博文作为自己学习WF的一个反馈。
首先,了解下这三个东西的来源:
WF的全称是Windows Workflow Foundation ,最早在2004年的时候,那时微软推出了一个CTP版的开发包,WinFX.在WinFX中提供了三个内容: Avalon, Indigo, WinOE;与微软很多的技术一样,WinFX在Bata2后就没有后续了. WinFX在2006年以Net 3.0 的方式正式发布了,在NET 3.0 中:
Avalon成为了Windows Presentation Foundation 简称为WPF
Indigo成为了 Windows Communication Foundation 简称为WCF
WinOE 成为了 Windows Workflow Foundation 简称为WF
后来, 微软基于WPF推出了WFP/E, WFP/E就是silverlight的前身.
以上为这三个东西的起源。按照微软的设计者三者本来就是一体的,下面我们开始第一个WF的尝试:
本例是已这篇文章为蓝本的 : http://www.cnblogs.com/zhuqil/archive/2010/05/14/Hello-World.html
接下来我们做点小小的改动:
用户输入帐号密码,通过工作流请求服务器取得验证结果
代码如下:
<Activity mc:Ignorable="sap sap2010 sads" x:Class="WF.Business.Activity1" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mca="clr-namespace:Microsoft.CSharp.Activities;assembly=System.Activities" xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel" xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <x:Members> <x:Property Name="returnValue" Type="OutArgument(x:String)" /> <x:Property Name="UserId" Type="InArgument(x:String)" /> <x:Property Name="UserPwd" Type="InArgument(x:String)" /> </x:Members> <sap2010:ExpressionActivityEditor.ExpressionActivityEditor>C#</sap2010:ExpressionActivityEditor.ExpressionActivityEditor> <sap2010:WorkflowViewState.IdRef>WF.Business.Activity1_1</sap2010:WorkflowViewState.IdRef> <TextExpression.NamespacesForImplementation> <sco:Collection x:TypeArguments="x:String"> <x:String>System</x:String> <x:String>System.Collections.Generic</x:String> <x:String>System.Data</x:String> <x:String>System.Linq</x:String> <x:String>System.Text</x:String> <x:String>System.ServiceModel.Activities</x:String> </sco:Collection> </TextExpression.NamespacesForImplementation> <TextExpression.ReferencesForImplementation> <sco:Collection x:TypeArguments="AssemblyReference"> <AssemblyReference>Microsoft.CSharp</AssemblyReference> <AssemblyReference>System</AssemblyReference> <AssemblyReference>System.Activities</AssemblyReference> <AssemblyReference>System.Core</AssemblyReference> <AssemblyReference>System.Data</AssemblyReference> <AssemblyReference>System.Runtime.Serialization</AssemblyReference> <AssemblyReference>System.ServiceModel</AssemblyReference> <AssemblyReference>System.ServiceModel.Activities</AssemblyReference> <AssemblyReference>System.Xaml</AssemblyReference> <AssemblyReference>System.Xml</AssemblyReference> <AssemblyReference>System.Xml.Linq</AssemblyReference> <AssemblyReference>mscorlib</AssemblyReference> <AssemblyReference>WF.Business</AssemblyReference> </sco:Collection> </TextExpression.ReferencesForImplementation> <Sequence sap2010:WorkflowViewState.IdRef="Sequence_1"> <Sequence.Variables> <Variable x:TypeArguments="p:CorrelationHandle" Name="_handle1" /> </Sequence.Variables> <WriteLine sap2010:WorkflowViewState.IdRef="WriteLine_1" Text="开始请求服务器!" /> <p:Send x:Name="__ReferenceID0" sap2010:WorkflowViewState.IdRef="Send_1" OperationName="Login" ServiceContractName="IService1"> <p:Send.CorrelationInitializers> <p:RequestReplyCorrelationInitializer> <p:RequestReplyCorrelationInitializer.CorrelationHandle> <InArgument x:TypeArguments="p:CorrelationHandle"> <mca:CSharpValue x:TypeArguments="p:CorrelationHandle">_handle1</mca:CSharpValue> </InArgument> </p:RequestReplyCorrelationInitializer.CorrelationHandle> </p:RequestReplyCorrelationInitializer> </p:Send.CorrelationInitializers> <p:Send.Endpoint> <p:Endpoint AddressUri="http://localhost:8001/Service1"> <p:Endpoint.Binding> <p:BasicHttpBinding Name="basicHttpBinding" /> </p:Endpoint.Binding> </p:Endpoint> </p:Send.Endpoint> <p:SendParametersContent> <InArgument x:TypeArguments="x:String" x:Key="userId"> <mca:CSharpValue x:TypeArguments="x:String">UserId</mca:CSharpValue> </InArgument> <InArgument x:TypeArguments="x:String" x:Key="userPwd"> <mca:CSharpValue x:TypeArguments="x:String">UserPwd</mca:CSharpValue> </InArgument> </p:SendParametersContent> </p:Send> <p:ReceiveReply Request="{x:Reference __ReferenceID0}" DisplayName="ReceiveReplyForSend" sap2010:WorkflowViewState.IdRef="ReceiveReply_2"> <p:ReceiveParametersContent> <OutArgument x:TypeArguments="x:String" x:Key="LoginResult"> <mca:CSharpReference x:TypeArguments="x:String">returnValue</mca:CSharpReference> </OutArgument> </p:ReceiveParametersContent> </p:ReceiveReply> <WriteLine sap2010:WorkflowViewState.IdRef="WriteLine_2" Text="工作流结束!" /> <sads:DebugSymbol.Symbol>dzBEOlxTYWtlXERhdGFDb250ZXh0U2FtcGxlXFdGLkJ1c2luZXNzXExvZ2luLnhhbWwLLgNWDgIBATIFMlACAR8zBUwOAgEPTQVTFgIBBFQFVE4CAQIyQzJNAgEgSQtJUAIBGjgPOGACARVGC0ZPAgEQUAtQXAIBBVRDVEsCAQM=</sads:DebugSymbol.Symbol> </Sequence> <sap2010:WorkflowViewState.ViewStateManager> <sap2010:ViewStateManager> <sap2010:ViewStateData Id="WriteLine_1" sap:VirtualizedContainerService.HintSize="257,63" /> <sap2010:ViewStateData Id="Send_1" sap:VirtualizedContainerService.HintSize="257,94" /> <sap2010:ViewStateData Id="ReceiveReply_2" sap:VirtualizedContainerService.HintSize="257,94" /> <sap2010:ViewStateData Id="WriteLine_2" sap:VirtualizedContainerService.HintSize="257,63" /> <sap2010:ViewStateData Id="Sequence_1" sap:VirtualizedContainerService.HintSize="279,558"> <sap:WorkflowViewStateService.ViewState> <scg:Dictionary x:TypeArguments="x:String, x:Object"> <x:Boolean x:Key="IsExpanded">True</x:Boolean> </scg:Dictionary> </sap:WorkflowViewStateService.ViewState> </sap2010:ViewStateData> <sap2010:ViewStateData Id="WF.Business.Activity1_1" sap:VirtualizedContainerService.HintSize="319,638" /> </sap2010:ViewStateManager> </sap2010:WorkflowViewState.ViewStateManager> </Activity>
WPF:
对原有界面添加一个TextBox、PasswordBox(PS:这里我使用了Win8的界面风格)
xaml代码:
1 <m2:MetroWindow x:Class="WPF.HelloWord.LoginMetroWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:m2="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" 5 xmlns:ctrl="clr-namespace:PT.Desktop.Controls;assembly=PT.Desktop" 6 xmlns:prism="http://www.codeplex.com/prism" 7 xmlns:tb="http://www.hardcodet.net/taskbar" 8 xmlns:helloWord="clr-namespace:WPF.HelloWord" 9 Title="请登陆" Height="300" Width="309" WindowStartupLocation="CenterScreen"> 10 <Window.BindingGroup> 11 <BindingGroup></BindingGroup> 12 </Window.BindingGroup> 13 <Grid Margin="0,27,0,-30"> 14 <Grid.ColumnDefinitions> 15 <ColumnDefinition Width="150"></ColumnDefinition> 16 </Grid.ColumnDefinitions> 17 <Grid.RowDefinitions> 18 <RowDefinition Height="Auto" /> 19 <RowDefinition Height="Auto" /> 20 <RowDefinition /> 21 <RowDefinition Height="Auto" /> 22 </Grid.RowDefinitions> 23 <Label Content="用户名:" VerticalAlignment="Center" Grid.Row="0" Grid.Column="0" Margin="0,17.013,8,8.987" Grid.RowSpan="2"></Label> 24 <TextBox Name="TxtUserid" Text="{Binding UserName,Mode=TwoWay, UpdateSourceTrigger=Explicit}" Height="23" Margin="142,17,-142,8.837" Grid.RowSpan="2"/> 25 <Label Content="密 码:" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" Margin="0,17.039,0,204.204" HorizontalAlignment="Left" Width="142" Grid.RowSpan="2" /> 26 <PasswordBox Name="PwdUser" helloWord:PasswordBoxBindingHelper.IsPasswordBindingEnabled="True" 27 helloWord:PasswordBoxBindingHelper.BindedPassword="{Binding UserPwd, Mode=TwoWay, UpdateSourceTrigger=Explicit}" 28 Grid.Row="1" Height="23" Margin="142,18.6,-142,202.48" Grid.RowSpan="2" 29 PasswordChanged="PasswordBox_OnPasswordChanged"/> 30 31 <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,28.5,-218,142.663" Grid.Row="2" Width="300" Height="50"> 32 <Button x:Name="BtnCancel" Content="取 消 " Width="75" Height="23" Click="BtnCancel_OnClick" Margin="0,12.5"/> 33 <Button x:Name="BtnOk" Content="登 陆" IsDefault="True" Width="75" Height="23" Click="BtnOk_OnClick" Margin="0,12.5"/> 34 </StackPanel> 35 </Grid> 36 37 </m2:MetroWindow>
对于BtnOK的点击事件代码如下
1 private void BtnOk_OnClick(object sender, RoutedEventArgs e) 2 { 3 Dictionary<string,object> dict =new Dictionary<string,object>(); 4 dict.Add("UserId",this.TxtUserid.Text); 5 dict.Add("UserPwd",this.PwdUser.Password); 6 IDictionary<string, object> results = WorkflowInvoker.Invoke(new Activity1(),dict ); 7 8 MessageBox.Show(results["returnValue"].ToString()); 9 }
Activity1 就是我们上文所定义的工作流,工作流中设定了2个输入参数:UserId 和 UserPwd;
1 <x:Members> 2 <x:Property Name="returnValue" Type="OutArgument(x:String)" /> 3 <x:Property Name="UserId" Type="InArgument(x:String)" /> 4 <x:Property Name="UserPwd" Type="InArgument(x:String)" /> 5 </x:Members>
因此在执行WF的时候需要 需要带入参数。
完整的WPF C#代码如下:
1 using System; 2 using System.Activities; 3 using System.Collections; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Reflection; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows; 10 using System.Windows.Controls; 11 using System.Windows.Data; 12 using System.Windows.Documents; 13 using System.Windows.Input; 14 using System.Windows.Media; 15 using System.Windows.Media.Imaging; 16 using System.Windows.Shapes; 17 using MahApps.Metro.Controls; 18 using WF.Business; 19 20 namespace WPF.HelloWord 21 { 22 /// <summary> 23 /// StudentView.xaml 的交互逻辑 24 /// </summary> 25 public partial class LoginMetroWindow : MetroWindow 26 { 27 public LoginMetroWindow() 28 { 29 InitializeComponent(); 30 } 31 32 33 34 private void BtnOk_OnClick(object sender, RoutedEventArgs e) 35 { 36 Dictionary<string,object> dict =new Dictionary<string,object>(); 37 dict.Add("UserId",this.TxtUserid.Text); 38 dict.Add("UserPwd",this.PwdUser.Password); 39 IDictionary<string, object> results = WorkflowInvoker.Invoke(new Activity1(),dict ); 40 41 MessageBox.Show(results["returnValue"].ToString()); 42 } 43 44 private void BtnCancel_OnClick(object sender, RoutedEventArgs e) 45 { 46 this.Close(); 47 } 48 49 private void PasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e) 50 { 51 PasswordBox passwordtext = (PasswordBox) sender; 52 SetPasswordBoxSelection(passwordtext, passwordtext.Password.Length + 1, passwordtext.Password.Length + 1); 53 } 54 55 private static void SetPasswordBoxSelection(PasswordBox passwordBox, int start, int length) 56 { 57 var select = passwordBox.GetType().GetMethod("Select", 58 BindingFlags.Instance | BindingFlags.NonPublic); 59 60 select.Invoke(passwordBox, new object[] { start, length }); 61 } 62 } 63 }
WCFServer模块:
对原有项目 http://www.cnblogs.com/zhuqil/archive/2010/05/14/Hello-World.html 的WCFProject 添加一个login 函数