Add hyperlink to automatically generated XML in Silverlight (C#) - c#

Recently, my question here was answered. Now that I've got my XML all parsed and looking pretty, I've got another question about my application I've been banging my head against a wall over the past few day(s).
The XML is used to automatically add Artist names to a listbox. What I want to do is provide links to Amazon searches from these artists. In the following function, the XML is parsed and the artist name is then added to the list. I need to somehow put a hyperlink on this artist name. Does anybody know how this would be possible?
EDIT: I am missing the connection between steps 2 and 3 in the answer that has been provided. Also, I do not understand how number 3 works at all. I must admit I'm a neophyte at Silverlight programming. From my understanding, you do the binding in the XAML page. How can this be done for listbox items that have not even been created yet?
Additionally, I realized something that the Amazon URLs use + signs where spaces are in artist names. I edited the code to reflect that. Please understand that having the hyperlink as text under each artist name is not what I'm going after. ;)
public void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null || e.Error.Message.IndexOf("NotFound") == -1)
{
ArtistsList.Items.Clear();
uname.Text = "Try Another One!";
XDocument doc = XDocument.Parse(e.Result);
var topArtists = from results in doc.Descendants("artist")
select results.Element("name").Value.ToString();
foreach (string artist in topArtists)
{
ArtistsList.Items.Add(artist);
string amazonPlus = artist.Replace(" ", "+");
string amazonURL = "http://www.amazon.ca/s/ref=nb_ss_gw?url=search-alias%3Daps&field-keywords=" + amazonPlus + "&x=0&y=0";
ArtistsList.Items.Add(amazonURL);
}
}
}
EDIT 2 Is there anybody who can clarify the answer provided?

1) Create an Artist object with a Name and Amazon Url Property
2) When you parse the XML, create a collection of items using LINQ.
var topArtists = from result in doc.Descendants("artists")
select new Artist
{
Name = result.Element("name").Value,
Amazon = new Uri(string.format("http://amazon.com/artist={0}", result.Element("name").Value), UriKind.Absolute),
};
ArtistList.ItemsSource = topArtists;
3) I would then use a data template to bind the Name to a TextBlock
Text or HyperlinkButton Content and the Amazon property to the
HyperlinkButton.NavigateUrl.

Related

Geckofx get specific links from a page

var links = new List<GeckoElement>();
foreach (var link in geckoWebBrowser1.Document.Links)
{
if (!String.IsNullOrEmpty(link.GetAttribute("href").ToString()))
links.Add(link);
}
}
I have this code to collect all links in the page, but I cant figure it out how can I filter to some specific links that starts with for example "ow.ly". Rest in the list should be ignored.
Tryed this but didnt seem to work
if (links.Count > 0)
{
if (links.Equals("ow.ly"))
{
}
}
When I debug if links equals ow.ly it shows 0 reults.
links is a a List<GeckoElement>. So it’s unlikely that the list equals to the string "ow.ly". Instead, you want to filter the list for items, which href property contains that text.
You could do that for example like this:
var owlyLinks = geckoWebBrowser1.Document.Links.Where(link =>
{
string hrefAttribute = link.GetAttribute("href").ToString();
return !string.IsNullOrEmpty(hrefAttribute) && hrefAttribute.Contains("ow.ly");
}).ToList();
You might want to adjust the check so that "ow.ly" needs to appear somewhere special instead of just somewhere inside the string. For example, you could parse the whole Url, and then check that the host name equals to ow.ly.

Calculate a new content type field

This is my first post ever here after many years of finding solutions to my issues.
I'm rather new to SharePoint, though not a complete beginner.
Here is what I want to achieve:
I want to create a list, which will be filled by users
This list will contain an Identifier text field, we'll call it strId
This list must contain a field, that's a value retrieved from an oracle database using strId as a parameter
I already did some BDC Models but this seems to be different, as the list elements do not come from a database, just one column does.
I thought about creating a content Type with 2 site columns in it, one with the strId and the other would do the calculation, but I can't seem to be able to do it.
Can anyone help on this matter ?
I would suggest the following:
create external content list : (eg: T_Investigator)
create new custom list (eg: ExternalDisplay)
add your strid as a single line of text
add the external content list as a lookup (eg: external_id), and add the field you wish to see as well (eg: firstname, lastname)
create a sharepoint solution with an eventreceiver with onAdding/onUpdating for the ExternalDisplay list. On adding / updating, you can overwrite the 'external_id' field with the new value in your strid
code:
using Microsoft.SharePoint;
namespace SOEventReceiver.ExternalTest
{
public class ExternalTest : SPItemEventReceiver
{
public override void ItemAdding(SPItemEventProperties properties)
{
string strid = (properties.AfterProperties["strid"] ?? string.Empty).ToString();
if (!string.IsNullOrWhiteSpace(strid))
{
properties.AfterProperties["external_id"] = strid + ";#" + strid;
}
base.ItemAdding(properties);
}
public override void ItemUpdating(SPItemEventProperties properties)
{
string strid = (properties.AfterProperties["strid"] ?? string.Empty).ToString();
if (!string.Equals(strid, properties.ListItem["strid"] ?? string.Empty))
{
properties.AfterProperties["external_id"] = strid + ";#" + strid;
}
base.ItemUpdating(properties);
}
}
}
with elements.xml pointing to the url of your list
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Receivers ListUrl="Lists/ExternalDisplay">
<Receiver>
<Name>ExternalTestItemAdding</Name>
<Type>ItemAdding</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>SOEventReceiver.ExternalTest.ExternalTest</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
<Receiver>
<Name>ExternalTestItemUpdating</Name>
<Type>ItemUpdating</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>SOEventReceiver.ExternalTest.ExternalTest</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
</Receivers>
</Elements>
Sharepoint automatically makes the changes since the Lookupfield value has changed, and the external columns are displayed.
The advantage here would be that sharepoint does the main magic, it also doesn't matter which kind of lookup list is behind this (external/sharepoint).
A disadvantage is that the external_id field could also be changed, which wouldn't update the strid field, and you would get inconsistent data. This you could however, also block inside the updating statement

Aspose.Words - MailMerge images

I am trying to loop through a Dataset, creating a page per item using Aspose.Words Mail-Merge functionality. The below code is looping through a Dataset - and passing some values to the Mail-Merge Execute function.
var blankDocument = new Document();
var pageDocument = new Document(sFilename);
...
foreach (DataRow row in ds.Tables[0].Rows){
var sBarCode = row["BarCode"].ToString();
var imageFilePath = HttpContext.Current.Server.MapPath("\\_temp\\") + sBarCode + ".png";
var tempDoc = (Document)pageDocument.Clone(true);
var fieldNames = new string[] { "Test", "Barcode" };
var fieldData = new object[] { imageFilePath, imageFilePath };
tempDoc.MailMerge.Execute(fieldNames, fieldData);
blankDocument.AppendDocument(tempDoc, ImportFormatMode.KeepSourceFormatting);
}
var stream = new MemoryStream();
blankDocument.Save(stream, SaveFormat.Docx);
// I then output this stream using headers,
// to cause the browser to download the document.
The mail merge item { MERGEFIELD Test } gets the correct data from the Dataset. However the actual image displays page 1's image on all pages using:
{ INCLUDEPICTURE "{MERGEFIELD Barcode }" \* MERGEFORMAT \d }
Say this is my data for the "Barcode" field:
c:\img1.png
c:\img2.png
c:\img3.png
Page one of this document, displays c:\img1.png in text for the "Test" field. And the image that is show, is img1.png.
However Page 2 shows c:\img2.png as the text, but displays img1.png as the actual image.
Does anyone have any insight on this?
Edit: It seems as this is more of a Word issue. When I toggle between Alt+F9 modes inside Word, the image actually displays c:\img1.png as the source. So that would be why it is being displayed on every page.
I've simplified it to:
{ INCLUDEPICTURE "{MERGEFIELD Barcode }" \d }
Also, added test data for this field inside Word's Mailings Recipient List. When I preview, it doesn't pull in the data, changing the image. So, this is the root problem.
I know this is old question. But still I would like to answer it.
Using Aspose.Words it is very easy to insert images upon executing mail merge. To achieve this you should simply use mergefield with a special name, like Image:MyImageFieldName.
https://docs.aspose.com/words/net/insert-checkboxes-html-or-images-during-mail-merge/#how-to-insert-images-from-a-database
Also, it is not required to loop through rows in your dataset and execute mail merge for each row. Simply pass whole data into MailMerge.Execute method and Aspose.Words will duplicate template for each record in the data.
Here is a simple example of such template
After executing mail merge using the following code:
// Create dummy data.
DataTable dt = new DataTable();
dt.Columns.Add("FirstName");
dt.Columns.Add("LastName");
dt.Columns.Add("MyImage");
dt.Rows.Add("John", "Smith", #"C:\Temp\1.png");
dt.Rows.Add("Jane", "Smith", #"C:\Temp\2.png");
// Open template, execute mail merge and save the result.
Document doc = new Document(#"C:\Temp\in.docx");
doc.MailMerge.Execute(dt);
doc.Save(#"C:\Temp\out.docx");
The result will look like the following:
Disclosure: I work at Aspose.Words team.
If this was Word doing the output, (not sure about Aspose), there would be two possible problems here.
INCLUDEPICTURE expects backslashes to be doubled up, e.g. "c\\img2.png", or (somewhat less reliable) to use forward slashes, or Mac ":" separators on that platform. It may be OK if the data comes in via a field result as you are doing here, though.
INCLUDEPICTURE results have not updated automatically "by design" since Microsoft modified a bunch of field behaviors for security reasons about 10 years ago. If you are merging to an output document, you can probably work around that by using the following nested fields:
{ INCLUDEPICTURE { IF TRUE "{ MERGEFIELD Barcode }" } }
or to remove the fields in the result document,
{ IF { INCLUDEPICTURE { IF TRUE "{ MERGEFIELD Barcode }" } } {
INCLUDEPICTURE { IF TRUE "{ MERGEFIELD Barcode }" } } }
All the { } need to be inserted with Ctrl+F9 in the usual way.
(Don't ask me where this use of "TRUE" is documented - as far as I know, it is not.)

Extracting inner value on CDATA with Linq to XML using a filter

I am using this code to retrieve the values I want from XML:
IEnumerable<ForewordReview> reviews = null;
try
{
reviews = from item in xmlDoc.Descendants("node")
select new ForewordReview()
{
PubDate = item.Element("created").ToString(),
Isbn = item.Element("isbn").ToString(),
Summary = item.Element("review").ToString()
};
} // ...
Incidentally, a client is now passing us almost every tag with CDATA which I need to extract:
<review>
<node>
<created>
<![CDATA[2012-01-23 12:40:57]]>
</created>
<isbn>
<![CDATA[123456789]]>
</isbn>
<summary>
<![CDATA[Teh Kittehs like to play in teh mud]]>
</summary>
</node>
</review>
I have seen a couple of solutions for extracting these values from within the CDATA tag, one of which is to use a where clause on the LINQ statement:
where element.NodeType == System.Xml.XmlNodeType.CDATA
I sort of see whats going on here, but I am not sure this works with how I am using Linq (specifically, building an object from selected items.
Do I need to apply this filter on the items in the select statement individually? Otherwise, I dont really understand how this will work with the code I am using.
As always, I appreciate the help.
Cast each XElement to a string instead:
reviews = from item in xmlDoc.Descendants("node")
select new
{
PubDate = (string)item.Element("created"),
Isbn = (string)item.Element("isbn"),
Summary = (string)item.Element("summary")
};
// Output:
// {
// PubDate = 2012-01-23 12:40:57,
// Isbn = 123456789,
// Summary = Teh Kittehs like to play in teh mud
// }
This also works with other data types, such as int, float, DateTime, etc:
reviews = from item in xmlDoc.Descendants("node")
select new
{
PubDate = (DateTime)item.Element("created")
};
// Output:
// {
// PubDate = 1/23/2012 12:40:57
// }
It also works with XAttributes as well.
Remember that there is no difference between the meaning of:
<a>
<b>Hello</b>
<c>& hello again</c>
</a>
and of
<a>
<b><![CDATA[Hello]]></b>
<c><![CDATA[& hello again]]></c>
</a>
Since you're calling ToString() and getting the entire content back - opening and closing tags, entity references, etc. still in XML form, then you must be prepared to deal with it in XML form. If not, the problem isn't with the code you show here, it's with the code that was okay with PubDate being "<created>2012-01-23 12:40:57</created>" and now
isn't okay with it being the exactly equivalent "";
Either change that code to really parse the XML (for which the framework offers lots of things to help), or change it to take the date on its own and use Element("created").Value to retrieve it.

Rendering C# Objects to Html

We have bunch of Domain Entities which should be rendered to an html format, which shows their detail in a pop up window.
I would be glad to do something like this:
Product product = new Product(...);
product.ToHtml(); // or: HtmlRenderer.Render(Product);
but my main problem is how to do this stuff from behind.
I have 3 different answers:
1. Render By Code:
I can simply write my code for rendering the Html inside the ToHtml Method (C#) - the problem it is that it is too static. if you would like to move a little bit the header to the middle you should change code.
moreover, it is very hard to read Html indentation in C#.
2. Using XSL:
XSL Files can easily manage the Html template and using XSLT I can transform XML file to the right place of the documents.
the parser already written by someone else (just need to learn the syntax)
** for this we will need that each object could serialize to Xml. and if the object changed -> the Xml will be changed --> the xslt need to be changed too
** this will also give me the option to indent the html easily for example: adding css capabilities and\or change the html design
3. using other template engine:
Write my own C# -> Html template Engine so it will read the template from file (*.template) and will insert the right property in the right place of the template using reflection.
** in this solution we have many issues that we can think of, for example: how the syntax should be like?
is this thing ok?
%Name% %Description%
and how we can handle arrays?
** maybe we can use an existing engine (Brail or T4-Templating)?
What do you prefer?
do you know a good engine?
for now I prefer the second solution, but it gonna be very slow.
thanks
I once had a need to render a collection of any type to an Html Table. I created an extension method on IEnumerable<T> that had overloads for css and the like. You can see my blog post about it here:
http://crazorsharp.blogspot.com/2009/03/cool-ienumberable-extension-method_25.html
It uses reflection to get all the properties of the object, and renders a nice little table. See if that would work for you.
[System.Runtime.CompilerServices.Extension()]
public string ToHtmlTable<T>(IEnumerable<T> list, string propertiesToIncludeAsColumns = "")
{
return ToHtmlTable(list, string.Empty, string.Empty, string.Empty, string.Empty, propertiesToIncludeAsColumns);
}
[System.Runtime.CompilerServices.Extension()]
public string ToHtmlTable<T>(IEnumerable<T> list, string tableSyle, string headerStyle, string rowStyle, string alternateRowStyle, string propertiesToIncludeAsColumns = "")
{
dynamic result = new StringBuilder();
if (String.IsNullOrEmpty(tableSyle)) {
result.Append("<table id=\"" + typeof(T).Name + "Table\">");
} else {
result.Append((Convert.ToString("<table id=\"" + typeof(T).Name + "Table\" class=\"") + tableSyle) + "\">");
}
dynamic propertyArray = typeof(T).GetProperties();
foreach (object prop in propertyArray) {
if (string.IsNullOrEmpty(propertiesToIncludeAsColumns) || propertiesToIncludeAsColumns.Contains(prop.Name + ",")) {
if (String.IsNullOrEmpty(headerStyle)) {
result.AppendFormat("<th>{0}</th>", prop.Name);
} else {
result.AppendFormat("<th class=\"{0}\">{1}</th>", headerStyle, prop.Name);
}
}
}
for (int i = 0; i <= list.Count() - 1; i++) {
if (!String.IsNullOrEmpty(rowStyle) && !String.IsNullOrEmpty(alternateRowStyle)) {
result.AppendFormat("<tr class=\"{0}\">", i % 2 == 0 ? rowStyle : alternateRowStyle);
} else {
result.AppendFormat("<tr>");
}
foreach (object prop in propertyArray) {
if (string.IsNullOrEmpty(propertiesToIncludeAsColumns) || propertiesToIncludeAsColumns.Contains(prop.Name + ",")) {
object value = prop.GetValue(list.ElementAt(i), null);
result.AppendFormat("<td>{0}</td>", value ?? String.Empty);
}
}
result.AppendLine("</tr>");
}
result.Append("</table>");
return result.ToString();
}
I can't agree more with John Feminella. Integrating Html rendering directly into your domain entities is a horrid pollution of your domain with a completely arbitrary and external concern. Like John said, you will make your entities very brittle by doing that, and break both of those critical rules: Separation of Concerns and Single Responsibility.
From the choices you gave, #3 is the closest to an appropriate way to do it. You need not write your own template engine. There are plenty free and ready-made template engines on the net that will do the job more than adequately (NVelocity, StringTemplate, Brail, etc. etc.)
Keep rendering where it belongs...in your presentation/view, and don't pollute your domain with higher level concerns.
This is the job of your view, not your controllers or models. As you suspect, using method (1) will make your changes very brittle. Instead, write a view fragment for each way in which you'd like to render a particular domain entity. Then simply pull in the appropriate views to build your final page.
I sincerely prefer method A. The HTML is already a standard so you shouldn't beat yourself up too much with intermediary formats such as XML (via XSL - method 2) or a custom format (method 3).
If writing in/for ASP.NET MVC you could however build an .ASCX (user control) that would accept an object of your type and render the appropiate HTML. And you don't get the nasty C# indentation and no highlighting nor autocomplete.
You could also write multiple User views to accomodate different scenarios for one object type.
Is there a reason you do not want to create a custom control .ascx file that takes domain entities object as an argument and then you can make the gui any way you want. This way you could dynamically add the controls to the page and just set a property to populate them.
Edit 1:
Just render the control to an HTMLTextWriter instead of placing it on the page.
StringBuilder outString = new StringBuilder();
StringWriter outWriter = new StringWriter(outString);
HtmlTextWriter htmlText = new HtmlTextWriter(outWriter);
System.Web.UI.WebControls.Label lbl1 = new System.Web.UI.WebControls.Label();
lbl1.Text = "This is just a test!";
lbl1.ToolTip = "Really";
lbl1.RenderControl(htmlText);
ViewData["Output"] = outString.ToString();
Output
<span title="Really">This is just a test!</span>
Of course use your custom control instead of a built in control but this was a quick and easy demo.
After bit of research i made my own function to convert Any Object toHtmlString.
VB .Net Code:
Public Function ToHtmlString(ByVal fObj As Object) As String
Dim pType = fObj.GetType()
Dim props As IList(Of PropertyInfo) = pType.GetProperties()
Dim value As Object = Nothing
Dim htmlString As String = "<html><body><form>"
For Each p In props
value = p.GetValue(fObj, Nothing)
If value <> Nothing Then
htmlString += String.Format("<input type=""hidden"" name=""{0}"" id=""{0}"" value=""{1}""/>", p.Name, value)
End If
Next
htmlString += "</form></body></html>"
Return htmlString.ToString()
it will get the property name and value from a particular object. you can manipulate html based on your
Personally, I'd combine method two and three: Just create a generic XML document using reflection, say:
<object type="xxxx">
<property name="ttt" value="vvv"/>
...
</object>
and use an XSTL stylesheet to create the actual HTML from this.

Categories