Analyzing Combining Unicode Characters

March 23, 2014

Some scripts supported by the Unicode standard define combining characters, which may cause confusion for people not familiar with a specific script:

For one of these questions, I even analyzed the character sequence manually. But this analysis is not much fun if you try perform it repeatedly.

Recently I stumbled upon the SE user name n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳, so I had the idea to write a small program to output the Unicode code points and character names for a given input, based on Unicode’s UnicodeData.txt file.

The output for the text samples in the above links looks like this:

unispell 1

unispell 2

unispell 3

unispell 4

unispell nhahtdh

The initial version of this program is available for download here.


How to use Application.Restart()

June 2, 2013

I have never used the function Application.Restart(), but I came across this SO question today about its seemingly mysterious behavior that it caused the application the be restarted twice (instead of once).

In the posted code, the function was called from the FormClosing event handler.

A little search turned up this article on CodeProject that also dealt with the problem, and sketches the correct solution.

So I created a little sample application that calls Application.Restart from different locations:

  • Program.Main()
  • FormMain.FormClosing()
  • Dialog.FormClosing()

The code:

static class Program
{
  [STAThread]
  static void Main()
  {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    var f = new Form1();
    Application.Run(f);

    if (f.AppRestart)
      Application.Restart();
  }
}

public partial class Form1 : Form
{
  bool restart;
  public bool AppRestart;

  public Form1()
  {
    InitializeComponent();
  }

  private void btnDialog_Click(object sender, EventArgs e)
  {
    (new FrmRestart()).Show();
  }

  private void btnRestartFromForm_Click(object sender, EventArgs e)
  {
    restart = true;
    this.Close();
  }

  private void Form1_FormClosing(object sender, FormClosingEventArgs e)
  {
    if (restart)
      Application.Restart();
  }

  private void btnRestartFromApp_Click(object sender, EventArgs e)
  {
    AppRestart = true;
    this.Close();
  }
}

public partial class FrmRestart : Form
{
  bool restart;

  public FrmRestart()
  {
    InitializeComponent();
  }

  private void FrmRestart_FormClosing(object sender, FormClosingEventArgs e)
  {
    if (restart)
      Application.Restart();
  }

  private void btnRestart_Click(object sender, EventArgs e)
  {
    restart = true;
    this.Close();
  }
}

The results from this experiment show

  • Program.Main() restarts 1 instance
  • FormMain.FormClosing() restarts 2 instances
  • Dialog.FormClosing() restarts 2 instances

This SO answer also analyses the stack trace and finds that Application.Restart also closes every open form, and thus invokes FormClosing, which in turn causes the application restart twice.

I find the proposed solution (and currently accepted answer) problematic

private bool isRestarted;

private void FormClosing(object sender, FormClosingEventArgs e)
{
    if (settingschanged && !isRestarted)
    {
        isRestarted = true;
        Application.Restart(); 
    }
}

because it works around a specified behavior of a framework, rather than use the framework as intended.


Synchronizing Scrolling WinForms TreeViews

January 17, 2012

In my previous post I sketched the mechanism to implement a TreeView class which raises scroll events and allows setting the scrollbar positions of a custom TreeView.

Now we need to wire up the events and the scroll methods of two TreeView controls.

First, each TreeNode object needs to know its corresponding TreeNode in the other TreeView for synchronization. We can use the Tag property to store the other TreeNode like this:

var tnL = tnLeftRoot.Nodes.Add("this is node " + i);
var tnR = tnRightRoot.Nodes.Add("this is node " + i);

tnL.Tag = tnR;
tnR.Tag = tnL;

Apart from the ScrollH/V messages, we also want to synchronize the Collapse and Expand actions. If one TreeNode is collapsed or expanded, its corresponding TreeNode in the other TreeView should perform the same action:

private void tvLeft_AfterCollapse(object sender, TreeViewEventArgs e)
{
    (e.Node.Tag as TreeNode).Collapse();
}

private void tvLeft_AfterExpand(object sender, TreeViewEventArgs e)
{
    (e.Node.Tag as TreeNode).Expand();
}

Let’s also synchronize the scroll position:

private void tvLeft_ScrollH(object sender, devio.Windows.Controls.ScrollEventArgs e)
{
    tvRight.ScrollToPositionH(e.ScrollInfo);
}

private void tvLeft_ScrollV(object sender, devio.Windows.Controls.ScrollEventArgs e)
{
    tvRight.ScrollToPositionV(e.ScrollInfo);
}

Of course, these events need to be implemented for the other TreeView (tvRight) as well.

The control library and a sample WinForms application is available for download at GitHub.


Scrolling WinForms TreeViews in Sync

January 16, 2012

For a WinForms application that implements the comparison of hierarchical data I needed to synchronize the scrollbar position of two TreeView controls: if the user moves the scrollbar in one TreeView, the other TreeView should move to the same position.

A first approach of the solution is sketched under this question on SO which subclasses the TreeView class and responds to the WM_VSCROLL message. Other discussions pointed to a solution which is currently not available, but can be fetched from the Wayback Machine.

The subclassed TreeView interacts with the Windows TreeView using the GetScrollInfo and SetScrollInfo functions of the Win32 API and the SCROLLINFO structure.

I extended the code I had found to also handle the WM_HSCROLL and WM_MOUSEWHEEL messages by implementing the WndProc() method:

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    switch (m.Msg)
    {
        case NativeMethods.WM_VSCROLL:
        case NativeMethods.SBM_SETSCROLLINFO:
            RaiseScrollVEvent(m.HWnd);
            break;

        case NativeMethods.WM_HSCROLL:
            RaiseScrollHEvent(m.HWnd);
            break;

        case NativeMethods.WM_MOUSEWHEEL:
            RaiseScrollVEvent(m.HWnd);
            RaiseScrollHEvent(m.HWnd);
            break;
    }
}

The RaiseScroll?Event() methods retrieve the scrollbar position (vertical or horizontal) of the tree view and raise a corresponding event in the Form’s code:

private void RaiseScrollVEvent(IntPtr handle)
{
    ScrollInfoStruct si = new ScrollInfoStruct();
    si.fMask = NativeMethods.SIF_ALL;
    si.cbSize = Marshal.SizeOf(si);

    NativeMethods.GetScrollInfo(handle, NativeMethods.SB_VERT, ref si);

    ScrollEventArgs e = new ScrollEventArgs();
    e.ScrollInfo = si;

    OnScrollV(e);
}

To adjust the scrollbar position of a tree view, we need a ScrollToPosition?() method like this:

public void ScrollToPositionV(ScrollInfoStruct si)
{
    ScrollInfoStruct siOwn = new ScrollInfoStruct();
    siOwn.fMask = NativeMethods.SIF_ALL;
    siOwn.cbSize = Marshal.SizeOf(si);

    NativeMethods.GetScrollInfo(this.Handle, NativeMethods.SB_VERT, 
        ref siOwn);

    if (siOwn.nPos != si.nPos)
    {
        NativeMethods.SetScrollInfo(this.Handle.ToInt32(), 
            NativeMethods.SB_VERT, ref si, true);
        NativeMethods.SendMessageLong(this.Handle.ToInt32(), 
            NativeMethods.WM_VSCROLL,
            NativeMethods.SB_THUMBTRACK + 0x10000 * si.nPos, 0);
    }
}


Adding Formatted Text to RichTextBox

January 28, 2011

RichTextBox provides the method AppendText() inherited from TextBoxBase to programmatically add  text to the editor control, but there seems to be no native method to append formatted text.

A small extension class adds this functionality:

public static class RichTextBoxExtensions
{
  const string NewLine = "\r\n";

  public static void AppendLine(this RichTextBox ed)
  {
    ed.AppendText(NewLine);
  }

  public static void AppendLine(this RichTextBox ed, string s)
  {
    ed.AppendText(s + NewLine);
  }

  public static void AppendBoldLine(this RichTextBox ed, string s)
  {
    int ss = ed.SelectionStart;
    ed.AppendText(s);
    int sl = ed.SelectionStart - ss + 1;

    Font bold = new Font(ed.Font, FontStyle.Bold);
    ed.Select(ss, sl);
    ed.SelectionFont = bold;
    ed.AppendText(NewLine);
  }
}