レポートを作成するには、 ReportBuilderまたはその他のレポートシステムを使用できます。 この記事では、Aspose.Wordsを使用してMS Word 2003 (およびそれ以降)でレポートを構築することを検討します。編集が簡単で、開発が便利で、レポートシステムの巨人などと連携するための特別なスキルが必要ないためです。
同じデータセットを使用して、一連のキーフィールドを持つ通常のWord 2003ドキュメントであるレポートテンプレートを作成する必要があります 。
次に、テンプレートを好きなようにカスタマイズできます。つまり、最終バージョンで表示するフォームにテンプレートを持ち込むことができます。 つまり、選択したデータセットの印刷可能なテンプレートを作成します。
このソリューションの利点は、印刷フォーム自体を開発する人がSQLまたはデータソースの知識を持っていない可能性があることです。 彼はMS Office Wordでのみ作業できるはずです。 すべてがシンプルであるように思われますが、このケースでは次のように想定されています。
- レポートテンプレートとレポート自体の作成は、MS Officeがインストールされていないコンピューターで発生する可能性があります。 これにより、COMを使用する機能が遮断されます。
- レポートテンプレートはdocxではなくdoc形式であり、 非常に便利です。
問題の声明
- 製品が回転しているサーバー上-Wordなどの製品はインストールしないでください
- 95ではなくWord 2003である必要があります
- 問題に対する便利で最も重要な迅速な解決策
- レポートテンプレートは、エンドビジネスユーザーが作成できるほど単純でなければなりません。
したがって、タスクは明確です。続行します。
印刷フォームの主な要素は、通常のデータフィールドとデータテーブルです。 便宜上、テンプレート内の一連のフィールドの作成と別のメソッドでのテーブルの作成を分けます。
/// <summary> </summary> /// <param name="additionalFields"> /// - , - /// ( ) /// </param> /// <returns> </returns> public static MemoryStream CreateNewTemplate(Dictionary<string, string> additionalFields) { var doc = new Document(); var docBuilder = new DocumentBuilder(doc); docBuilder.MoveToDocumentEnd();// , foreach (var field in additionalFields) { docBuilder.Font.Bold = true; docBuilder.InsertHtml(string.Format("{0}: ", field.Value)); docBuilder.Font.Bold = false; // , - , - , , Word docBuilder.InsertField(string.Format(@"MERGEFIELD {0} \* MERGEFORMAT", field.Key), field.Key); docBuilder.InsertHtml("<br>"); } var stream = new MemoryStream(); doc.Save(stream, SaveFormat.Doc); // stream.Position = 0; return stream; } /// <summary> </summary> /// <param name="stream"> </param> /// <param name="tableSysName"> ( )</param> /// <param name="additionalFields"> ( - , - ) </param> /// <param name="tableRusName"> </param> /// <returns> </returns> public static MemoryStream AddTable(Stream stream, string tableSysName, Dictionary<string, string> additionalFields, string tableRusName) { var doc = new Document(stream); var docBuilder = new DocumentBuilder(doc); docBuilder.MoveToDocumentEnd(); docBuilder.Font.Bold = true; docBuilder.Font.Size = 14; docBuilder.Writeln(tableRusName); docBuilder.Font.Size = 12; docBuilder.StartTable(); docBuilder.InsertCell(); foreach (var field in additionalFields) { docBuilder.Writeln(field.Value); if (field.Key != additionalFields.Last().Key) { docBuilder.InsertCell(); } } docBuilder.EndRow(); docBuilder.Font.Bold = false; docBuilder.InsertCell(); docBuilder.InsertField(string.Format("MERGEFIELD TableStart:{0}", tableSysName), "{"); foreach (var field in additionalFields) { docBuilder.InsertField(string.Format(@"MERGEFIELD {0} \* MERGEFORMAT", field.Key), field.Key); if (field.Key != additionalFields.Last().Key) { docBuilder.InsertCell(); } } docBuilder.InsertField(string.Format("MERGEFIELD TableEnd:{0}", tableSysName), "}"); docBuilder.Writeln(); docBuilder.EndTable(); docBuilder.Writeln(); stream.Close(); var newStream = new MemoryStream(); doc.Save(newStream, SaveFormat.Doc); newStream.Position = 0; return newStream; }
行に注意してください
docBuilder.InsertField(string.Format("MERGEFIELD TableStart:{0}", tableSysName), "{");
ここでは、外部シンボル「{」が付いたキーフィールドTableStartがテーブルに追加されます。このシンボルは基本的なものではなく、外部でフォーム上のスペースをほとんど占有しません。 また、最後に文字「}」を含むTableEndが追加されます。 これらのフィールドは、テーブルの同じ行にある必要があります。 それらに囲まれたフィールドは現在のテーブルを参照し、データセットに対して繰り返されます。 つまり、データを複数の行に配置する必要がある場合、エラーが発生します。
ただし、トリックがあります。最初の列に開始文字、最後の列に終了文字、3つの列の表を追加し、中央のセルに別の表を挿入します。 挿入されたテーブルは、各データセットに対して繰り返されます。 デフォルトでは、これが追加されることはほとんどありません(少なくとも現在適用されている目的のため)。
テンプレートを作成するには、これら2つの方法で十分です。 さらに、このテンプレートは構成されています(フォント、レイアウトなど)-これにはあまり関心がありません。 マークアップを保持したまま、完成したテンプレートにデータを入力するだけです。 テーブルにないフィールドに入力するには、すべてが簡単です:
/// <summary> </summary> /// <param name="stream"> () </param> /// <param name="fieldsToFill"> ( - , - ) </param> /// <returns> </returns> public static MemoryStream FillTemplate(Stream stream, Dictionary<string, string> fieldsToFill) { var doc = new Document(stream); var firstArray = fieldsToFill.Select(keys => keys.Key).ToArray(); var secondArray = fieldsToFill.Select(a => (object)a.Value).ToArray(); /* object. , . , , IFieldMergingCallback, . */ doc.MailMerge.Execute(firstArray, secondArray); var newStream = new MemoryStream(); doc.Save(newStream, SaveFormat.Doc); newStream.Position = 0; stream.Close(); return newStream; }
次に、テーブルに記入します。 Asposeでは、すぐにわかるように、この問題に非常に徹底的に取り組んできました。
/// <summary> /// /// </summary> /// <param name="stream"> </param> /// <param name="tableSysName"> </param> /// <param name="tableValues"> </param> /// <returns> </returns> public static MemoryStream FillTable(Stream stream, string tableSysName, List<Dictionary<string, string>> tableValues) { var doc = new Document(stream); // , var customersDataSource = new CustomerMailMergeDataSource(tableValues.ToArray(), tableSysName); // , IMailMergeDataSource, doc.MailMerge.ExecuteWithRegions(customersDataSource); var newStream = new MemoryStream(); doc.Save(newStream, SaveFormat.Doc); newStream.Position = 0; stream.Close(); return newStream; }
ここではすべてが非常に単純ですが、見てみましょう。
/// <summary> </summary> internal class CustomerMailMergeDataSource : IMailMergeDataSource { /// <summary> </summary> private readonly Dictionary<string, string>[] tableValue; /// <summary> </summary> private int recordIndex; /// <summary> </summary> /// <param name="data"> </param> /// <param name="tableName"> </param> public CustomerMailMergeDataSource(Dictionary<string, string>[] data, string tableName) { tableValue = data; recordIndex = -1; TableNamePrivate = tableName; } /// <summary> </summary> public string TableName { get { return TableNamePrivate; } } /// <summary> </summary> private static string TableNamePrivate { get; set; } /// <summary> </summary> private bool IsEof { get { return recordIndex >= tableValue.Count(); } } /// <summary> Aspose.Words , </summary> /// <param name="fieldName"> </param> /// <param name="fieldValue"> </param> /// <returns> </returns> public bool GetValue(string fieldName, out object fieldValue) { var containsKey = tableValue[recordIndex].ContainsKey(fieldName); fieldValue = containsKey ? tableValue[recordIndex][fieldName] : null; return containsKey; } /// <summary> </summary> /// <returns> , false </returns> public bool MoveNext() { if (!IsEof) { recordIndex++; } return !IsEof; } /// <summary> </summary> /// <param name="tableName"> </param> /// <returns> </returns> public IMailMergeDataSource GetChildDataSource(string tableName) { return null; } }
それだけです、必要最小限のものがあります。 マークアップの変更は発生せず、空のフィールド(データがないフィールド)は、印刷された形式では単純に消えます。 フィールドに値を入力する場合、 objectを渡す必要がありますが、ここでは文字列がどこでも渡されます。 原則として、任意のデータ型を転送し、 IFieldMergingCallbackを実装した後、データを処理およびフォーマットできます。
doc.MailMerge.FieldMergingCallbackハンドラーインスタンスを割り当てることで、ハンドラーを台無しにすることができます。 つまり、必要な数のハンドラを潜在的に作成できます
すべての機会とすべてのデータセット。 doc.MailMerge.ExecuteWithRegions()の呼び出しでハンドラーの割り当てを変更すると、より柔軟な処理が得られます。
可能な質問
- これは広告ですか? いいえ、これは広告ではありません。結果を共有することにしました
- Aspose.Wordsを正確に使用する理由 それはとても起こりました。 要件の下で最も最適になりました。
- これは有効なオプションですか? いいえ、これは簡易版です。 特別版と言うこともできますが、実際はすべてが少し複雑です。
- コードに関するコメントはどこにありますか? コード内のコメントは非常に包括的なことがわかりました。
ここにソースがあります