#このドキュメントの概要
前回 はActivity Creatorでのアクティビティの外観の設定方法について見ていきました。
今回はスコープをもったアクティビティの作成方法について見ていきます。
#WizardでのScope指定
ActivityCreatorのWizardでACtivityの定義画面でType項目をScopeに設定するとScopeをもったアクティビティになります。また同一のWizard画面内でScopeを持ったアクティビティとSimple設定のアクティビティを作成すると、そのSimpleアクティビティはScopeアクティビティのChildアクティビティとして設定されます。
TestScopeアクティビティのプロパティを以下のように設定しました。
またTestChildアクティビティのプロパティを以下のように設定しました。
TestScopeアクティビティとして以下のコードが自動生成されます。
using System;
using System.Activities;
using System.Threading;
using System.Threading.Tasks;
using System.Activities.Statements;
using System.ComponentModel;
using Company.Product3.Activities.Properties;
using UiPath.Shared.Activities;
using UiPath.Shared.Activities.Localization;
namespace Company.Product3.Activities
{
[LocalizedDisplayName(nameof(Resources.TestScope_DisplayName))]
[LocalizedDescription(nameof(Resources.TestScope_Description))]
public class TestScope : ContinuableAsyncNativeActivity
{
#region Properties
[Browsable(false)]
public ActivityAction<IObjectContainer> Body { get; set; }
/// <summary>
/// If set, continue executing the remaining activities even if the current activity has failed.
/// </summary>
[LocalizedCategory(nameof(Resources.Common_Category))]
[LocalizedDisplayName(nameof(Resources.ContinueOnError_DisplayName))]
[LocalizedDescription(nameof(Resources.ContinueOnError_Description))]
public override InArgument<bool> ContinueOnError { get; set; }
[LocalizedDisplayName(nameof(Resources.TestScope_Str_DisplayName))]
[LocalizedDescription(nameof(Resources.TestScope_Str_Description))]
[LocalizedCategory(nameof(Resources.Input_Category))]
public InArgument<string> Str { get; set; }
// A tag used to identify the scope in the activity context
internal static string ParentContainerPropertyTag => "ScopeActivity";
// Object Container: Add strongly-typed objects here and they will be available in the scope's child activities.
private readonly IObjectContainer _objectContainer;
#endregion
#region Constructors
public TestScope(IObjectContainer objectContainer)
{
_objectContainer = objectContainer;
Body = new ActivityAction<IObjectContainer>
{
Argument = new DelegateInArgument<IObjectContainer> (ParentContainerPropertyTag),
Handler = new Sequence { DisplayName = Resources.Do }
};
}
public TestScope() : this(new ObjectContainer())
{
}
#endregion
#region Protected Methods
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
if (Str == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(Str)));
base.CacheMetadata(metadata);
}
protected override async Task<Action<NativeActivityContext>> ExecuteAsync(NativeActivityContext context, CancellationToken cancellationToken)
{
// Inputs
var str = Str.Get(context);
return (ctx) => {
// Schedule child activities
if (Body != null)
ctx.ScheduleAction<IObjectContainer>(Body, _objectContainer, OnCompleted, OnFaulted);
// Outputs
};
}
#endregion
#region Events
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
faultContext.CancelChildren();
Cleanup();
}
private void OnCompleted(NativeActivityContext context, ActivityInstance completedInstance)
{
Cleanup();
}
#endregion
#region Helpers
private void Cleanup()
{
var disposableObjects = _objectContainer.Where(o => o is IDisposable);
foreach (var obj in disposableObjects)
{
if (obj is IDisposable dispObject)
dispObject.Dispose();
}
_objectContainer.Clear();
}
#endregion
}
}
#Childアクティビティへのデータの受け渡し
ScopeアクティビティからChildアクティビティへのデータの受け渡しは、ActivityCreatorで用意しているobjectContainerを利用します。
今回はScopeアクティビティにInArgument<string>
型のプロパティStrがありますので、これをChildアクティビティでも使えるようにします。具体的には以下のように _objectContainerにAddメソッドでデータを追加します。
(略)
// Inputs
var str = Str.Get(context);
_objectContainer.Add(str);
return (ctx) => {
// Schedule child activities
if (Body != null)
ctx.ScheduleAction<IObjectContainer>(Body, _objectContainer, OnCompleted, OnFaulted);
// Outputs
};
(略)
TestChild側ではobjectContainerインスタンスに対してGet<T>
で値を取得します。
以下の例ではTestScopeアクティビティのプロパティとして取得した値の前に"000"を付加してOutStringプロパティとして返しています。
(略)
protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
{
// Object Container: Use objectContainer.Get<T>() to retrieve objects from the scope
var objectContainer = context.GetFromContext<IObjectContainer>(TestScope.ParentContainerPropertyTag);
// Inputs
var str = objectContainer.Get<string>();
///////////////////////////
// Add execution logic HERE
///////////////////////////
str = "000" + str;
// Outputs
return (ctx) => {
OutString.Set(ctx, str);
};
}
(略)
動作確認のためUiPath Studioで以下のようなワークフローを作成してみます。
TestChildには入力プロパティがありませんが、TestScopeで入力した値を内部で参照しています。そのためTestScopeの入力で仮に"123"とした場合、strOutputには"000123"が返ってきます。
#Wizard作成後に別のアクティビティをChildアクティビティにする方法
Scopeアクティビティを作成した後にChildアクティビティを追加したい場合があると思います。
この場合はWizardでTypeをSimpleにしたアクティビティを作成した後、以下の設定を加えることによりChildアクティビティにすることができます。
- using UiPath.Shared.Activities.Utilities; の追加
- コンストラクタにScope内でのみの利用制限の追加
- 実行部分にobjectContainerの定義の追加
後で追加したTestChild2アクティビティに対する具体的な変更個所は以下になります。
(略)
using UiPath.Shared.Activities.Utilities;
(略)
#region Constructors
public TestChild2()
{
Constraints.Add(ActivityConstraints.HasParentType<TestChild2, TestScope>(string.Format(Resources.ValidationScope_Error, Resources.TestScope_DisplayName)));
}
#endregion
(略)
// Inputs
var objectContainer = context.GetFromContext<IObjectContainer>(TestScope.ParentContainerPropertyTag);
(略)
次回はnupkg周りの設定について説明します。
(その5 おわり)