How to set HTML to clipboard in C#? - c#

I want to put rich text in HTML on the clipboard so when the users paste to Word, it will include the source HTML formatting.
Using the Clipboard.SetText method doesn't work.
Also, I would like that if users paste into a rich editor like Word it will paste formatted text, and if they paste into a plain editor like Notepad it will paste plain text.

When setting HTML text, you need to provide a header with additional information to what fragment of the html you actually want to paste while being able to provide additional styling around it:
Version:0.9
StartHTML:000125
EndHTML:000260
StartFragment:000209
EndFragment:000222
<HTML>
<head>
<title>HTML clipboard</title>
</head>
<body>
<!–StartFragment–><b>Hello!</b><!–EndFragment–>
</body>
</html>
With the header (and correct indexes), calling Clipboard.SetText with TextDataFormat.Html will do the trick.
To handle HTML and plain text pastes, you can’t use the Clipboard.SetText method, as it clears the clipboard each time it’s called; you need to create a DataObject instance, call its SetData method once with HTML and once with plain text, and then set the object to clipboard using Clipboard.SetDataObject.
Update
See "Setting HTML/Text to Clipboard revisited" for more details and ClipboardHelper implementation.

I found some code: https://www.experts-exchange.com/questions/21966855/Create-a-hyperlink-in-VB-net-copy-to-clipboard-Should-be-able-to-paste-hyperlink-in-Microsoft-Word-Excel.html
This code handles the problems of updating the start and end indexes.
Converted to c#:
public void AddHyperlinkToClipboard(string link, string description)
{
const string sContextStart = "<HTML><BODY><!--StartFragment -->";
const string sContextEnd = "<!--EndFragment --></BODY></HTML>";
const string m_sDescription = "Version:1.0" + Constants.vbCrLf + "StartHTML:aaaaaaaaaa" + Constants.vbCrLf + "EndHTML:bbbbbbbbbb" + Constants.vbCrLf + "StartFragment:cccccccccc" + Constants.vbCrLf + "EndFragment:dddddddddd" + Constants.vbCrLf;
string sHtmlFragment = "" + description + "";
string sData = m_sDescription + sContextStart + sHtmlFragment + sContextEnd;
sData = sData.Replace("aaaaaaaaaa", m_sDescription.Length.ToString().PadLeft(10, '0'));
sData = sData.Replace("bbbbbbbbbb", sData.Length.ToString().PadLeft(10, '0'));
sData = sData.Replace("cccccccccc", (m_sDescription + sContextStart).Length.ToString().PadLeft(10, '0'));
sData = sData.Replace("dddddddddd", (m_sDescription + sContextStart + sHtmlFragment).Length.ToString().PadLeft(10, '0'));
sData.Dump();
Clipboard.SetDataObject(new DataObject(DataFormats.Html, sData), true );
}

Let me share a helper for setting the clipboard data as HTML, which I've just come up with for my little side project #DevComrade:
var dataObject = new DataObject();
dataObject.SetData(DataFormats.Html, ClipboardFormats.ConvertHtmlToClipboardData(html);
Host.SetClipboardDataObject(dataObject);
internal static class ClipboardFormats
{
static readonly string HEADER =
"Version:0.9\r\n" +
"StartHTML:{0:0000000000}\r\n" +
"EndHTML:{1:0000000000}\r\n" +
"StartFragment:{2:0000000000}\r\n" +
"EndFragment:{3:0000000000}\r\n";
static readonly string HTML_START =
"<html>\r\n" +
"<body>\r\n" +
"<!--StartFragment-->";
static readonly string HTML_END =
"<!--EndFragment-->\r\n" +
"</body>\r\n" +
"</html>";
public static string ConvertHtmlToClipboardData(string html)
{
var encoding = new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
var data = Array.Empty<byte>();
var header = encoding.GetBytes(String.Format(HEADER, 0, 1, 2, 3));
data = data.Concat(header).ToArray();
var startHtml = data.Length;
data = data.Concat(encoding.GetBytes(HTML_START)).ToArray();
var startFragment = data.Length;
data = data.Concat(encoding.GetBytes(html)).ToArray();
var endFragment = data.Length;
data = data.Concat(encoding.GetBytes(HTML_END)).ToArray();
var endHtml = data.Length;
var newHeader = encoding.GetBytes(
String.Format(HEADER, startHtml, endHtml, startFragment, endFragment));
if (newHeader.Length != startHtml)
{
throw new InvalidOperationException(nameof(ConvertHtmlToClipboardData));
}
Array.Copy(newHeader, data, length: startHtml);
return encoding.GetString(data);
}
}
I used this and this references. Also, kudos #DaveyBoy for spotting a bug.

Arthur is right about the header, but the important thing to note here is that the data isn't going to be on the clipboard as plain text. You have to use CF_HTML. You can read about that at MSDN: http://msdn.microsoft.com/en-us/library/aa767917(v=vs.85).aspx
To be proper, you'd have a CF_TEXT showing simply: "Hello!", and then CF_HTML with the HTML header and data, as in Arthur's example.

As Arthur had mentioned I used the code at Setting HTML/Text to Clipboard revisited
I had to add linefeeds to the Header to get it to work (in this case VB)
Private Const Header As String = "Version:0.9" & vbCrLf & "StartHTML:<<<<<<<<1" & vbCrLf & "EndHTML:<<<<<<<<2" & vbCrLf & "StartFragment:<<<<<<<<3" & vbCrLf & "EndFragment:<<<<<<<<4" & vbCrLf & "StartSelection:<<<<<<<<3" & vbCrLf & "EndSelection:<<<<<<<<4"
Hope this helps

Related

Proper way to output HTML to the page

I realize this is probably a fundamental thing I should know but I am self-teaching myself C# and asp.net so I am a little lost at this point.
I have a stored procedure, which will return around 700-800 image URLs.
I need to build HTML like this for all the 800 image URLs and return the HTML to the page:
<div class="tile">
<img src="source.png" height="100" width="100" />
</div>
This is my code currently:
if (reader.HasRows)
{
string flagwallcontent = ""; //using a string to build html
while (reader.Read())
{
flagwallcontent = flagwallcontent + "<div class='tile'>";
flagwallcontent = flagwallcontent + "<img src='" + reader.GetString(0) + "' height='100' width='100'/>";
flagwallcontent = flagwallcontent + "</div>";
}
FlagWallLiteral.Text = flagwallcontent; //returning html to asp literal
}
I feel that this is not the efficient way to do it. Using string to build HTML and return HTML to asp literal. Can you suggest what the best way would be?
One way you could do it is to have an ASP.NET Panel (which renders as a <div>) to be the container for each of X hundred images you will add, like this:
if (reader.HasRows)
{
while (reader.Read())
{
// Create new image control
var newImage = new Image ();
newImage.ImageUrl = reader.GetString(0);
newImage.Height = 100;
newImage.Width = 100;
// Add new image to panel
Panel1.Controls.Add (newImage);
}
}
This allows ASP.NET to render the HTML while working with the strongly typed C# API.

Get file extension or "HasExtension" type bool from Uri object C#

Quick question:
Can anyone think of a better way then RegEx or general text searching to work out whether a Uri object (not URL string) has a file extension?
A Uri object generated from http://example.com/contact DOES NOT
A Uri object generated from http://example.com/images/logo.png DOES
Any thoughts welcome. Apologies if I've missed something in the .NET framework / Uri class that already does this.
Slightly more complexity wise.
A Uri object generated from http://example.com/contact.is.sortof.valid DOES NOT
A Uri object generated from http://example.com/images/logo.is.sort.of.valid.png DOES
I've accepted craigtp's answer; however, for what I need the solution is thus.
var hasExtension = Path.HasExtension(requestUri.AbsolutePath);
To all who had a go at this. For a full and comprehensive answer, you would obviously need a mime types dictionary to do a further check. For example http://example/this.is.sort.of.valid.but.not.a.mime.type would return "true" has Path.HasExtension, however, for what I need, I would never have this type of path coming in.
You can use the HasExtension method of the System.IO.Path class to determine if a Uri's string has an extension.
By using the AbsoluteUri property of the Uri object, you can retrieve the complete string that represents the Uri. Passing this string to the Path class's HasExtension method will correctly return a boolean indicating whether the Uri contains a file extension.
Copy and paste the following code into a simple console application to test this out. Only myUri3 and myUrl4 return True, which also demonstrates that the HasExtension method can correctly deal with additional characters (i.e. Querystrings) after the filename (and extension).
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Uri myURI1 = new Uri(#"http://www.somesite.com/");
Uri myURI2 = new Uri(#"http://www.somesite.com/filenoext");
Uri myURI3 = new Uri(#"http://www.somesite.com/filewithext.jpg");
Uri myURI4 = new Uri(#"http://www.somesite.com/filewithext.jpg?q=randomquerystring");
Console.WriteLine("Does myURI1 have an extension: " + Path.HasExtension(myURI1.AbsoluteUri));
Console.WriteLine("Does myURI2 have an extension: " + Path.HasExtension(myURI2.AbsoluteUri));
Console.WriteLine("Does myURI3 have an extension: " + Path.HasExtension(myURI3.AbsoluteUri));
Console.WriteLine("Does myURI4 have an extension: " + Path.HasExtension(myURI4.AbsoluteUri));
Console.ReadLine();
}
}
}
EDIT:
Based upon the question asker's edit regarding determining if the extension is a valid extension, I've whipped up some new code below (copy & paste into a console app):
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Uri myUri1 = new Uri("http://www.somesite.com/folder/file.jpg?q=randomquery.string");
string path1 = String.Format("{0}{1}{2}{3}", myUri1.Scheme, Uri.SchemeDelimiter, myUri1.Authority, myUri1.AbsolutePath);
string extension1 = Path.GetExtension(path1);
Console.WriteLine("Extension of myUri1: " + extension1);
Uri myUri2 = new Uri("http://www.somesite.com/folder/?q=randomquerystring");
string path2 = String.Format("{0}{1}{2}{3}", myUri2.Scheme, Uri.SchemeDelimiter, myUri2.Authority, myUri2.AbsolutePath);
string extension2 = Path.GetExtension(path2);
Console.WriteLine("Extension of myUri1: " + extension2);
Console.ReadLine();
}
}
}
This new code now de-constructs all of the component parts of a Uri object (i.e. Scheme - the http part etc.) and specifically removes any querystring part of the Uri. This gets around the potential problem as noted by Adriano in a comment on this answer that the querystring could contain a dot character (thereby potentially messing up the HasExtension method).
Once the Uri is de-constructed, we can now properly determine both if the Uri string has an extension and also what that extension is.
From here, it's merely a case of matching this extension against a list of known valid extensions. This part is something that the .NET framework will never given you as any file extension is potentially valid (any application can make up it's own file extension if it so desires!)
The Uri.IsFile property suggested by others does not work.
From the docs
The IsFile property is true when the Scheme property equals UriSchemeFile.
file://server/filename.ext"
http://msdn.microsoft.com/en-us/library/system.uri.isfile.aspx
What you can do is get the AbsolutePath of the URI (which corresponds to /contact or /images/logo.png for example) and then use the FileInfo class to check/get the extension.
var uris = new List<Uri>()
{
new Uri("http://mysite.com/contact"),
new Uri("http://mysite.com/images/logo.png"),
new Uri("http://mysite.com/images/logo.png?query=value"),
};
foreach (var u in uris)
{
var fi = new FileInfo(u.AbsolutePath);
var ext = fi.Extension;
if (!string.IsNullOrWhiteSpace(ext))
{
Console.WriteLine(ext);
}
}
You probably need to check against a list of supported extensions to handle the more complicated cases (contact.is.sortof.valid and contact.is.sortof.valid.png)
Tests:
"http://mysite.com/contact" //no ext
"http://mysite.com/contact?query=value" //no ext
"http://mysite.com/contact?query=value.value" //no ext
"http://mysite.com/contact/" //no ext
"http://mysite.com/images/logo.png" //.png
"http://mysite.com/images/logo.png?query=value" //.png
"http://mysite.com/images/logo.png?query=value.value" //.png
"http://mysite.com/contact.is.sortof.valid" //.valid
"http://mysite:123/contact.is.sortof.valid" //.valid
Take a look at the UriBuilder Class. Not only can you retrieve certain parts of the url, but you can also swap them out at will.
public bool HasExtension(Uri myUri)
{
var validExtensions = new List<string>() { ".png", ".jpg" };
var builder = UriBuilder(myUri)
foreach (var extension in validExtensions) {
if(builder.Path.Equals(extension, StringComparison.InvariantCultureIgnoreCase))
return true;
return false;
}
here is my solution to make it right ;)
var inputString = ("http://ask.com/pic.JPG http://aSk.com/pIc.JPG "
+ "http://ask.com/pic.jpg "
+ "http://yoursite.com/contact "
+ "http://yoursite.com/contact?query=value "
+ "http://yoursite.com/contact?query=value.value "
+ "http://yoursite.com/contact/ "
+ "http://yoursite.com/images/Logo.pnG "
+ "http://yoursite.com/images/lOgo.pNg?query=value "
+ "http://yoursite.com/images/logo.png?query=value.value "
+ "http://yoursite.com/contact.is.sortof.valid "
+ "http://mysite:123/contact.is.sortof.valid").Split(' ');
var restultString = "";
foreach (var is1 in inputString)
{
restultString += (!string.IsNullOrEmpty(restultString) ? " " : "") +
(Path.HasExtension(is1) ? Path.ChangeExtension(is1, Path.GetExtension(is1).ToLower()) : is1);
}

C# Added Ajax Update Panel but Anchor will not run server-side code once placeholder is populated

I've just been told I need to update my links from posting back so I added an Ajax Update Panel to handle the requests instead so there isn't a postback. My links are generated from the codebehind and populate a placeholder. I need to update the code so that it will be able to run server side code. I have tried programmatically adding via controls.add but I couldn't get anything to run server side code.
while (myReader.Read())
{
try
{
eventListCounter++;
string strEventTitle = myReader["eventTitle"].ToString();
string strEventThumb = myReader["eventThumb"].ToString();
string strEventInfo = myReader["eventInfo"].ToString();
int eventID = Int32.Parse(myReader["ID"].ToString());
strHTML += "<div class='styleWeekRedCarpetEventBox'><div class='styleWeekRedCarpetPictureBox'>";
strHTML +="<a href='RedCarpet.aspx?eventID=" + eventID.ToString() +"' runat='server' id='linkEventShowImageSet'><img Width='125' Height='95'src='Images/" + strEventThumb.ToString() + "' border='0'></a></div><div id='styleWeekRedCarpetPictureBoxText'><p><a href='RedCarpet.aspx?eventID=" + eventID + "' runat='server' id='linkEventShowImageSet'><b>" + strEventTitle.ToString() + "</b><br />" + strEventInfo.ToString() + "</a></p></div></div><br><br>";
if (eventListCounter == 5)
{
strHTML += "</div><div class=\"eventListColumn\">";
eventListCounter = 0;
}
}
catch (Exception strError)
{
Response.Write(strError.ToString());
}
}
LiteralControl eventListPlaceholder = new LiteralControl(strHTML.ToString());
PlaceHolder2.Controls.Add(eventListPlaceholder);
This looks like the perfect place to use a repeater control, you should shy away from using Response.Write and let the framework abstract that away from you.
There is a high chance this this will solve the event not being fired server-side.

XSLT for displaying XML as HTML

I have an XML document (in a .net C# web app) that I want to display in its simplistic form (similar to IE). I need it to be browser independent (or as much as possible) and have a consistent display.
I have tried pushing the xml document through as "text/xml" and this is inconsistent and doens't work in some browsers.
http://sources.redhat.com/ml/xsl-list/2002-02/msg00831.html
A link here provided a good way of transforming the XML to HTML/XHTML using stylesheets.
However the stylesheet provided didnt work.
First: Is this the best way to do it? Are there better solutions?
Second: If not, does anyone know where I can find the XSLT?
EDIT (clarification): The XSLT I refer to will transform the XML into the Internet Explorer style display of XML files.
Thanks in advance! :)
I decided my best approach (for my situation) was to write 2 simple C# methods to generate HTML from the XML on the server side. This helps reduce any reliance on browser displaying XML (Opera is Crazy!)
(This example uses pretty simple formatting, which was fine for my situation.
I loosely modelled it on IE's display of XML.
For others' reference, here are the methods:
/// <summary>
/// Does a simple convert to display an Xml document in HTML.
/// </summary>
/// <param name="xmlString"></param>
private static string ConvertXmlToHtml(string xmlString)
{
StringBuilder html = new StringBuilder();
html.AppendLine("<HTML>");
html.AppendLine("<HEAD><TITLE>Xml Document</TITLE></HEAD>");
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlString);
html.AppendLine(ConvertXmlElementToHTML(1, doc.DocumentElement));
html.AppendLine("</HTML>");
return html.ToString();
}
/// <summary>
/// Converts an XML element (and all of its children) to HTML.
/// This is a recursive method.
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private static string ConvertXmlElementToHTML(int level, XmlNode element)
{
int padding = level; // padding (cm == level).
StringBuilder returnHTML = new StringBuilder();
if (element is XmlElement)
{
// Formatting for symbols to simplify code below.
string close_bracket = "<SPAN style=\"color: blue\">></SPAN>";
string close_bracket_no_children = "<SPAN style=\"color: blue\"> /></SPAN>";
string open_bracket = "<SPAN style=\"color: blue\"><</SPAN>";
string open_bracket_end_el = "<SPAN style=\"color: blue\"></</SPAN>";
string el_name = "<SPAN style=\"color: brown\">" + element.Name + "</SPAN>";
string quote = "<SPAN style=\"color: blue\">\"</SPAN>";
string equals_sign = "<SPAN style=\"color: blue\">=</SPAN>";
// Open Element.
returnHTML.AppendLine("<DIV style=\"margin-left: " + padding + "cm\">" + open_bracket + el_name);
// Print element attributes.
foreach(XmlAttribute att in element.Attributes)
{
returnHTML.AppendLine(" <SPAN style=\"color: brown\">" + att.Name + "</SPAN>" + equals_sign + quote + "<SPAN style=\"color: black; text-weight: bold\">" + att.Value + "</SPAN>" + quote);
}
// If no children, we end the element here with a '/ >'
// otherwise, we close the element and start to write children '>'
if (element.ChildNodes.Count == 0)
{
returnHTML.AppendLine(close_bracket_no_children + "</DIV>");
}
else
{
returnHTML.AppendLine(close_bracket + "</DIV>");
}
// Print Children. (Recursive call). Note location is IMPORTANT: we need child elements
// to print after the element is opened and before the element is closed.
foreach (XmlNode child in element.ChildNodes)
{
returnHTML.AppendLine(ConvertXmlElementToHTML(level + 1, child));
}
// If we have printed child elements, we need to print a closing element tag.
if (element.ChildNodes.Count > 0)
{
returnHTML.AppendLine("<DIV style=\"margin-left: " + padding + "cm\">" + open_bracket_end_el + el_name + close_bracket + "</DIV>");
}
}
// Return a string of HTML that will display elements at this level and below (child nodes).
return returnHTML.ToString();
}
The stylesheet you are trying to use only works in modern browsers and won't work in any version of IE. Doing such things in modern browsers is trivial but in IE you need to call MSXML through javascript and .NET stuff in order to do the same transformations native to other far more modern browsers (Firefox, Safari, Chrome, Opera, K-Meleon, Konqueror, Epiphany, Flock....you get the idea). This is all part of the problem IE causes by not implementing XHTML which, essentially, is what you want to do and every other browser does.
I'll check back in the morning if you still haven't solved this with javascript I use.
I am about to test it but I found this link:
here
EDIT: Found out only works wiht (some) vresions of IE.grr!

Accessing the Sharepoint Lists Web Service from .NET

I have an InfoPath form with custom submit code to update a Sharepoint list by calling the Sharepoint Lists web service. The code runs without any exceptions, and I was able to set breakpoints to make sure that the variables contain the correct values before being sent to the web service. The values never get added to the Sharepoint list, though. Here is my code:
[InfoPathEventHandler(MatchPath = "Submit", EventType = InfoPathEventType.OnClick)]
public void Submit_OnClick(DocActionEvent e)
{
ListsService.Lists listService = new Risk_Form.ListsService.Lists();
listService.Credentials = System.Net.CredentialCache.DefaultCredentials;
string riskID = thisXDocument.DOM.selectSingleNode("//my:myFields/my:RiskID").text;
string headline = thisXDocument.DOM.selectSingleNode("//my:myFields/my:RiskHeadline").text;
XmlDocument doc = new XmlDocument();
XmlElement batch = doc.CreateElement("Batch");
batch.SetAttribute("OnError", "Continue");
batch.SetAttribute("ListVersion", "1");
batch.InnerXml =
"<Method ID='" + riskID + "' Cmd='New'>" +
"<Field Name='RiskID'>" + riskID + "</Field>" +
"<Field Name='Headline'>" + headline + "</Field>" +
"</Method>";
try
{
// Update list using the list's GUID
listService.UpdateListItems("2F6CA5F4-D78A-4716-B111-507917CF89E4", batch);
}
catch(Exception ex)
{
thisXDocument.DOM.selectSingleNode("//my:myFields/my:RiskStatement").text = ex.Message;
}
}
Two things:
You might also need the default View ID in your batch when calling UpdateListItems().
Instead of hardcoding the list guid, you can obtain it programatically by calling listService.GetListAndView().
Here is some code to demonstrate both items:
System.Xml.XmlNode ndListView = listService.GetListAndView(DISPLAYNAMEOFLIST, "");
string listGuid = ndListView.ChildNodes[0].Attributes["Name"].Value;
string listView = ndListView.ChildNodes[1].Attributes["Name"].Value;
batch.SetAttribute("ViewName", listView);
You can then just call UpdateListItems() with listGuid and batch.
Ok, I finally figured this stupid bug out. There was a list on the root Sharepoint site with the same display name as the list I was trying to access on my subsite. Even though my service reference pointed to the Lists web service located on my subsite, it was still returning the wrong list. I used the internal name for my list and now it works.
From the documentation on MSDN: It is recommended that you use the list GUID surrounded by curly braces (i.e., "{GUID}"), but you can also use the list display name.
Those curly braces seem to be missing in your call.
I found a partial answer to my problem. When I added the service reference to the subsite I am working on, for some reason app.config still contained a reference to the root Sharepoint site. Therefore the list I was looking for did not exist. Now I'm having another problem, though. I check the return value of the UpdateListItems() call, and I get the following error: "One or more field types are not installed properly. Go to the list settings page to delete these fields." I searched around and all of the problems that cause this error seem to involve having a field name with a space in it. Neither of my fields have spaces in them, though. Here is my updated code:
ListsService.Lists listService = new Risk_Form.ListsService.Lists();
listService.Credentials = System.Net.CredentialCache.DefaultCredentials;
XmlNode list = null;
list = listService.GetListAndView("Risks", "");
string listID = list.ChildNodes[0].Attributes["Name"].Value;
string viewID = list.ChildNodes[1].Attributes["Name"].Value;
string riskID = thisXDocument.DOM.selectSingleNode("//my:myFields/my:RiskID").text;
string headline = thisXDocument.DOM.selectSingleNode("//my:myFields/my:RiskHeadline").text;
XmlDocument doc = new XmlDocument();
XmlElement batch = doc.CreateElement("Batch");
batch.SetAttribute("OnError", "Continue");
batch.SetAttribute("ListVersion", "1");
batch.SetAttribute("ViewName", viewID);
batch.InnerXml =
"<Method ID='1' Cmd='New'>" +
"<Field Name='RiskID'>" + riskID + "</Field>" +
"<Field Name='Headline'>" + headline + "</Field>" +
"</Method>";
XmlNode ret = listService.UpdateListItems(listID, batch);
MessageBox.Show(ret.OuterXml);

Categories

Resources