WWF工作流提供了Tracking跟踪功能来对工作流实例及其所包含的活动在运行时的状态进行跟踪,以便用户在需要时可以通过这些历史信息进行分析。WWF的Tracking跟踪功能是通过"SqlTrackingService"服务自动完成的,它与WWF的Persistence持续化功能一样,会将所需的状态信息自动保存到SQLServer数据库中。WWF的持续化功能只是保存工作流实例从开始运行到最终结束这个生命周期内的信息,工作流结束后相关数据即被清空。WWF的Tracking跟踪功能为用户提供了工作流实例运行时的历史信息,这样用户就可以在需要时对它进行查询。
与Persistence持续化功能一样,在使用WWF的Tracking功能前必须在SQL Server数据库中创建相应的数据表。打开路径"C:\Windows\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\zh-CHS(或EN)",该路径下有两个SQL文件:"Tracking_Logic.sql"和"Tracking_Schema.sql"。用户打开SQL Server先创建一个新的数据库"Tracking"。并在它的基础上运行这两个SQL脚本,创建后的数据库结构和存储过程如下:
一、WorkflowTrackingRecord
工作流在运行时有很多种状态。不同的事件代表不同的状态,这些状态可以由WWF的Tracking功能自动保存到SQLServer数据库中,用户可以通过"sqlTrackingWorkflowInstance"对象中的"WorkflowEvents"子对象对其信息进行查看。
状态名称 | 事件名称 | 状态描述 |
Completed | WorkflowCompleted | 当工作流结束时的状态 |
Terminated | WorkflowTerminated | 当工作流终止时的状态 |
Suspended | WorkflowSuspended | 当工作流暂停时的状态 |
Aborted | WorkflowAborted | 当工作流失败时的状态 |
Created | WorkflowCreated | 当工作流创建时的状态 |
Idle | WorkflowIdled | 当工作流空闲时的状态 |
Loaded | WorkflowLoaded | 当工作流被加载时的状态 |
Persisted | WorkflowPersisted | 当工作流被持续化时的状态 |
Resumed | WorkflowResumed | 当工作流重新启动时的状态 |
Started | WorkflowStarted | 当工作流开始启动时的状态 |
Unloaded | WorkflowUnloaded | 当工作流被卸载时的状态 |
Exception | 无 | 当工作流发生异常时的状态 |
示例:
创建一个顺序类型的工作流项目,添加一个Delay活动来观察工作流的"Idle"状态,然后拖入一个IfElse活动,并在它左边的分支中添加一个Suspend暂停活动,在它的右边的分支中添加一个Terminate终止活动,以分别来观察工作流的"Suspended"和"Terminated"状态,最后在IfElse活动后添加一个Code活动,来提供用户工作流被重新启动并即将结束。
代码如下:
//在工作流定义一个属性"Condition",用作ifElse活动的逻辑判断条件。 private bool condition; public bool Condition { get { return condition; } set { condition = value; } } public Workflow1() { InitializeComponent(); } private void Code1(object sender, EventArgs e) { MessageBox.Show("工作流被重新启动!"); } private void ifElseCondition(object sender, ConditionalEventArgs e) { e.Result = Condition; }
创建一个Windows应用程序,界面如下。根据用户选择的判断条件工作流将执行ifElse分支。当用户点击TrackingWFEvent按钮后,界面就会显示工作流状态的变迁过程信息。
代码如下:
public partial class Form1 : Form { static AutoResetEvent waitHandle = new AutoResetEvent(false); const string connectionString = "Initial Catalog=Tracking;Data Source=CZZ;Integrated Security=SSPI;"; private WorkflowRuntime workflowRuntime = null; public Form1() { InitializeComponent(); workflowRuntime = new WorkflowRuntime(); workflowRuntime.AddService(new SqlTrackingService(connectionString)); workflowRuntime.WorkflowCompleted += OnWorkflowCompleted; workflowRuntime.WorkflowTerminated += OnWorkflowTerminated; workflowRuntime.WorkflowSuspended += OnWorkflowSuspend; workflowRuntime.StartRuntime(); } //如果工作流暂停了,那么就将其重新启动 //触发工作流的"WorkflowSuspended"事件,并将工作流的状态设置为"Suspended"。 static void OnWorkflowSuspend(object sender, WorkflowSuspendedEventArgs instance) { instance.WorkflowInstance.Resume(); waitHandle.Set(); } //当工作流结束时,将各种工作流的事件添加到列表中 //当工作流正常运行结束时吗,也需要将它运行的状态信息显示到界面列表中。通过delegate代理实现 public void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e) { AddListViewItem addListItem = new AddListViewItem(AddListViewItemAsync); Invoke(addListItem, e.WorkflowInstance.InstanceId); waitHandle.Set(); } //当工作流终止时,将各种工作流的事件添加到列表中 //当Terminate活动执行时将触发工作流的"WorkflowTerminated"事件,并将工作流运行状态设置为"Terminated"。 //由于工作流终止了,因此它的后续活动Code将不再触发。但由于工作流与应用程序不在同一个线程中,因此需要通过deletegate代理来实现。 public void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e) { AddListViewItem addListItem = new AddListViewItem(AddListViewItemAsync); Invoke(addListItem, e.WorkflowInstance.InstanceId); waitHandle.Set(); } //通过delegate代理将各种工作流的事件添加到列表中 private delegate void AddListViewItem(Guid instanceId); public void AddListViewItemAsync(Guid instanceId) { //工作流在运行时通过"SqlTrackingService"服务已经将工作流的状态信息自动保存到数据库中,可以通过 //"SqlTrackingQuery"类来对SQL Server中"Tracking"数据库进行查询 SqlTrackingQuery sqlTrackingQuery = new SqlTrackingQuery(connectionString); SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance; sqlTrackingQuery.TryGetWorkflow(instanceId, out sqlTrackingWorkflowInstance); if (sqlTrackingWorkflowInstance != null) { foreach (WorkflowTrackingRecord workflowTrackingRecord in sqlTrackingWorkflowInstance.WorkflowEvents) { this.ListView1.Items.Add(new ListViewItem(new String[] { workflowTrackingRecord.TrackingWorkflowEvent.ToString(), workflowTrackingRecord.EventDateTime.ToString() })); } } } //当用户点击按钮时,应用程序会将下拉列表中的信息作为参数传递给工作流,工作流中的ifElse活动根据其信息执行不同逻辑分支,并将列表内容清空。 private void button1_Click(object sender, EventArgs e) { Dictionary<string, object> parameters = new Dictionary<string, object>(); parameters.Add("Condition", Convert.ToBoolean(this.comboBox1.Text.ToString())); WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1), parameters); workflowInstance.Start(); this.ListView1.Items.Clear(); } }
运行结果如下:
留意到,只有当选择true时,codeActivity1才会执行,因为如果在前面中已经Terminate,工作流就被终止了。
上面的ListView就是工作流整个状态的变迁过程。
另外,如果刚才运行正常的程序增加了新的活动后,工作流会报错。这是因为工作流在运行时会通过"SqlTrackingService"服务将工作流及其工作流上的所有活动偶读注册到SQLServer数据库中。当再次运行时检测到数据库中该工作流已经存在,那么将不再进行注册,因此对新增加的活动进行跟踪时就会出现错误。可以通过重命名工作流或再次注册工作流就可以解决了。
二、ActivityTrackingRecord
工作流是一组活动所构成,用户往往也需要了解各活动的执行情况,这可以通过"sqlTrackingWorkflowInstance"对象中的"ActivityEvents"子对象来获取相关历史信息。
状态名称 | 状态描述 |
Canceling | 当活动被取消时触发该状态 |
Closed | 当活动关闭时触发该状态 |
Compensating | 当活动执行补偿时触发该状态 |
Executing | 当活动执行时触发该状态 |
Faulting | 当活动失败时触发该状态 |
Initialized | 当活动被初始化时触发该状态 |
其状态转换关系如下:
创建一个顺序类型的工作流项目,然后在工作流的设计界面中添加一个Code活动,其工作流界面如下:
代码如下:
public sealed partial class Workflow1 : SequentialWorkflowActivity { public Workflow1() { InitializeComponent(); } private void Code1Execute(object sender, EventArgs e) { int i = 10; int j = 0; int k = i / j; } private void Code2Execute(object sender, EventArgs e) { MessageBox.Show("捕捉到除数为0的异常!"); } }
创建一个WinForm程序,界面如下:
代码如下:
public partial class Form1 : Form { static AutoResetEvent waitHandle = new AutoResetEvent(false); const string connectionString = "Initial Catalog=Tracking;Data Source=CZZ;Integrated Security=SSPI;"; private WorkflowRuntime workflowRuntime = null; public Form1() { InitializeComponent(); workflowRuntime = new WorkflowRuntime(); workflowRuntime.AddService(new SqlTrackingService(connectionString)); workflowRuntime.WorkflowCompleted += OnWorkflowCompleted; workflowRuntime.WorkflowTerminated += OnWorkflowTerminated; workflowRuntime.WorkflowSuspended += OnWorkflowSuspend; workflowRuntime.StartRuntime(); } //如果工作流暂停了,那么就将其重新启动 //触发工作流的"WorkflowSuspended"事件,并将工作流的状态设置为"Suspended"。 static void OnWorkflowSuspend(object sender, WorkflowSuspendedEventArgs instance) { instance.WorkflowInstance.Resume(); waitHandle.Set(); } //当工作流结束时,将各种工作流的事件添加到列表中 //当工作流正常运行结束时吗,也需要将它运行的状态信息显示到界面列表中。通过delegate代理实现 public void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e) { AddListViewItem addListItem = new AddListViewItem(AddListViewItemAsync); Invoke(addListItem, e.WorkflowInstance.InstanceId); waitHandle.Set(); } //当工作流终止时,将各种工作流的事件添加到列表中 //当Terminate活动执行时将触发工作流的"WorkflowTerminated"事件,并将工作流运行状态设置为"Terminated"。 //由于工作流终止了,因此它的后续活动Code将不再触发。但由于工作流与应用程序不在同一个线程中,因此需要通过deletegate代理来实现。 public void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e) { AddListViewItem addListItem = new AddListViewItem(AddListViewItemAsync); Invoke(addListItem, e.WorkflowInstance.InstanceId); waitHandle.Set(); } //通过delegate代理将各种工作流的事件添加到列表中 private delegate void AddListViewItem(Guid instanceId); public void AddListViewItemAsync(Guid instanceId) { //对活动运行状态跟踪的方法与工作流运行状态进行跟踪的方法类似,只是在获取活动状态使用的是"sqlTrackingWorkflowInstance"对象中的"ActivityEvents"子对象。 //因此只需要对"AddListViewItemAsync"方法进行修改 SqlTrackingQuery sqlTrackingQuery = new SqlTrackingQuery(connectionString); SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance; sqlTrackingQuery.TryGetWorkflow(instanceId, out sqlTrackingWorkflowInstance); if (sqlTrackingWorkflowInstance != null) { foreach (ActivityTrackingRecord activityTrackingRecord in sqlTrackingWorkflowInstance.ActivityEvents) { this.ListView1.Items.Add(new ListViewItem(new String[] { activityTrackingRecord.ExecutionStatus.ToString(), activityTrackingRecord.EventDateTime.ToString(), activityTrackingRecord.QualifiedName.ToString() })); } } } //当用户点击按钮时,应用程序会将下拉列表中的信息作为参数传递给工作流,工作流中的ifElse活动根据其信息执行不同逻辑分支,并将列表内容清空。 private void button1_Click(object sender, EventArgs e) { WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1)); workflowInstance.Start(); this.ListView1.Items.Clear(); } }
显示效果:
三、UserTrackingRecord
用户可以通过"TrackingData"来为每个活动添加自定义的状态或信息。并且可以通过"sqlTrackingWorkflowInstance"对象中的"UserEvents"子对象将这些信息从数据库中获取出来。
创建一个工作流如下,只有两个Code活动:
代码如下:
public sealed partial class Workflow2 : SequentialWorkflowActivity { public Workflow2() { InitializeComponent(); } private void Code1(object sender, EventArgs e) { TrackData("key1","Code1执行!"); } private void Code2(object sender, EventArgs e) { TrackData("key2", "Code2执行!"); } }
新建一个WinForm应用程序,对用户自定义信息的跟踪方法与对工作流运行状态的跟踪方法基本相同,只是在获取用户自定义信息时使用的是"sqlTrackingWorkflowInstance对象中的UserEvents"子对象。
界面如下:
与上面第二个示例相比,唯一改变的是委托代码如下:
//通过delegate代理将各种工作流的事件添加到列表中 private delegate void AddListViewItem(Guid instanceId); public void AddListViewItemAsync(Guid instanceId) { //对活动运行状态跟踪的方法与工作流运行状态进行跟踪的方法类似,只是在获取活动状态使用的是"sqlTrackingWorkflowInstance"对象中的"UserEvents"子对象。 //因此只需要对"AddListViewItemAsync"方法进行修改 SqlTrackingQuery sqlTrackingQuery = new SqlTrackingQuery(connectionString); SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance; sqlTrackingQuery.TryGetWorkflow(instanceId, out sqlTrackingWorkflowInstance); if (sqlTrackingWorkflowInstance != null) { foreach (UserTrackingRecord userTrackingRecord in sqlTrackingWorkflowInstance.UserEvents) { this.ListView1.Items.Add(new ListViewItem(new String[] { userTrackingRecord.UserDataKey, userTrackingRecord.UserData.ToString(), userTrackingRecord.EventDateTime.ToString(), userTrackingRecord.QualifiedName.ToString() })); } } }
显示效果如下:
四、TrackingRuleActionEvent
用于跟踪逻辑判断的。例如Policy活动。该活动可以根据用户定义的规则"Rule"来进行判断,并根据其结果来分别执行"Then Actions"和"Else Actions"中的操作。WWF的Tracking功能同样可以对基于逻辑判断活动所执行的判断结果进行跟踪。
对于基于逻辑判断类型活动的判断结果,进行跟踪的方法与用户自定义信息的跟踪方法基本相同。只需要将UserEvents子对象类型转换为"RuleActionTrackingEvent"类型即可。
foreach (UserTrackingRecord userTrackingRecord in sqlTrackingWorkflowInstance.UserEvents) { RuleActionTrackingEvent rule = (RuleActionTrackingEvent)userTrackingRecord.UserData; this.ListView1.Items.Add(new ListViewItem(new String[] { userTrackingRecord.QualifiedName, rule.RuleName, rule.ConditionResult.ToString() })); }
五、TraclingProfile
WWF的"TrackingProfile"文件对"Tracking"功能提供的信息进行过滤,并可以根据用户的不通需要定义不同版本的"TrackingProfile"文件。
在构建"TrackingProfile"文件时,将使用到"TrackingService"和"TrackingChannel"两个抽象类。需要自定义两个类来继承并实现两个抽象类中的内容。应用程序与"TrackingService"和"TrackingChannel"的关系如下:
新建一个工作流如下:
Sequence容器的"Fault Handlers"视图中,添加一个FaultHandler活动来捕捉"除数为0"的异常,并在其内部添加一个Code活动来对该异常处理。
代码如下:
public sealed partial class TrackingProfile: SequentialWorkflowActivity { public TrackingProfile() { InitializeComponent(); } private void Code1Exe(object sender, EventArgs e) { int i = 9; int j = 0; int k = 1; k = i / j; } private void Code2Exe(object sender, EventArgs e) { TrackData("key1", "发生除数为0的异常错误"); } private void Code3Exe(object sender, EventArgs e) { TrackData("key2", "工作流重新启动"); } }
MyTrackingChannel:
public class MyTrackingChannel : TrackingChannel { private TrackingParameters parameters = null; private Form1 trackingForm = new Form1(); public MyTrackingChannel(TrackingParameters parameters, Form1 form) { this.parameters = parameters; trackingForm = form; } protected override void InstanceCompletedOrTerminated() { } protected override void Send(TrackingRecord record) { if (record is WorkflowTrackingRecord) { TrackingWFRecord((WorkflowTrackingRecord)record); } if (record is ActivityTrackingRecord) { TrackingActivityRecord((ActivityTrackingRecord)record); } if (record is UserTrackingRecord) { TrackingUserRecord((UserTrackingRecord)record); } } //对工作流对象的跟踪 public void TrackingWFRecord(WorkflowTrackingRecord record) { Form1.AddListViewWF addListItem = new Form1.AddListViewWF(trackingForm.AddListViewMethodWF); TrackingInfo proInfo = new TrackingInfo(); proInfo.Date = record.EventDateTime.ToString(); proInfo.EventName = record.TrackingWorkflowEvent.ToString(); trackingForm.Invoke(addListItem, proInfo); } //对活动的跟踪 public void TrackingActivityRecord(ActivityTrackingRecord record) { Form1.AddListViewActivity addListItem = new Form1.AddListViewActivity(trackingForm.AddListViewMethodActivity); TrackingInfo proInfo = new TrackingInfo(); proInfo.Date = record.EventDateTime.ToString(); proInfo.EventName = record.ExecutionStatus.ToString(); proInfo.ActivityName = record.QualifiedName.ToString(); trackingForm.Invoke(addListItem, proInfo); } //对用户自定义状态信息的跟踪 public void TrackingUserRecord(UserTrackingRecord record) { Form1.AddListViewUser addListItem = new Form1.AddListViewUser(trackingForm.AddListViewMethodUser); TrackingInfo proInfo = new TrackingInfo(); proInfo.Date = record.EventDateTime.ToString(); proInfo.Key = record.UserDataKey; proInfo.ActivityName = record.QualifiedName.ToString(); proInfo.EventName = record.UserData.ToString(); trackingForm.Invoke(addListItem, proInfo); } }
MyTrackingService:
public class MyTrackingService : TrackingService { private Form1 tform; public MyTrackingService(Form1 form) { tform = form; } protected override System.Workflow.Runtime.Tracking.TrackingProfile GetProfile(Guid workflowInstanceId) { throw new Exception("The method or operation is not implemented."); } protected override System.Workflow.Runtime.Tracking.TrackingProfile GetProfile(Type workflowType, Version profileVersionId) { throw new Exception("The method or operation is not implemented."); } protected override TrackingChannel GetTrackingChannel(TrackingParameters parameters) { return new MyTrackingChannel(parameters, tform); } protected override bool TryGetProfile(Type workflowType, out System.Workflow.Runtime.Tracking.TrackingProfile profile) { //如果是4.0.0就哪有追踪用户状态的TrackingProfile if (tform.PVersion == "4.0.0") { profile = GetProfile(); } else { profile = GetProfile401(); } return true; } protected override bool TryReloadProfile(Type workflowType, Guid workflowInstanceId, out System.Workflow.Runtime.Tracking.TrackingProfile profile) { profile = null; return false; } private static TrackingProfile GetProfile() { //创建一个Tracking Profile文件 TrackingProfile profile = new TrackingProfile(); profile.Version = new Version("4.0.0"); //添加对活动进行跟踪的节点 ActivityTrackPoint activityTrackPoint = new ActivityTrackPoint(); ActivityTrackingLocation activityLocation = new ActivityTrackingLocation(typeof(Activity)); activityLocation.MatchDerivedTypes = true; IEnumerable<ActivityExecutionStatus> statuses = Enum.GetValues(typeof(ActivityExecutionStatus)) as IEnumerable<ActivityExecutionStatus>; foreach (ActivityExecutionStatus status in statuses) { activityLocation.ExecutionStatusEvents.Add(status); } activityTrackPoint.MatchingLocations.Add(activityLocation); profile.ActivityTrackPoints.Add(activityTrackPoint); //添加对工作流进行跟踪的节点 WorkflowTrackPoint workflowTrackPoint = new WorkflowTrackPoint(); workflowTrackPoint.MatchingLocation = new WorkflowTrackingLocation(); foreach (TrackingWorkflowEvent workflowEvent in Enum.GetValues(typeof(TrackingWorkflowEvent))) { workflowTrackPoint.MatchingLocation.Events.Add(workflowEvent); } profile.WorkflowTrackPoints.Add(workflowTrackPoint); //添加对用户自定义信息进行跟踪的节点(4.0.0版本有用户自定义状态追踪) UserTrackPoint userTrackPoint = new UserTrackPoint(); UserTrackingLocation userLocation = new UserTrackingLocation(); userLocation.ActivityType = typeof(Activity); userLocation.MatchDerivedActivityTypes = true; userLocation.ArgumentType = typeof(object); userLocation.MatchDerivedArgumentTypes = true; userTrackPoint.MatchingLocations.Add(userLocation); profile.UserTrackPoints.Add(userTrackPoint); return profile; } private static TrackingProfile GetProfile401() { //创建一个Tracking Profile文件 TrackingProfile profile = new TrackingProfile(); profile.Version = new Version("4.0.1"); //添加对活动进行跟踪的节点 ActivityTrackPoint activityTrackPoint = new ActivityTrackPoint(); ActivityTrackingLocation activityLocation = new ActivityTrackingLocation(typeof(Activity)); activityLocation.MatchDerivedTypes = true; IEnumerable<ActivityExecutionStatus> statuses = Enum.GetValues(typeof(ActivityExecutionStatus)) as IEnumerable<ActivityExecutionStatus>; foreach (ActivityExecutionStatus status in statuses) { activityLocation.ExecutionStatusEvents.Add(status); } activityLocation.ExecutionStatusEvents.Remove(ActivityExecutionStatus.Faulting); activityTrackPoint.MatchingLocations.Add(activityLocation); profile.ActivityTrackPoints.Add(activityTrackPoint); //添加对工作流进行跟踪的节点 WorkflowTrackPoint workflowTrackPoint = new WorkflowTrackPoint(); workflowTrackPoint.MatchingLocation = new WorkflowTrackingLocation(); foreach (TrackingWorkflowEvent workflowEvent in Enum.GetValues(typeof(TrackingWorkflowEvent))) { workflowTrackPoint.MatchingLocation.Events.Add(workflowEvent); } workflowTrackPoint.MatchingLocation.Events.Remove(TrackingWorkflowEvent.Exception); profile.WorkflowTrackPoints.Add(workflowTrackPoint); //4.0.1版本在这个地方少了对用户自定义状态的跟踪 return profile; } }
TrackingInfo:
public class TrackingInfo { private string name; private string date; private string activityname; private string key; public string EventName { get { return name; } set { name = value; } } public string Date { get { return date; } set { date = value; } } public string ActivityName { get { return activityname; } set { activityname = value; } } public string Key { get { return key; } set { key = value; } } }
代码架构如下:
Winform界面如下:
Winform代码如下:
public partial class Form1 : Form { private string pversion; public string PVersion { set { pversion = value; } get { return pversion; } } static AutoResetEvent waitHandle = new AutoResetEvent(false); const string connectionString = "Initial Catalog=Tracking;Data Source=CZZ;Integrated Security=SSPI;"; private WorkflowRuntime workflowRuntime=null; public Form1() { InitializeComponent(); workflowRuntime = new WorkflowRuntime(); workflowRuntime.WorkflowSuspended += OnWorkflowSuspend; workflowRuntime.AddService(new MyTrackingService(this)); workflowRuntime.AddService(new SqlTrackingService(connectionString)); workflowRuntime.StartRuntime(); } //如果工作流暂停了,那么就将其重新启动 static void OnWorkflowSuspend(object sender, WorkflowSuspendedEventArgs instance) { instance.WorkflowInstance.Resume(); waitHandle.Set(); } //启动工作流的实例,然后根据下拉列表中用户选择的内容传递给工作流。 private void button1_Click(object sender, EventArgs e) { this.ListViewActivity.Items.Clear(); this.ListViewUser.Items.Clear(); this.listViewWF.Items.Clear(); this.PVersion = this.comboBox1.Text.ToString(); WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow2)); workflowInstance.Start(); } public delegate void AddListViewWF(TrackingInfo proInfo); public void AddListViewMethodWF(TrackingInfo proInfo) { this.listViewWF.Items.Add(new ListViewItem(new String[] { proInfo.EventName, proInfo.Date })); } public delegate void AddListViewActivity(TrackingInfo proInfo); public void AddListViewMethodActivity(TrackingInfo proInfo) { this.ListViewActivity.Items.Add(new ListViewItem(new String[] { proInfo.EventName, proInfo.Date , proInfo.ActivityName})); } public delegate void AddListViewUser(TrackingInfo proInfo); public void AddListViewMethodUser(TrackingInfo proInfo) { this.ListViewUser.Items.Add(new ListViewItem(new String[] { proInfo.Key, proInfo.EventName, proInfo.Date, proInfo.ActivityName })); } }
显示效果如下:4.0.0显示用户自定义状态而4.0.1不显示。