XSLT for displaying XML as HTML - c#

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!

Related

Is there any way to do backspace twice to clear a text field using selenium webdriver through C#

I have a text field that contains a 2 digit value by default. I want to clear it before I type a new value. I was using TextSlider.Clear(); but after the latest ChromeDriver update, it's no longer working so I am trying to workaround it using backspace. Currently I am doing two backspaces, one at a time.
TextSlider.SendKeys(Keys.Backspace);
TextSlider.SendKeys(Keys.Backspace);
I also tried DELETE but that's also not working. Is there any way to do this in a single line?
Thank you all,
i have managed to workaround using ctrl A and Delete
TextSlider.SendKeys(Keys.Control + "a");
TextSlider.SendKeys(Keys.Delete);
TextSlider.SendKeys(Keys.Backspace + Keys.Backspace);
First try to fix like how TextSlider.Clear(); is not working. There might me loading issue, SendKeys method will work. Try to add wait for page to load properly.
If still not working then you can use,
TextSlider.Click();
TextSlider.Clear();
But below functionality will definatly work,
TextSlider.SendKeys(Keys.Backspace + Keys.Backspace);
Instead of using Keys.Backspace, ideally to clear a text field you need to induce WebDriverWait for the element to be clickable and you can use either of the following solutions:
Using ElementToBeClickable Method (IWebElement):
new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.ElementToBeClickable(TextSlider)).Clear();
Using ElementToBeClickable Method (By):
new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(By.CssSelector("css_TextSlider")).Clear();
Another option is to clear the text element by using Javascript. Due to issues occurring in certain parallel testing situations, I stopped relying on the SendKeys function some time ago. Instead, I use these functions now to set a certain text:
private void SetText(IWebElement element, string text, bool clearOldText)
{
// Clear old text if needed
if (clearOldText)
{
LogInfo("Clearing " + element.ToString() + #" from any text.");
SetElementValue(element, "");
}
element.Click();
SetElementValue(element, text);
}
public string SetElementValue(IWebElement element, string value)
{
ScrollToElement(element);
PaintElement(element, "yellow");
var exec = (IJavaScriptExecutor)this;
var script = #"
var el = arguments[0];
el.value = '" + value + #"';
try
{
if (""createEvent"" in document) {
var evt = document.createEvent(""HTMLEvents"");
evt.initEvent(""change"", false, true);
el.dispatchEvent(evt);
}
else
el.fireEvent(""onchange"");
}
catch(err){ return err; }
return ""Javascript executed."";
";
LogInfo("Setting value to '" + value + "' for " + element.ToString());
var result = exec.ExecuteScript(script, element);
Recorder?.AddScreenshot();
return result.ToString();
}
Personally I dislike the hardcoded javascript a bit, but it always did the job reliably. "SetElementValue" is called twice in this code to ensure correct handling of certain events in my tests: it might not be necessary in other cases.

How To Write To A OneNote 2013 Page Using C# and The OneNote Interop

I have seen many articles about this but all of them are either incomplete or do not answer my question. Using C# and the OneNote Interop, I would like to simply write text to an existing OneNote 2013 Page. Currently I have a OneNote Notebook, with a Section titled "Sample_Section" and a Page called "MyPage".
I need to be able to use C# code to write text to this Page, but I cannot figure out how or find any resources to do so. I have looked at all of the code examples on the web and none answer this simple question or are able to do this. Also many of the code examples are outdated and break when attempting to run them.
I used the Microsoft code sample that shows how to change the name of a Section but I cannot find any code to write text to a Page. There is no simple way to do this that I can see. I have taken a lot of time to research this and view the different examples online but none are able to help.
I have already viewed the MSDN articles on the OneNote Interop as well. I vaguely understand how the OneNote Interop works through XML but any extra help understanding that would also be appreciated. Most importantly I would really appreciate a code example that demonstrates how to write text to a OneNote 2013 Notebook Page.
I have tried using this Stack Overflow answer:
Creating new One Note 2010 page from C#
However, there are 2 things about this solution that do not answer my question:
1) The marked solution shows how to create a new page, not how to write text to it or how to populate the page with any information.
2) When I try to run the code that is marked as the solution, I get an error at the following line:
var node = doc.Descendants(ns + nodeName).Where(n => n.Attribute("name").Value == objectName).FirstOrDefault();
return node.Attribute("ID").Value;
The reason being that the value of "node" is null, any help would be greatly appreciated.
I asked the same question on MSDN forums and was given this great answer. Below is a nice, clean example of how to write to OneNote using C# and the OneNote interop. I hope that this can help people in the future.
static Application onenoteApp = new Application();
static XNamespace ns = null;
static void Main(string[] args)
{
GetNamespace();
string notebookId = GetObjectId(null, OneNote.HierarchyScope.hsNotebooks, "MyNotebook");
string sectionId = GetObjectId(notebookId, OneNote.HierarchyScope.hsSections, "Sample_Section");
string firstPageId = GetObjectId(sectionId, OneNote.HierarchyScope.hsPages, "MyPage");
GetPageContent(firstPageId);
Console.Read();
}
static void GetNamespace()
{
string xml;
onenoteApp.GetHierarchy(null, OneNote.HierarchyScope.hsNotebooks, out xml);
var doc = XDocument.Parse(xml);
ns = doc.Root.Name.Namespace;
}
static string GetObjectId(string parentId, OneNote.HierarchyScope scope, string objectName)
{
string xml;
onenoteApp.GetHierarchy(parentId, scope, out xml);
var doc = XDocument.Parse(xml);
var nodeName = "";
switch (scope)
{
case (OneNote.HierarchyScope.hsNotebooks): nodeName = "Notebook"; break;
case (OneNote.HierarchyScope.hsPages): nodeName = "Page"; break;
case (OneNote.HierarchyScope.hsSections): nodeName = "Section"; break;
default:
return null;
}
var node = doc.Descendants(ns + nodeName).Where(n => n.Attribute("name").Value == objectName).FirstOrDefault();
return node.Attribute("ID").Value;
}
static string GetPageContent(string pageId)
{
string xml;
onenoteApp.GetPageContent(pageId, out xml, OneNote.PageInfo.piAll);
var doc = XDocument.Parse(xml);
var outLine = doc.Descendants(ns + "Outline").First();
var content = outLine.Descendants(ns + "T").First();
string contentVal = content.Value;
content.Value = "modified";
onenoteApp.UpdatePageContent(doc.ToString());
return null;
}
This is just what I've gleaned from reading examples on the web (of course, you've already read all of those) and peeking into the way OneNote stores its data in XML using ONOMspy (http://blogs.msdn.com/b/johnguin/archive/2011/07/28/onenote-spy-omspy-for-onenote-2010.aspx).
If you want to work with OneNote content, you'll need a basic understanding of XML. Writing text to a OneNote page involves creating an outline element, whose content will be contained in OEChildren elements. Within an OEChildren element, you can have many other child elements representing outline content. These can be of type OE or HTMLBlock, if I'm reading the schema correctly. Personally, I've only ever used OE, and in this case, you'll have an OE element containing a T (text) element. The following code will create an outline XElement and add text to it:
// Get info from OneNote
string xml;
onApp.GetHierarchy(null, OneNote.HierarchyScope.hsSections, out xml);
XDocument doc = XDocument.Parse(xml);
XNamespace ns = doc.Root.Name.Namespace;
// Assuming you have a notebook called "Test"
XElement notebook = doc.Root.Elements(ns + "Notebook").Where(x => x.Attribute("name").Value == "Test").FirstOrDefault();
if (notebook == null)
{
Console.WriteLine("Did not find notebook titled 'Test'. Aborting.");
return;
}
// If there is a section, just use the first one we encounter
XElement section;
if (notebook.Elements(ns + "Section").Any())
{
section = notebook.Elements(ns + "Section").FirstOrDefault();
}
else
{
Console.WriteLine("No sections found. Aborting");
return;
}
// Create a page
string newPageID;
onApp.CreateNewPage(section.Attribute("ID").Value, out newPageID);
// Create the page element using the ID of the new page OneNote just created
XElement newPage = new XElement(ns + "Page");
newPage.SetAttributeValue("ID", newPageID);
// Add a title just for grins
newPage.Add(new XElement(ns + "Title",
new XElement(ns + "OE",
new XElement(ns + "T",
new XCData("Test Page")))));
// Add an outline and text content
newPage.Add(new XElement(ns + "Outline",
new XElement(ns + "OEChildren",
new XElement(ns + "OE",
new XElement(ns + "T",
new XCData("Here is some new sample text."))))));
// Now update the page content
onApp.UpdatePageContent(newPage.ToString());
Here's what the actual XML you're sending to OneNote looks like:
<Page ID="{20A13151-AD1C-4944-A3D3-772025BB8084}{1}{A1954187212743991351891701718491104445838501}" xmlns="http://schemas.microsoft.com/office/onenote/2013/onenote">
<Title>
<OE>
<T><![CDATA[Test Page]]></T>
</OE>
</Title>
<Outline>
<OEChildren>
<OE>
<T><![CDATA[Here is some new sample text.]]></T>
</OE>
</OEChildren>
</Outline>
</Page>
Hope that helps get you started!
If you're using C#, Check out the newer OneNote REST API at http://dev.onenote.com. It already supports creating a new page and has a beta API to patch and add content to an existing page.

Using XhtmlTextWriter with XmlTextReader

After reading this article, I have decided to update the following code (using XmlDocument) with XmlReader:
Rendering controls
public string Rendering(Control baseControl)
{
StringBuilder stringBuilder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(stringBuilder))
using (XhtmlTextWriter htmlWriter = new XhtmlTextWriter(stringWriter))
{
baseControl.RenderControl(htmlWriter);
return PretifyWithNewlines(stringBuilder.ToString());
}
}
Adding newline after each node
private string PretifyWithNewlines(string minifiedMarkup)
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.XmlResolver = null;
try
{
xmlDocument.LoadXml("<base>" + minifiedMarkup + "</base>");
}
catch // when minifiedMarkup contains the whole HTML with DTD tag defined,
{ // it throws an exception with <base>
xmlDocument.LoadXml(minifiedMarkup);
}
return recursiveOperation(xmlDocument.ChildNodes)
.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine)
.Replace(Environment.NewLine + "<base>" + Environment.NewLine, "")
.Replace(Environment.NewLine + "</base>" + Environment.NewLine, "");
}
Recursively traverse each node and plant new element
private static string recursiveOperation(XmlNodeList xmlNodeList)
{
string result = "";
foreach (XmlNode currentNode in xmlNodeList)
{
XmlNode clonedNode = currentNode;
string interimMarkup = recursiveOperation(currentNode.ChildNodes);
try
{
clonedNode.InnerXml = interimMarkup;
}
finally
{
result += Environment.NewLine + clonedNode.OuterXml + Environment.NewLine;
}
}
return result;
}
Questions:
Is there a room for optimizing the existing code?
How would I go about directly instantiating XmlTextReader from Control, StringWriter or XhtmlTextWriter object? Or do I really need to render it as a string first then instantiate XmlTextReader?
Edit
As per Jon Skeet's answer, here is the update. The idea is to implode a newline after each element:
Before prettify:
<div class="tag"><span>text<span class="another-span"></span></span></div><div>Text<img src="some/relative/URL/" />namely</div>
After prettify:
<div class="tag">
<span>
text
<span class="another-span"></span>
</span>
</div>
<div>
Text
<img src="some/relative/URL/" />
namely
</div>
Notice how span.another-span keep collapsed while everything else (with child nodes) expanded. The indentation will be asserted by Visual Studio.
Is there a room for optimizing the existing code?
Absolutely. The first place I'd change has nothing to do with how you load the XML - it's string concatenation. I'd change your recursiveOperation method to:
private static string RecursiveOperation(XmlNodeList xmlNodeList)
{
StringBuilder result = new StringBuilder();
foreach (XmlNode currentNode in xmlNodeList)
{
XmlNode clonedNode = currentNode;
// Remove try/finally block - if an exception is thrown your
// result will be lost anyway
string interimMarkup = RecursiveOperation(currentNode.ChildNodes);
clonedNode.InnerXml = interimMarkup;
result.Append(Environment.NewLine)
.Append(clonedNode.OuterXml)
.Append(Environment.NewLine);
}
return result.ToString();
}
It's possible you could optimize this further using a single StringBuilder passed into RecursiveOperation, but I haven't quite got to grips with your code sufficiently to say yet. (It's before the first coffee of the morning.)
In terms of the XML handling itself, you're currently doing a lot of reparsing by setting the OuterXml node in each child (recursively). I suspect if I had a better grasp of what you were doing, it would be feasible to change the whole approach. Given that this is functionality that XmlReader really doesn't have (it wouldn't make sense), it's not clear that you should be taking much notice of the other article at the moment.
How would I go about directly instantiating XmlTextReader from Control, StringWriter or XhtmlTextWriter object? Or do I really need to render it as a string first then instantiate XmlTextReader?
It's not clear what it would even mean to create an XmlTextReader from any of those objects - they're not inherently a source of XML data. I think what you've got already looks reasonable to me.
If you're still concerned about the performance, you should avoid guesswork and use a profiler to measure where the time is being taken. You should set yourself a target first though, otherwise you won't know when you've finished optimizing.

How to set HTML to clipboard in 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

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