Transform Clipboard Contents Between Copy and Paste - CodeProject
Transform Clipboard Contents Between Copy and Paste - CodeProject
Introduction
This Windows desktop application, while running, monitors your clipboard, and allows you to alter the contents before you paste.
This can be quite powerful, for example, copying a range of data in Excel (unformatted) and then transforming it into a pretty table
to be pasted into an email. Or, splitting a string of numbers and getting a distinct, sorted subset for analysis on paste. One of my
favorites is copying the top row of a SQL result set from MS SQL Server Management Studio, and transforming it into a column list
to make selecting and re-arranging the columns easier. The most powerful one is taking an Excel sheet and transforming it into a
SQL temp table script. All with the push of a button.
Filter/Search: Start typing to filter transformers. Backspace/Delete to clear. Current filter and the count of matches shown in
the upper right corner of the transformer list.
https://www.codeproject.com/Articles/1118362/Transform-Clipboard-Contents-Between-Copy-and-Past?display=Print 1/7
08/03/2019 Transform Clipboard Contents Between Copy and Paste - CodeProject
Many New Transformers: Escape XML, Remove HTML Tags, and more. The bigger change is that almost all but a handful of
transformations are now done using the .xml file based transformation, as opposed to being coded in C#. Feel free to use
any of the existing transformations as an example or extend upon them to meet your needs.
Configurable Groups: Moved group name and sort order to a configuration file (Groups.xml). You can add/remove/re-
arrange groups as you see fit. There are still constants (GroupSortOrder. CannedGroups.*) that map to values for
use in coded transformers in case you need them, but they are not required. This removed the notion of "group rank", which
was previously used to group items together. Now, a group's sort order is determined by the order in which it appears in the
Groups.xml file. Items with an unknown group added to the top or bottom of the list based on the app.config setting
UNKNOWN_GROUPS_TO_TOP.
In-Line Unit Testing: Since we can add a new transformer by just dropping a text file in the app folder, it made sense that
we should be able to just drop the unit tests in as well. Now both coded transformers and XML transformers have their unit
tests in line. See any of the Transmogifier XML files for examples, or Sample.xml for more details. To run the tests, run
the application with either the /DEBUG or /TEST startup flag, or run the unit test InLineUnitTestRunAll.
https://www.codeproject.com/Articles/1118362/Transform-Clipboard-Contents-Between-Copy-and-Past?display=Print 2/7
08/03/2019 Transform Clipboard Contents Between Copy and Paste - CodeProject
ListBoxTransformerItem.ItemType: There was too much confusion around what each list item was (a section header?, a
horizontal rule?, an actual transformer?, etc.), so that logic is now encapsulated into
ListBoxTransformerItem.ItemType. This made figuring out what to show and hide in the list box much cleaner
when filtering.
Serialization Tools: Broke the Serializer calls into its own class, SerializationTools. Why I did not do this years ago
(or why this is not baked into the .NET framework) -- I am not sure! This is code I will certainly reuse.
Debugging Window: Added a debugging window, visible with the /DEBUG flag. This can be useful for diagnosing issues
with a failed transformation.
https://www.codeproject.com/Articles/1118362/Transform-Clipboard-Contents-Between-Copy-and-Past?display=Print 3/7
08/03/2019 Transform Clipboard Contents Between Copy and Paste - CodeProject
When and how to trim text (Per row? Per cell? Etc...). See options:
TRIM_ON_SPLIT_ROW,TRIM_ON_SPLIT_COL, REMOVE_EMPTY_ROWS, REMOVE_EMPTY_CELLS for
more details.
How to deal with different types of line breaks. (Can treat \r, \n and \r\n all the same!) See option:
NORMALIZE_NEW_LINES for more details.
How to deal with different types of spaces. (Can treat Ascii(32) the same as !) See option:
NORMALIZE_SPACES for more details.
Row based operation like sorting, top and distinct. See options: DISTINCT_ROWS, SORT_ROWS, GRIDIFY
and the property TopRows for more details.
I found working with XML escape sequences challenging, especially for new line and tabs. I have created an option of
USE_COMMON_ESCAPE_CHARS that will pre-process the XML file swapping out \n, \r, \t and \l (custom tag
that maps to Environment.NewLine) with their XML escape sequences. It just made working with the
transformation files easier.
Support for raw XLST transformations. Assuming the input is valid XML, you can just apply an XSLT transformation. An
included sample is used to convert the XML help text generated by Microsoft Visual Studio into HTML. Thanks to Emma
Burrows for Simple XSLT stylesheet for Visual Studio .NET XML Documentation.
Group is inferred based on directory structure, unless explicitly set. It just felt more natural that way.
Transmogifier process is broken into several steps to make working with the code easier. See
Transmogifier.Transform for details.
Two stage sorting. Primary sorting of list items is by group, ordered in the order in which they appear in the Groups.xml file.
If a Rank is specified, it will be used to position items within the group, the finally sorting by name.
Background
Repetitive tasks drive me nuts. After having to alter text to make it fit some need over and over and over again, I finally realized
what I need to do is be able to programmatically alter the text in my clipboard without having to go to some intermediate tool.
For example, without this tool, to send results from SQL Server Management Studio to my co-worker, I would copy the results with
headers, launch MS Excel, paste into Excel, format in Excel, copy from Excel, paste into the email to be sent. That's a lot of busy work
just to display a table of data. Given I was doing that many times a day, I needed a solution! This is that solution.
Conversely, I would regularly get Excel workbooks from users asking me to import/export/explain some piece of data. To get that
into our production database on the other side of the firewall was a pain. The Excel to SQL transformation takes the Excel input and
makes it simple to paste that Excel data into a SQL #Temp table, which I can then use to import or analyze the issue.
After using this tool for several years, and sharing with my co-workers, it seemed a proper time to share with the rest of the world.
Enjoy!
Overview
The general design is a ViewModel bound WPF application. On startup, we use reflection to find all classes that implement
TextTransformerBase in the currently loaded assembly, we then add those classes to the list of transformers displayed in
the application. On transform, we capture the user's clipboard, and apply the transformations (calling Transform(ref text)
on each selected transformer), then write the result back to the clipboard, finally closing the application.
As with most WPF application, we start in App.xaml.cs, in Application_Startup, we parse the command line to see if we are
running in UI mode (display a list of possible transformations) or command line mode (a canned list of transformers was passed in,
so just transform and exit -- no UI). In either case, the core initialization happens in
TextTransformerBase.AllTransformers, where we parse assemblies looking for transformers, returning them as a
collection. They are then wrapped in ListBoxTransformerItem, which keeps track of selection sort order and other UI
features, finally being added to an ObservableCollection that is rendered into the UI.
On selection of any item, we DoPreview() to display what the output of the transformation will look like. Ultimately, if the user
clicks apply, we basically just do VM.ClipboardText = VM.Transform(); and close the application.
All that said, there is quite a bit of code to make the systray bubble work.
Problem 1: Integrating into Win32 components! This code was entirely grafted from the internet (clipboard-event-c-sharp) thanks
to dbkk!
Problem 2: How to display the bubble for the right amount of time after the transformation, but before the application closes
(removing the bubble and systray icon)? Most of the logic around this is in MainWindowVM.DisplayTransformComplete.
If we pass in delegate function (onDoneHandler), we wire it in to a 3 second timer to be called on done. In most cases, this
basically calls VM.Quit() to shut the app down. In any case, no matter what apply mode we are in, as soon as the "apply" starts,
we hide the main window to make it "look" like the application has shut down while we display the popup bubble.
The remaining code that is noteworthy is really the transformer code. My hope in posting this is that developers take this code and
create their own transformers!
On startup, Transmogrifier.GetAllTransmogrifiers parse the application directory (and sub directories) for *.xml
files that contain XML-based (serialized) transformation. See Sample.xml for full details, but the gist is: if your tranformation is not
too complex, then you can just create a Transmogrifier. The biggest advantage being no need to re-compile code, just create the
XML (text) file and include it below the application root. Note that you can also include unit tests in the XML file as well, again
avoiding the need to actually compile code.
To create your own custom transformer, create a class that implements the abstract base class TextTransformerBase.
The semantics are petty simple (I hope), where the constructor defines the basics (name, group, description, ...), and then overrides
https://www.codeproject.com/Articles/1118362/Transform-Clipboard-Contents-Between-Copy-and-Past?display=Print 5/7
08/03/2019 Transform Clipboard Contents Between Copy and Paste - CodeProject
Example
There are checks in place to ensure str is never null, so you should just be able to write whatever transformations you desire.
Points of Interest
Disable Preview: While working on this post, I had cause to transform a very large SQL result set (> 100 MB). It was SLOOOOW!
After a little debugging, I realized that the issue was solely with the rendering to the UI. As such, I have added a "Disable Preview"
option that will trigger if it thinks the amount of data in your clipboard is too big.
ClipboardFormat: There are some transformers that return HTML. HTML in the clipboard gets tricky. You have to provide these
index positions of where the pasted HTML lives, which is somewhat cumbersome to compute. If you need to return an HTML result,
I would recommend you use HTMLClipboardHelper.HTMLToClipboardFormattedText to provide the result.
Desktop Shortcuts: I have used this app for years, and I always just create macro shortcuts to it by hand, but that's pretty lame. So
with the help of the internet (create-shortcut-on-desktop-c-sharp) and thanks to Simon Mourier, I was able to wire in a simple
button to do so. See ShortcutHelper.CreateShortcut for the "fun" code.
Event Log: Meaningful, but non-crashing, errors are logged to the Windows Event Log. This can be helpful when debugging XML
Transformer issues when you don't have a debugger attached.
KeyHelper: This class is silly, but I couldn't think of a better way to solve the problem of cherry picking what sets of keys are viable
for a subset of actions.
GroupSortOrder: This seemed a practical solution for a user configurable arbitrarily re-sortable list of items. This is likely code I will
use again in the future in some manner or another.
InLineUnitTest: Given we are really just testing text transformations, the unit testing of that is pretty simple string before/after pair
testing. I think this solution worked out well for this use case and lends quite a bit of durability to custom transformers.
Help: I worked hard to have the help be included in-line in a seamless way, which is why TextTransformerBase has a
HelpText property. The styling and other details are defined in Help.html, which is what is ultimately rendered out to the help
window.
History
6th March, 2019 - (5.0) Bug fixes
Fixed bug in Serializable Dictionary that was preventing some transmogrifiers from working correctly
Added debugging aids to both the serialization processes and transmogrify unit tests
Fixed bug in Strip HTML transformer
Fixed bug in Replace Smart Quote transformer
Breaking Change - Removed "Root" element from serializable dictionary by default. To fix your custom
transmogrifiers, remove the <r> and </r> tags in your XML transmogrifies.
Added some fun on the help page
https://www.codeproject.com/Articles/1118362/Transform-Clipboard-Contents-Between-Copy-and-Past?display=Print 6/7
08/03/2019 Transform Clipboard Contents Between Copy and Paste - CodeProject
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile Article Copyright 2016 by Brad Joss
Web06 | 2.8.190306.1 | Last Updated 7 Mar 2019 Everything else Copyright © CodeProject, 1999-2019
https://www.codeproject.com/Articles/1118362/Transform-Clipboard-Contents-Between-Copy-and-Past?display=Print 7/7