1- using DevExpress . XtraBars . Ribbon ;
1+ using DevExpress . XtraBars . Ribbon ;
22using DevExpress . XtraRichEdit ;
33using DevExpress . XtraRichEdit . API . Native ;
44using DevExpress . XtraTab ;
55using System ;
6- using System . Collections . Generic ;
76using System . Data ;
87using System . Globalization ;
8+ using System . IO ;
99
1010namespace RichEditMasterDetailMailMerge
1111{
1212 public partial class Form1 : RibbonForm
1313 {
14-
1514 DataSet xmlDataSet ;
16- DataTable products ;
17- DataTable categories ;
1815 CultureInfo cultureInfo ;
19-
16+ private bool introductionHandled ; // flag to ensure Introduction is processed only once
17+ private RichEditDocumentServer ? introductionContent ; // cached content
2018
2119 public Form1 ( )
2220 {
2321 InitializeComponent ( ) ;
24-
25- //Subscribe to the CalculateDocumentVariable event that triggers the master-detail report generation:
26- resultRichEdit . CalculateDocumentVariable += ResultRichEdit_CalculateDocumentVariable ;
2722 cultureInfo = CultureInfo . CreateSpecificCulture ( "en-US" ) ;
2823
29- //Link all RichEditControls to TabPages:
3024 xtraTabPage1 . Tag = mainRichEdit ;
31- xtraTabPage2 . Tag = masterRichEdit ;
32- xtraTabPage3 . Tag = detailRichEdit ;
33- xtraTabPage4 . Tag = resultRichEdit ;
34- }
35- private void Form1_Load ( object sender , EventArgs e )
36- {
37- //Load main template, master and detail documents to the corresponding RichEditControls:
38- mainRichEdit . LoadDocument ( "Templates\\ Template.rtf" ) ;
39- masterRichEdit . LoadDocument ( "Templates\\ Master.rtf" ) ;
40- detailRichEdit . LoadDocument ( "Templates\\ Detail.rtf" ) ;
25+ xtraTabPage2 . Tag = resultRichEdit ;
26+
27+ mainRichEdit . LoadDocument ( "Data//template.docx" ) ;
4128
42- //Initialize and fill the dataset:
4329 xmlDataSet = new DataSet ( ) ;
4430 xmlDataSet . ReadXml ( "nwind.xml" ) ;
45-
46- //Fill the required data tables:
47- categories = xmlDataSet . Tables [ "Categories" ] ;
48- products = xmlDataSet . Tables [ "Products" ] ;
31+ xmlDataSet . Tables [ "Categories" ] . PrimaryKey = new DataColumn [ ] { xmlDataSet . Tables [ "Categories" ] . Columns [ "CategoryID" ] } ;
4932 }
5033
51- #region #start
5234 private void barButtonItem1_ItemClick ( object sender , DevExpress . XtraBars . ItemClickEventArgs e )
5335 {
54- //The mail merge won't start without a data source. The main template do not require using any data,
55- //so provide it with a mock data.
56- mainRichEdit . Options . MailMerge . DataSource = CreateFakeDataSource ( ) ;
57-
58- //Start the mail merging process and pass the result to the resultRichEdit.
59- //When the mail merge starts, the CalculateDocumentVariable raises:
60- mainRichEdit . MailMerge ( resultRichEdit . Document ) ;
61-
62- xtraTabControl1 . SelectedTabPage = xtraTabPage4 ;
63- }
64- #endregion #start
65-
66- #region #master
67- private void ResultRichEdit_CalculateDocumentVariable ( object sender , CalculateDocumentVariableEventArgs e )
68- {
69- //Check whether the event is raised to the required field:
70- if ( e . VariableName == "Categories" )
71- {
72- //Provide the data source for the next document part:
73- masterRichEdit . Options . MailMerge . DataSource = categories ;
74-
75- //Create a new RichEditDocumentServer for further processing:
76- IRichEditDocumentServer result = masterRichEdit . CreateDocumentServer ( ) ;
77-
78- //Subscribe the new instance to the CalculateDocumentVariable event to handle the detail part:
79- result . CalculateDocumentVariable += result_CalculateDocumentVariable ;
80-
81- //Set additional mail merge options if necessary:
82- MailMergeOptions options = masterRichEdit . CreateMailMergeOptions ( ) ;
83- options . LastRecordIndex = 4 ;
84-
85- //Merge the document and pass it to the RichEditDocumentServer:
86- masterRichEdit . MailMerge ( options , result . Document ) ;
87- result . CalculateDocumentVariable -= result_CalculateDocumentVariable ;
88-
89- e . Value = result ;
90- e . Handled = true ;
91- }
92-
93- }
94- #endregion #master
95-
96- #region #detail
97- private void result_CalculateDocumentVariable ( object sender , CalculateDocumentVariableEventArgs e )
98- {
99- //Check whether the event is raised for the required field:
100- if ( e . VariableName == "Products" )
101- {
102- //Provide the detail part with the data source:
103- detailRichEdit . Options . MailMerge . DataSource = products ;
104-
105- //Create an intermediate document server instance:
106- IRichEditDocumentServer result = detailRichEdit . CreateDocumentServer ( ) ;
36+ MailMergeOptions mailMergeOptions = mainRichEdit . CreateMailMergeOptions ( ) ;
37+ mailMergeOptions . DataSource = xmlDataSet ;
38+ mailMergeOptions . DataMember = "Categories" ;
39+ mailMergeOptions . MergeMode = MergeMode . NewSection ;
40+ mailMergeOptions . LastRecordIndex = 10 ;
41+ resultRichEdit . CalculateDocumentVariable += detail_CalculateDocumentVariable ;
42+ mainRichEdit . MailMerge ( mailMergeOptions , resultRichEdit . Document ) ;
10743
108- //Set the merged ranges delimitation and a number of records to be merged:
109- MailMergeOptions options = detailRichEdit . CreateMailMergeOptions ( ) ;
110- options . MergeMode = MergeMode . JoinTables ;
111- options . LastRecordIndex = 10 ;
11244
113- //Provide a procedure for further processing:
114- result . CalculateDocumentVariable += detail_CalculateDocumentVariable ;
115-
116- // Create a merged document with a detail template:
117- detailRichEdit . MailMerge ( options , result . Document ) ;
118- result . CalculateDocumentVariable -= detail_CalculateDocumentVariable ;
119-
120- e . Value = result ;
121- e . Handled = true ;
122- }
123-
124-
125- //Format other merged fields:
126- if ( e . VariableName == "LowestPrice" )
127- {
128- e . Value = String . Format ( cultureInfo , "{0:C2}" , products . Compute ( "Min(UnitPrice)" , String . Empty ) ) ;
129- e . Handled = true ;
130- }
131- if ( e . VariableName == "HighestPrice" )
132- {
133- e . Value = String . Format ( cultureInfo , "{0:C2}" , products . Compute ( "Max(UnitPrice)" , String . Empty ) ) ;
134- e . Handled = true ;
135- }
136- if ( e . VariableName == "ItemsCount" )
137- {
138- e . Value = products . Rows . Count ;
139- e . Handled = true ;
140- }
45+ xtraTabControl1 . SelectedTabPage = xtraTabPage2 ;
14146 }
142- #endregion #detail
14347
144- #region #UnitPrice
14548 void detail_CalculateDocumentVariable ( object sender , CalculateDocumentVariableEventArgs e )
14649 {
147- int productId = GetID ( e . Arguments [ 0 ] . Value ) ;
148- if ( productId == - 1 )
149- return ;
150-
151- //Format the UnitPrice field:
152- if ( e . VariableName == "UnitPrice" )
50+ switch ( e . VariableName )
15351 {
154- string expression = String . Format ( "ProductID = {0}" , productId ) ;
155- e . Value = String . Format ( cultureInfo , "{0:C2}" , products . Select ( expression ) [ 0 ] [ "UnitPrice" ] ) ;
156- e . Handled = true ;
52+ case "ItemsNumber" :
53+ int itemCount = xmlDataSet . Tables [ "Products" ] . Select ( "CategoryID=" + GetID ( e . Arguments [ 0 ] . Value ) ) . Length ;
54+ e . Value = itemCount ;
55+ e . Handled = true ;
56+ break ;
57+ case "Picture" :
58+ DataRow ? row = xmlDataSet . Tables [ "Categories" ] . Rows . Find ( GetID ( e . Arguments [ 0 ] . Value ) ) ;
59+ byte [ ] imageBytes = row ? [ "Picture" ] as byte [ ] ;
60+
61+ if ( imageBytes != null )
62+ {
63+ var imageProcessor = new RichEditDocumentServer ( ) ;
64+
65+ Shape image = imageProcessor . Document . Shapes . InsertPicture (
66+ imageProcessor . Document . Range . Start ,
67+ DocumentImageSource . FromStream ( new MemoryStream ( imageBytes ) )
68+ ) ;
69+ image . TextWrapping = TextWrappingType . InLineWithText ;
70+
71+ // Set the RichEditDocumentServer with prepared content as the field value.
72+ e . Value = imageProcessor ;
73+ e . Handled = true ;
74+ }
75+ else
76+ {
77+ // No image found.
78+ e . Value = null ;
79+ }
80+ break ;
81+ case "Introduction" :
82+ // Only handle once. Subsequent occurrences will produce empty content.
83+ if ( ! introductionHandled )
84+ {
85+ introductionContent = new RichEditDocumentServer ( ) ;
86+ introductionContent . Document . AppendDocumentContent ( "Data//template-intro.rtf" ) ;
87+ e . Value = introductionContent ;
88+ e . Handled = true ;
89+ introductionHandled = true ;
90+ }
91+ else
92+ {
93+ // Return empty string (or could skip handling). We handle to suppress further processing.
94+ e . Value = DocVariableValue . Current ;
95+ e . Handled = true ;
96+ }
97+ break ;
15798 }
99+
158100 }
159- #endregion #UnitPrice
160101
161- #region #Help
162102 protected internal virtual int GetID ( string value )
163103 {
164104 int result ;
@@ -173,12 +113,6 @@ void tabControl_SelectedPageChanged(object sender, TabPageChangedEventArgs e)
173113 richEditBarController1 . RichEditControl = richEditControl ;
174114
175115 }
176- static List < int > CreateFakeDataSource ( )
177- {
178- List < int > result = new List < int > ( ) ;
179- result . Add ( 0 ) ;
180- return result ;
181- }
182- #endregion #Help
116+
183117 }
184- }
118+ }
0 commit comments