WPF ドュメント

Last Updated 2011/09/21


WPF にはフロードキュメントと固定ドキュメントの 2 種類のドキュメントの形式があります。固定ドキュメント、つまり、XPS ドキュメントは単独のページを用意しましたので、このページでは主にフロードキュメントを扱うことにします。


フロードキュメント "flow document" は、ウインドウのサイズまたはディスプレイの解像度に応じて、コンテンツの再配置を可能にするドキュメントの形式です。その代表例は、HTML ドキュメントですね。インターネットエクスプローラで表示した HTML ドキュメントはビューワーのサイズを変えると、それに応じてコンテンツ(テキストや画像)が移動(フロー)します。また、ドキュメントのフォントや外観を変更することもできます。

一方、固定ドキュメント "fixed document" は名前のとおり、そのコンテンツは常に一定で、あとから変更することはできません。また、「固定」には別の意味があります。つまり、ディスプレイやプリンタの種類にかかわらず、常に同じ外観になるということです。

さて、WPF ではフロードキュメントは FlowDocument クラス、固定ドキュメントは FixedDocument クラスであらわしますが、このページでは、FlowDocument クラスだけに焦点をあてます。


フロードキュメントの作成

FlowDocument オブジェクトを定義するには、XAML コードを使います。つまり、テキストエディタで作成することは可能です。HTML ドキュメントもそうですが、テキストエディタで直接編集してもそれほど面倒ではありません。

WPF の機能を使ってフロードキュメントを作成するもっとも簡単な方法は、RichTextBox コントロールを使うことです。WPF アプリケーションとして、フロードキュメントを作成するツールを自作することはそれほど面倒ではありません。むしろ、こんなに簡単にできるのかと思うほどです。RichTextBox コントロールには編集コマンドが標準でサポートされていますので、編集のためのコードを書く必要がないからです。

Note RichTextBox コントロールを使って、ドキュメントを編集するアプリケーションを作っていますので、近いうちに公開することになるでしょう。

RichTextBox コントロールでフロードキュメントを作成し、保存するときに XAML 形式を選択すると、ファイルの拡張子が .xamlpackage のバイナリファイルが作成されますが、RTF 形式で保存することも可能です。

下図は、作成中のアプリケーションを起動したところですが、テキスト中に挿入した図表はドキュメント自体に直接設定したものです。このアプリケーションでどうやってこれを実現するかを現在思案中です。

FlowDocEditor


フロードキュメントビューワークラス

固定ドキュメントを表示するコントロールは、DocumentViewer コントロールだけですが、フロードキュメントを表示するコントロールは次の 3 種類があります。

  1. FlowDocumentPageViewer
  2. FlowDocumentReader
  3. FlowDocumentScrollViewer

1.は、ページモードで表示するコントロール、2. は表示モードを切り替え可能なコントロール、3. はスクロールモードで表示するコントロールです。

3 種類のビューワーに対応するため、フロードキュメントを定義する XAML ファイルを読み込むメソッドを作ってみました。

// フロードキュメントをドキュメントビューワーに読み込む
// control : フロードキュメントビューワー
// stream  : フロードキュメントを保持するストリーム
private void LoadStreamToFlowDocumnetViewer(System.Windows.Controls.Control control, Stream stream)
{
  FlowDocument document = null;

  if (stream == null)
    return;
  else
  {
    try
    {
      document = XamlReader.Load(stream) as FlowDocument;

      if (document == null)
        throw (new XamlParseException("指定のファイルを読み込めませんでした。"));
    }
    catch (XamlParseException e)
    {
      System.Windows.MessageBox.Show(e.Message);
    }
    catch (Exception e)
    {
      System.Windows.MessageBox.Show(e.Message);
    }

    if (document == null)
      return;

    // 3 種類あるビューワーを切り替えてドキュメントを設定するが
    // ほかにスマートな方法を思いつかなかった
    if (control.GetType() == typeof(FlowDocumentPageViewer))
    {
      FlowDocumentPageViewer viewer = control as FlowDocumentPageViewer;
      viewer.Document = document;
    }
    else if (control.GetType() == typeof(FlowDocumentReader))
    {
      FlowDocumentReader viewer = control as FlowDocumentReader;
      viewer.Document = document;
    }
    else if (control.GetType() == typeof(FlowDocumentScrollViewer))
    {
      FlowDocumentScrollViewer viewer = control as FlowDocumentScrollViewer;
      viewer.Document = document;
    }
  }
}

以下は、上記のメソッドを呼び出すコードです。ここでは、button1 をクリックしたと想定しています。なお、ビューワーのオブジェクト名は flowDocumentViewer とします。

private void button1_Click(Object sender, RoutedEventArgs args)
{
  OpenFileDialog dlg = new OpenFileDialog();
  dlg.Filter = "XAML ファイル (*.xaml)|*.xaml|すべてのファイル (*.*)|*.*";

  if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  {
    FileStream stream = dlg.OpenFile() as FileStream;
    this.LoadStreamToFlowDocumnetViewer(flowDocumentViewer, stream);
    stream.Close();
  }
}

以下は、テスト用のフロードキュメントを定義する XAML ファイルの内容です。文字コードは UTF-8 にしてください。

<FlowDocument
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Paragraph>これは FlowDocument オブジェクトの例です。</Paragraph>
</FlowDocument>

XAML と RTF とを相互変換する

PresentationFramework.dll のSystem.Windows.Documents 名前空間内に、internal な XamlRtfConverter クラスがあります。クラス名を見てのとおり、XAML から RTF またはその逆のコード変換をするメソッドを持ちます。つまり、このクラスは次の 2 つのメソッドを持ちます。

ただし、XAML ファイルは FlowDocument オブジェクトでなければなりません。

internal ではなく、public にしてくれれば自由に使えるのですが、変なところでケチになる Microsoft らしいとはいえますね。ともあれ、リフレクションを利用すれば internal なメソッドであっても使うことができます。そこで、これら 2 つのメソッドを呼び出すメソッドを作ってみました。

// XAML を RTF に変換するメソッド
private string ConvertXamlToRtf(string xamlContent)
{
  // FrameworkElement クラスを含むアセンブリ、つまり、PresentationFramework.dll を取得する
  System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(
      typeof(System.Windows.FrameworkElement));

  // XamlRtfConverter クラスの型を取得する
  Type type = assembly.GetType("System.Windows.Documents.XamlRtfConverter");

  // 指定の型のインスタンスを作成する
  // 2 番目の引数の true は非 public も含むことを指定する
  object converter = Activator.CreateInstance(type, true);

  // ConvertXamlToRtf メソッドを取得する
  System.Reflection.MethodInfo method = type.GetMethod("ConvertXamlToRtf",
      System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

  // ConvertXamlToRtf メソッドを呼び出す
  string content = (string)method.Invoke(converter, new object[] { xamlContent });

  return content;
}

次に、上記のメソッドを呼び出すコードを示す。FlowDocument オブジェクトを定義する XAML ファイルのファイル名を FlowDocument.xaml、出力ファイル名を FlorDocument.rtf とします。テスト用にファイルは、フロードキュメントビューワークラスの項で紹介したフロードキュメントが使えます。変換後の RTF ファイルは Microsoft Word などの RTF を表示できるツールを使って確認してください。

private void button1_Click(object sender, RoutedEventArgs e)
{
  string xamlFileName = "FlowDocument.xaml";
  string rtfFileName = "FlowDocument.rtf";

  StreamReader reader = new StreamReader(xamlFileName);
  string xaml = reader.ReadToEnd();
  reader.Close();

  string rtf = this.ConvertXamlToRtf(xaml);

  StreamWriter writer = new StreamWriter(rtfFileName, false);
  writer.Write(rtf);
  writer.Close();
}

以下は、RTF から XAML に変換するメソッドです。動作を確認する手順は上記にならってください。

// RTF を XAML に変換するメソッド
private string ConvertRtfToXaml(string rtfContent)
{
  System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(
      typeof(System.Windows.FrameworkElement));

  Type type = assembly.GetType("System.Windows.Documents.XamlRtfConverter");

  object converter = Activator.CreateInstance(t, true);

  System.Reflection.MethodInfo method = type.GetMethod("ConvertRtfToXaml",
      System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

  string content = (string)method.Invoke(converter, new object[] { rtfContent });

  return content;
}

HTML を XAML に変換する

フロードキュメントを作成する手順はいろいろありますが、リッチなドキュメントというと HTML を思い浮かべます。HTML ドキュメントを作成・編集するツールは、有料・無料のものを含めてたくさんあります。しかも、文字色を設定したり、画像を挿入することも容易です。したがって、HTML から XAML に変換できれば便利ですね。

前の項で紹介した XAML と RTF との相互変換を可能にする便利なメソッドはないので、シコシコと作るほかないのかと思っていたところ、WPF SDK の中に、「XAML から HTML への変換デモ」があることに気付きました。

これは、HTML から XAML へ、XAML から HTML への変換するアプリケーションです、テストしたところ、おおむねうまくいきます。「おおむね」の意味は、ドキュメントの構造が異なりますから完全には復元できないということです。たとえば、HTML の H3 タグを XAML に変換すると、フォントのサイズを指定する構文になります。これを再び、HTML に復元しようとしてもフォントサイズが H3 タグをさすのかどうかは分かりませんからまったく別の HTML 構文になってしまいます。

まあ、しかし、おおむね OK というだけでも上出来だと思いますので、このデモソフトは使えると思います。興味のある人は、ご自分でテストしてみてください。

−以上−