Question

I'm working with a RichTextBox control, and it's going great, except for the line spacing. I'm using an interop call via SendMessage with PARAFORMAT2 to set the line spacing for a paragraph.

That works great. It does EXACTLY what I need it to do. The problem is, when I save the RTF string out, then bring it back in, the line spacing information is gone.

I can prove this by adding a second RichText Box, and using the code below. In richTextBox1, the rtf string is formatted properly. But by the time it gets to richTextBox2, it's single-spaced again.

    private void button15_Click(object sender, EventArgs e)
    {
        string rtf = richTextBox1.Rtf;

        richTextBox2.Rtf = rtf;
    }

Any suggestions for keeping the formatting? I know I can go in and mangle the RTF string using the /ls setting, but that is VERY painful. Is there a cleaner solution that I'm just missing somewhere?

UPDATE:

The EM_STREAMIN/OUT suggestions are awesome, but unfortunately result in the same insanity. As I've read more on STREAMIN and STREAMOUT, I've discovered that SaveFile and LoadFile provided by the control are simple wrappers for these two message commands, so for simplicity I'm using those two commands in this sample. I'm attaching a sample application. You should be able to pretty much cut and paste this code into a form with one text box and five buttons:

Load, Save, Clear, DoubleSpace and SingleSpace

I'm using the MemoryStream object to handle the temporary data between the Save and Load clicks. The test: (1) Use the DoubleSpace button to change the spacing of one or more paragraphs. (2) Save the RichText to the MemoryStream. (3) Clear the rich text control (4) re-load the MemoryStream data back into the control.

Note that when it is re-loaded, it has lost the double-space formatting. All other formatting remains.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication4
{
    public partial class Form2 : Form
    {
        MemoryStream ms = new MemoryStream();
        public Form2()
        {
            InitializeComponent();

            // First, load some crap in...
            richTextBox1.Text = "The quick red fox jumped over the lazy brown dog. The quick red fox jumped over the lazy brown dog. The quick red fox jumped over the lazy brown dog. The quick red fox jumped over the lazy brown dog. \r\nThe quick red fox jumped over the lazy brown dog. The quick red fox jumped over the lazy brown dog. The quick red fox jumped over the lazy brown dog. \r\nThe quick red fox jumped over the lazy brown dog. The quick red fox jumped over the lazy brown dog. The quick red fox jumped over the lazy brown dog. ";
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            ms.Seek(0, SeekOrigin.Begin);
            richTextBox1.LoadFile(ms, RichTextBoxStreamType.RichText);
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            if (ms.Length > 0) ms.Dispose();

            ms = new MemoryStream();
            richTextBox1.SaveFile(ms, RichTextBoxStreamType.RichText);

        }

        private void btnSingleSpace_Click(object sender, EventArgs e)
        {
            SetParagraphSpacing(richTextBox1, 0);
        }

        private void btnDoubleSpace_Click(object sender, EventArgs e)
        {
            SetParagraphSpacing(richTextBox1, 2);
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            richTextBox1.Text = "";
        }

        [DllImport("USER32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wp, IntPtr lp);

        public void SetParagraphSpacing(RichTextBox rtb, int Spacing)
        {
            PARAFORMAT2 paraform = new PARAFORMAT2();
            paraform.cbSize = Marshal.SizeOf(paraform);
            paraform.bLineSpacingRule = Convert.ToByte(Spacing);
            paraform.wReserved = 0;
            paraform.dwMask = ParaMessages.PFM_LINESPACING;

            IntPtr res = IntPtr.Zero;

            IntPtr wparam = IntPtr.Zero;

            //Get the pointer to the FORMATRANGE structure in memory
            IntPtr lparam = IntPtr.Zero;
            lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(paraform));
            Marshal.StructureToPtr(paraform, lparam, false);

            //Send the rendered data for printing 
            res = SendMessage(rtb.Handle, ParaMessages.EM_SETPARAFORMAT, wparam, lparam);

            //Free the block of memory allocated
            Marshal.FreeCoTaskMem(lparam);
        }

        [StructLayout(LayoutKind.Sequential)]

        public struct PARAFORMAT2
        {
            public int cbSize;
            public uint dwMask;
            public short wNumbering;
            public short wReserved;
            public int dxStartIndent;
            public int dxRightIndent;
            public int dxOffset;
            public short wAlignment;
            public short cTabCount;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public int[] rgxTabs;

            // PARAFORMAT2 from here onwards.
            public int dySpaceBefore;
            public int dySpaceAfter;
            public int dyLineSpacing;
            public short sStyle;
            public byte bLineSpacingRule;
            public byte bOutlineLevel;
            public short wShadingWeight;
            public short wNumberingStart;
            public short wNumberingStyle;
            public short wNumberingTab;
            public short wBorderSpace;
            public short wBorderWidth;
            public short wBorders;
        }

        public class ParaMessages
        {
            public static uint PFM_SPACEAFTER = 128;
            public static uint PFM_LINESPACING = 256;

            // Constants from the Platform SDK.
            public static uint EM_SETEVENTMASK = 1073;
            public static uint EM_GETPARAFORMAT = 1085;
            public static uint EM_SETPARAFORMAT = 1095;
            public static uint EM_SETTYPOGRAPHYOPTIONS = 1226;
            public static uint WM_SETREDRAW = 11;
            public static uint TO_ADVANCEDTYPOGRAPHY = 1;
            public static uint PFM_ALIGNMENT = 8;
            public static uint SCF_SELECTION = 1;
        }
    }
}
Was it helpful?

Solution 2

Here's what I found... my SetParagraphSpacing wasn't setting everything I needed. It was enough to get the RichTextBox to display properly, but I guess there were some flags not being set. Instead, I changed it to the following, and it seems to be working PERFECTLY, now.

You have to use dyLineSpacing IN CONJUNCTION with bLineSpacingRule. bLineSpacing rule seems to be the display side of life, while dyLineSpacing is the flag that is used during the save/load. If you leave off either of these, it doesn't save/load properly or doesn't display properly. I'm not sure the mechanics behind all of this, (if someone can explain, I'd be grateful), but for now, this is the change I've made.

public void SetParagraphSpacing(RichTextBox rtb, int Spacing)
{
    PARAFORMAT2 paraform = new PARAFORMAT2();
    paraform.cbSize = Marshal.SizeOf(paraform);

    // NOTE: You need both of these!
    paraform.bLineSpacingRule = Convert.ToByte(Spacing);
    paraform.dyLineSpacing = Spacing;

    paraform.wReserved = 0;
    paraform.dwMask = ParaMessages.PFM_LINESPACING;

    IntPtr res = IntPtr.Zero;

    IntPtr wparam = IntPtr.Zero;

    //Get the pointer to the FORMATRANGE structure in memory
    IntPtr lparam = IntPtr.Zero;
    lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(paraform));
    Marshal.StructureToPtr(paraform, lparam, false);

    //Send the rendered data for printing 
    res = SendMessage(rtb.Handle, ParaMessages.EM_SETPARAFORMAT, wparam, lparam);

    //Free the block of memory allocated
    Marshal.FreeCoTaskMem(lparam);
}

OTHER TIPS

As it was suggested you need to post up some of your code, as from your description it's quite hard to say what the problem is.

From my understanding using Rtf property of the richtextbox should do exactly what you need. Default implementation is using EM_STREAMOUT and EM_STREAMIN messages to stream the content in SF_RTF format in and out of the richedit. You an also try doing the manually using the code from this SO question: Example of using EM_STREAMOUT with c# and RichEditBox

hope this helps, regards

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top