One of my current Visio VSTO add-in projects requires me to react to users pasting custom shapes in the Visio page from the clipboard. The process involves registering the ExitScope event and then checking the Clipboard contents. Initially, I was merely testing for a line in the DataObject that started with the word “Visio “, but then testing revealed that copying and pasting text from one shape to another was also getting processed, so then I added a further check that the line also finished with the word ” Shapes”. This works fine, but then I got to wonder if I could also post-process tables copied and pasted from Excel because the standard paste maintains font settings but omits the tabs and line feeds, making the text unusable without serious manual editing. This article explains how this can done.
Although I managed to put the tabs and line feeds back into the pasted text, I could not automatically set the tab spacing because that is not supported in code. So, the tab spacing must be set manually, or I need to create a custom master shape, with tab spacing included, that the text can be pasted into.
The following screenshots are the contents of the Clipboard DataObject when the following are copied and pasted into Visio:
- Excel table
- Notepad text
- Visio shape
- Visio text
This the C# code that checks the contents of the Clipboard, and passes processing to the relevant methods if a Visio shape or Unicode text is found (I have left other types of text formats in for future use if necessary)..
case (int)Visio.VisUICmds.visCmdUFEditPaste: //Paste 1022
{
try
{
var isVisio = false;
var isRTF= false;
var isText = false;
var isUnicodeText = false;
var isCsv = false;
var isHtml = false;
IDataObject iData = Clipboard.GetDataObject();
if (iData != null)
{
var clipList = iData.GetFormats();
foreach (var itm in clipList)
{
if (itm.StartsWith("Visio ") && itm.EndsWith(" Shapes"))
{
isVisio = true;
break;
}
if (itm.Equals("Rich Text Format"))
{
isRTF = true;
break;
}
if (itm.Equals("Text"))
{
isText = true;
//break;
}
if (itm.Equals("UnicodeText"))
{
isUnicodeText = true;
//break;
}
if (itm.Equals("HTML Format"))
{
isHtml = true;
//break;
}
if (itm.Equals("Csv"))
{
isCsv = true;
//break;
}
}
}
if (isVisio)
{
pagVm.ProcessDuplicates(subjectApplication.ActiveWindow.Selection);
}
if (isUnicodeText)
{
string uniText = Clipboard.GetText(TextDataFormat.UnicodeText);
if (string.IsNullOrEmpty(uniText))
{
uniText = Clipboard.GetData(DataFormats.UnicodeText) as string;
}
if (string.IsNullOrEmpty(uniText))
{
return;
}
pagVm.ProcessUnicodeText(subjectApplication.ActiveWindow.Selection, uniText);
}
}
catch (Exception ex)
{
Flogger.WriteError(Flogger.GetFlogDetail("EventSink.HandleExitScope", ex));
}
break;
}
So, once the Unicode Text format has been detected, the Selection and the uniText is passed to the ProcessUniicodeText(…) method where each line and tab is looped through at the same time as the Characters in the pasted shape text. Then the tab (CHAR(9)) and carriage return – line feed (CHAR(13) & CHAR(10)) can be inserted in the pasted text. Finally, the width, height and vertical alignment are updated to ensure that the shape encloses the text.
public void ProcessUnicodeText(Visio.Selection selection, string uniText)
{
if (selection.Count == 0 || string.IsNullOrEmpty(uniText))
{
return;
}
try
{
var shp = selection.PrimaryItem;
var vsoChars = shp.Characters;
// Now you can process tabs and newlines
string[] lines = uniText.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None);
var charPos = 0;
var maxTabCount = 0;
foreach (var line in lines)
{
string[] columns = line.Split('\t');
if (columns.Length - 1 > maxTabCount)
{
maxTabCount = columns.Length - 1;
}
//Console.WriteLine("Line:");
var colIndex = 0;
foreach (var col in columns)
{
charPos = charPos + col.Length;
vsoChars.Begin = charPos;
vsoChars.End = vsoChars.Begin;
colIndex++;
if (colIndex < columns.Length)
{
vsoChars.Text = ((char)9).ToString();
charPos = charPos + 1;
}
}
vsoChars.Begin = charPos;
vsoChars.End = vsoChars.Begin;
vsoChars.Text = ((char)13).ToString() + ((char)10).ToString();
charPos = charPos + 2;
}
shp.CellsSRC[(short)Visio.VisSectionIndices.visSectionObject,
(short)Visio.VisRowIndices.visRowText,
(short)Visio.VisCellIndices.visTxtBlkVerticalAlign].FormulaU = $"=0";
shp.CellsSRC[(short)Visio.VisSectionIndices.visSectionObject,
(short)Visio.VisRowIndices.visRowXFormOut,
(short)Visio.VisCellIndices.visXFormHeight].FormulaU = $"=TEXTHEIGHT(TheText,2)";
shp.CellsSRC[(short)Visio.VisSectionIndices.visSectionObject,
(short)Visio.VisRowIndices.visRowXFormOut,
(short)Visio.VisCellIndices.visXFormWidth].FormulaU = $"=TEXTWIDTH(TheText)";
}
catch (Exception ex)
{
Flogger.WriteError(Flogger.GetFlogDetail("PageVm.ProcessUnicodeText", ex));
}
}
This is a simple alternative to the Paste Special options (shown below), and maintains the text formatting present in the normal Paste task and allows the user to select parts of the text.
Of course, there could be many more options to post-processing pasted objects into the Visio page, but I just wanted to demonstrate how simple it can be within a Visio VSTO add-in.
Related articles
How SVG in Visio can cause a Shape.BoundingBox(…) error
I have used Visio’s Shape.BoundingBox(…) for many, many years and I cannot ever recall it failing, but I have now managed to create some shapes that cause it to error. This caused many hours of confusion, so I nearly abandoned using the method, until I discussed it with my fellow Visio MVP, John Goldsmith (see…
Optimize Visio Flowcharts: Swimlane Reordering Tips
Microsoft Visio desktop Plan 2 and Professional editions provides the ability to create and synchronize cross-functional flowcharts between the diagram and an Excel table. This is great, and widely used for many types of processes. The Excel table normally has a Function / Swimlane column that contains text that becomes labels on the swimlane containers,…
Refreshing the cached installed files of Visio
I have created many Visio solutions over the past 25 years and used a number of methods of creating an installation that includes Visio templates and stencils. I have just wasted many hours trying to debug an installation created with Advanced Installer until I realised that the problem was that Visio was not properly updating…
Linking Data to Shapes in Visio after using Data Visualizer
Data Visualizer (DV) in Visio Plan 2 (Data | Create from Data | Create ) is great because it provides a way of automatically creating a diagram from data, but it also prevents some of the other data-linking features in Visio from being used. This is because DV wants to take control of the data…
New Requirement for VBA Digital Signatures in Visio
Like most developers, I have to buy a new digital certificate every 3 years to sign my Visio add-ins and VBA projects. Usually that means verifying my bone fides, paying the fee and downloading the certificate, but security has been increased, and now, like everyone else, I have to use a USB key with it…
Using Visio Color by Value on Connectors
Data Graphics in Visio Plan 2 and Visio Professional is great, but it only enables us to use them with 2D shapes in Visio, i.e. not on connectors. So, what if you want to change the line colour of the connectors between the 2D shapes because of the data flowing between them? Well, it is…