views:

458

answers:

5

This seemed like an easy thing to do. I just wanted to pop up a text window and display two columns of data -- a description on the left side and a corresponding value displayed on the right side. I haven't worked with Forms much so I just grabbed the first control that seemed appropriate, a TextBox. I thought using tabs would be an easy way to create the second column, but I discovered things just don't work that well.

There seems to be two problems with the way I tried to do this (see below). First, I read on numerous websites that the MeasureString function isn't very precise due to how complex fonts are, with kerning issues and all. The second is that I have no idea what the TextBox control is using as its StringFormat underneath.

Anyway, the result is that I invariably end up with items in the right column that are off by a tab. I suppose I could roll my own text window and do everything myself, but gee, isn't there a simple way to do this?

Thanks for any help!

 TextBox textBox    = new TextBox();
 textBox.Font       = new Font("Calibri", 11);
 textBox.Dock       = DockStyle.Fill;
 textBox.Multiline  = true;
 textBox.WordWrap   = false;
 textBox.ScrollBars = ScrollBars.Vertical;

 Form form            = new Form();
 form.Text            = "Recipe";
 form.Size            = new Size(400, 600);
 form.FormBorderStyle = FormBorderStyle.Sizable;
 form.StartPosition   = FormStartPosition.CenterScreen;
 form.Controls.Add(textBox);

 Graphics g = form.CreateGraphics();

 float targetWidth = 230;

 foreach (PropertyInfo property in properties)
    {
  string text = String.Format("{0}:\t", Description);

  while (g.MeasureString(text,textBox.Font).Width < targetWidth)
   text += "\t";

  textBox.AppendText(text + value.ToString() + "\n");
 }

 g.Dispose();
 form.ShowDialog();
A: 

Don't the text boxes allow HTML usage? If that is the case, just use HTML to format the text into a table. Otherwise, try adding the text to a datagrid and then adding that to the form.

Nicholas Mancuso
A: 

If you want, you can translate this VB.Net code to C#. The theory here is that you change the size of a tab in the control.

Private Declare Function SendMessage _
  Lib "user32" Alias "SendMessageA" _
  (ByVal handle As IntPtr, ByVal wMsg As Integer, _
  ByVal wParam As Integer, ByRef lParam As Integer) As Integer


Private Sub SetTabStops(ByVal ctlTextBox As TextBox)

  Const EM_SETTABSTOPS As Integer = &HCBS

  Dim tabs() As Integer = {20, 40, 80}

  SendMessage(ctlTextBox.Handle, EM_SETTABSTOPS, _
    tabs.Length, tabs(0))

End Sub

I converted a version to C# for you, too. Tested and working in VS2005.

Add this using statement to your form:

using System.Runtime.InteropServices;

Put this right after the class declaration:

    private const int EM_SETTABSTOPS = 0x00CB;
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr h, int msg, int wParam, int[] lParam);

Call this method when you want to set the tabstops:

    private void SetTabStops(TextBox ctlTextBox)
    {
        const int EM_SETTABSTOPS = 203;
        int[] tabs = { 100, 40, 80 };
        SendMessage(textBox1.Handle, EM_SETTABSTOPS, tabs.Length, tabs);
    }

To use it, here is all I did:

    private void Form1_Load(object sender, EventArgs e)
    {
        SetTabStops(textBox1);

        textBox1.Text = "Hi\tWorld";
    }
Matt Dawdy
A: 

If you want something truly tabular, Mr. Haren's answer is a good one. The DataGridView will give you a very Excel spreadsheet type of look.

If you just want a two column layout (similar to HTML's table), then try out the TableLayoutPanel. It'll give you the layout you desire with the ability to use standard controls within each table cell.

Ken Wootton
A: 

I believe the only way is to do something similar to what you are doing, but use a fixed font and do your own padding with spaces so that you don't have to worry about tab expansion.

bruceatk
Yes, a fixed font would also solve the problem, but fixed fonts tend to look rather UGLY, so that wasn't a good solution for me.
AZDean
Wow marked down by the ASK'er of the question, based on font aesthetics!!
bruceatk
+1  A: 

Thanks Matt, your solution worked great for me. Here's my version of your code...

// This is a better way to pass in what tab stops I want...
SetTabStops(textBox, new int[] { 12,120 });

// And the code for the SetTabsStops method itself...
private const uint EM_SETTABSTOPS = 0x00CB;

[DllImport("User32.dll")]
private static extern uint SendMessage(IntPtr hWnd, uint wMsg, int wParam, int[] lParam);

public static void SetTabStops(TextBox textBox, int[] tabs)
{
 SendMessage(textBox.Handle, EM_SETTABSTOPS, tabs.Length, tabs);
}
AZDean
Even better -- you made it more generic which is perfect. Only other suggestion I might have is if you combined tabPos1-3 into an array as a parameter, then you could pass as few or as many as you want, and you wouldn't need the int[] tabs... line at all. Good luck AZDean.
Matt Dawdy