リホスティングしているワークフローデザイナーで、デザインした内容を保存する場合には WorkflowDesigner クラスの Save メソッドを呼び出します。しかし、条件ははっきりしていないのですが保存時にぬるぽな Exception が発生する事があります。
これは WF の、というよりも XAML を扱うもの共通で関わる問題なのですが、XAML の解析において正常に解析・解決できないケースがあるということです。その際は XML 名前空間プレフィックスの解決に失敗しインスタンスを生成しようとしたんだけどできなかった、というような形でぬるぽ例外が発生してしまうとのことです。
対応策としては、明示的に「XAML プロセッサで無視してよい」という指定を XAML 上に追加する必要があります。その際に必要となるのが mc:Ignorable 属性です。これを元にWorkflowDesinger.Save 呼び出し時に mc:Ignorable 属性を付与する方法が海外 MSDN Blog に記載されていましたので、それを VB 用に移植したものを紹介します。Blog にて紹介されている IgnorableXamlWrite クラスを用意し、WorkflowDesignerで保存する前に XAML 上に mc:Ignorable 属性を付与するようにします。
1: Imports System.Collections.Generic
2: Imports System.IO
3: Imports System.Xaml
4: Imports System.Xml
5:
6: Public Class IgnorableXamlXmlWriter
7: Inherits XamlXmlWriter
8:
9: Private ignorableNamespaces As New HashSet(Of NamespaceDeclaration)()
10: Private allNamespaces As New HashSet(Of NamespaceDeclaration)()
11: Private objectWritten As Boolean
12: Private hasDesignNamespace As Boolean
13: Private designNamespacePrefix As String
14:
15: Public Sub New(ByVal tw As TextWriter, ByVal context As XamlSchemaContext)
16: MyBase.new(XmlWriter.Create(tw, New XmlWriterSettings With {.Indent = True, .OmitXmlDeclaration = True}),
17: context,
18: New XamlXmlWriterSettings With {.AssumeValidInput = True})
19: End Sub
20:
21: Public Overrides Sub WriteNamespace(ByVal namespaceDeclaration As System.Xaml.NamespaceDeclaration)
22: If Not objectWritten Then
23: allNamespaces.Add(namespaceDeclaration)
24: If namespaceDeclaration.Namespace = "http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" Then
25: hasDesignNamespace = True
26: designNamespacePrefix = namespaceDeclaration.Prefix
27: End If
28: End If
29: MyBase.WriteNamespace(namespaceDeclaration)
30: End Sub
31:
32: Public Overrides Sub WriteStartObject(ByVal type As System.Xaml.XamlType)
33: If Not objectWritten Then
34: If hasDesignNamespace Then
35: Dim mcAlias = "mc"
36: WriteNamespace(New NamespaceDeclaration("http://schemas.openxmlformats.org/markup-compatibility/2006", mcAlias))
37: End If
38: End If
39: MyBase.WriteStartObject(type)
40:
41: If Not objectWritten Then
42: If hasDesignNamespace Then
43: Dim ign As New XamlDirective("http://schemas.openxmlformats.org/markup-compatibility/2006", "Ignorable")
44: WriteStartMember(ign)
45: WriteValue(designNamespacePrefix)
46: WriteEndMember()
47: objectWritten = True
48: End If
49: End If
50: End Sub
51:
52: End Class
53:
上記のような形で IgonorableXamlXmlWriter クラスとして用意した次には、このように利用して保存を行います。
1: Dim mdlTree = designer.Context.Services.GetService(Of Model.ModelTreeManager).Root
2: Dim sb As New StringBuilder
3: Dim xsc = New XamlSchemaContext
4: Dim bw = ActivityXamlServices.CreateBuilderWriter(New IgnorableXamlXmlWriter(
5: New StringWriter(sb),
6: xsc))
7:
8: XamlServices.Save(bw, mdlTree.GetCurrentValue)
9: designer.Save("tempWF.xaml")
このようにすることで、WorkflowDesigner が保存を行う際に Ignorable 属性が付与した形で保存されるようになります。流れとしては次のようになります。
その時点でデザイナ上に展開されている内容を取得し、ActivityXamlServices クラスを利用してアクティビティの内容を表すインスタンスを生成します。ActivityXamlServices.CreateBuilderWriter メソッドで生成されたインスタンスに書き込む事ができ、その際に利用する Writer クラスを指定し呼び出します。呼び出す事で、指定した Writer クラスにてアクティビティの XAML を生成する処理が行われますので、その内容を XamlServices.Save メソッドにて保存、WorkflowDesigner 側へと反映させます。その後に WorkflowDesigner.Save メソッドを呼ぶことで、Ignorable 属性が付与した形にてファイルに保存する事ができます。
このように書くと何が何だかわからなくなりそうなので、デザイナから保存する場合はこういうおまじないが必要!、という程度でも構わないですw