2011年4月18日月曜日

WorkflowDesigner.Save 実行時に Exception が発生する

リホスティングしているワークフローデザイナーで、デザインした内容を保存する場合には 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

0 件のコメント:

コメントを投稿