I have a very small utility application that I wrote that takes an XML string and submits it as a query against a web service, displaying the result. Very simple, and just for testing purposes. There are times when the XML string that I want to use as the web service query is encoded. That is, it's littered with < and > strings in place of the XML '<' and '>' respectively. Were these strings succinct it wouldn't be a big deal, but there are times where the query string is several thousand characters.
In the past I've resorted to opening a text editor and performing a find/replace on each of the escaped sequences, and then copying and pasting the result into my application to run the test. Simple but cumbersome.
Well, I finally got tired of jumping through those hoops and decided to make the application a bit smarter in that I wanted to have it identify when encoded XML was being pasted into it, and have the application automatically format the result performing the string replacements automatically. This is quite trivial, but I wanted to share the result as it may be informative.
My 'algorithm' for determining whether the text being pasted is in fact XML is quite rudimentary and basic, but it suffices in this instance. I'm simply checking for '<' at the start of the string. If that sequence is found, it determines, hey, this is an XML string, and attempts to perform the replacement.
It occurred to me as well while writing this that XML isn't the only format that I may want to custom-paste into a TextBox. Therefore, I abstracted the code such that you can write your own special formatting code and simply 'plug it in' to your application. NOTE: I did NOT want to derive from the TextBox to create a special 'CustomPasteTextBox' control as that would further restrict its usefulness.
The base class (which I called PasteFormatterBase) takes a TextBox and a callback. The operation is wrapped within a NativeWindow-derived SubClasser class; when the WM_PASTE message is received, a call is made back out to the callback to handle it.
public abstract class PasteFormatterBase {
public PasteFormatterBase(TextBox txt, MethodInvoker callback) {
_subClass = new SubClasser(callback);
_subClass.AssignHandle(txt.Handle);
}
private SubClasser _subClass;
private class SubClasser : NativeWindow {
private const int WM_PASTE = 0x0302;
private MethodInvoker _callback;
public SubClasser(MethodInvoker callback) {
_callback = callback;
}
protected override void WndProc(ref Message m) {
if ( m.Msg == WM_PASTE && null != _callback ) {
_callback();
return;
}
base.WndProc(ref m);
}
}
}
Then, the class that performs the actual XML decoding of the string is quite simple:
public sealed class PasteXmlFormatter : PasteFormatterBase {
public PasteXmlFormatter(TextBox txt)
: base(txt,
delegate {
IDataObject obj = Clipboard.GetDataObject();
string data = obj.GetData(DataFormats.Text) as string;
if ( null != data && data.StartsWith("<") ) {
data = data.Replace("<", "<").Replace(">", ">");
txt.SelectedText = data;
}
})
{ }
}
There may be better ways to write this (it's just a quick first pass), but it's elegant in its simplicity.