I'm currently refactoring a console application whose main responsibility is to generate a report based on values stored in the database.
The way I've been creating the report up til now is as follows:
const string format = "<tr><td>{0, 10}</td><td>
{1}</td><td>{2, 8}</td><td>{3}</td><td>{4, -30}</td>
<td>{5}</td><td>{6}</td></tr>";
if(items.Count > 0)
{
builder.AppendLine(
String.Format(format, "Date", "Id", "WorkItemId",
"Account Number", "Name", "Address", "Description"));
}
foreach(Item item in items)
{
builder.AppendLine(String.Format(format, item.StartDate, item.Id,
item.WorkItemId, item.AccountNumber,
String.Format("{0} {1}",
item.FirstName, item.LastName),
item.Address, item.Description));
}
string report = String.Format("<html><table border=\"1\">{0}
</table></html>",
builder.ToString());
(The above is just a sample...and sorry about the formatting...I tried to format it so it wouldn't require horizontal scrolling....)
I really don't like that way I've done this. It works and does the job for now...but I just don't think it is maintainable...particularly if the report becomes any more complex in terms of the html that needs to be created. Worse still, other developers on my team are sure to copy and paste my code for their applications that generate an html report and are likely to create a horrible mess. (I've already seen such horrors produced! Imagine a report function that has hundreds of lines of hard coded sql to retrieve the details of the report...its enough to make a grown man cry!)
However, while I don't like this at all...I just can't think of a different way to do it.
Surely there must be a way to do this...I'm certain of it. Not too long ago I was doing the same thing when generating tables in aspx pages until someone kindly showed me that I can just bind the objects to a control and let .NET take care of the rendering. It turned horrible code, similar to the code above, into two or three elegant lines of goodness.
Does anyone know of a similar way of creating the html for this report without hard-coding the html?
Make your app to produce XML file with raw data. Then apply an external XSLT to it which would contain HTML.
More info: http://msdn.microsoft.com/en-us/library/14689742.aspx
You could use a template engine like NVelocity to separate your report view and your code.
There are probably other decent template engines out there...
meziod - Another avenue to peruse is extension methods to the HtmlTextWriter object. I found a brilliant stab at just this on this very site.
HtmlTextWriter extension
I'm certain that you could leverage great potential from that...
regards - coola
Well, you could use one of the report frameworks (Crystal, MS RDL, etc) and export as html - however, I suspect that for simple data your current approach is less overhead. I might use an XmlWriter or LINQ-to-XML (rather than string.Format, which won't handle escaping)...
new XElement("tr",
new XElement("td", item.StartDate),
new XElement("td", item.Id),
new XElement("td", item.WorkItemId),
etc. Escaping is especially important for text values (name, description, etc).
You might consider using a simple template engine such as http://www.stefansarstedt.com/templatemaschine.html and separate your template from the content.
This is quite practical, allows template modification without recompiling and you still got C# power in your templates.
Microsoft SQL Reporting Services does this quite well, and can do multiple formats.
My company uses it to create PDF reports and since we have HIPAA requirements, we automatically put a password to it via a third party PDF control...
As coolashaka already mentioned, using the HtmlTextWriter is a good option, certainly if you add some useful extension methods, for example:
Simple example:
public static void WriteNav(this
HtmlTextWriter writer, List<String> navItems)
{
writer.RenderBeginTag("nav");
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
foreach (var item in navItems)
{
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
writer.AddAttribute(HtmlTextWriterAttribute.Href, "~/" + item + ".html");
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.Write(item);
writer.RenderEndTag();
writer.RenderEndTag();
}
writer.RenderEndTag();
writer.RenderEndTag();
}
I know this is an old question, but Google will keep directing people here for years to come.
Related
I am working on images to rank them. Initially i made up a data set to store image meta data into it. I ran into a problem when i have to extract meta data of images. I am able to extract all of the meta data except the "tags" field which i need to rank images.
I am attaching the link to similar post but it is in matlab.
Extracting meta data "tags field"
I only need the information which is encircled in red.
Simply put by means of meme: "One does not simply extract tag metadata", apparently.
From what I have discovered, the Tag metadata that gets set when on the properties page of a JPG, from within Explorer, is relatively easy to get.
They are in the PropertyItem with ID 9C9E (or 40094 in Decimal).
This PropertyItem's Value is a byte[] with Unicode characters and is null terminated.
Below is a method to extract the tag ( ; seperated, so you can adjust the method to return a list of them split up if you want)
private string ReadBasicTags(string filename)
{
string foundTags = string.Empty;
using (Image inputImage = new Bitmap(filename))
{
try
{
PropertyItem basicTag = inputImage.GetPropertyItem(40094); // Hex 9C9E
if (basicTag != null)
{
foundTags = Encoding.Unicode.GetString(basicTag.Value).Replace("\0", string.Empty);
}
}
// ArgumentException is thrown when GetPropertyItem(int) is not found
catch (ArgumentException)
{
// finalOutput = "Tags not found";
}
}
return foundTags;
}
With this sample image you should get a string output of
TagOne;TagTwo:
However you may have noticed that I am talking about basic Tag information and Tags that set in Windows Explorer itself. They are easy to get.
If you where to run the above code on either this sample or this sample, you would not get any data back. This is because of the massive amount of different ways that metadata can be stored inside a jpeg by all the various tools and hardware.
If you want to have an idea of how many different types of metadata and all the different names of the tags and their formats, head on over to the ExifTool by Phil Harvey, explore a bit on that page, especially the "Tag Names" page and you are sure to get a headache of the sheer amount of different tags.
Now you are probably wondering if you should even delve deeper into the world of metadata tag extraction or if you could make a wrapper for that tool to integrate it into C# (which apparently some have done, but requires the tool to be present and other things, check under the Additional Resources section of the tool's page for info).
Ah, but fear not! For someone has figured out a simpler way, in C#, to extract the right information so that you can use the metadata tags alone.
I found this little GitHub project (JpegMetadata by Marcel Wijnands) while trying to wrap my head around the enormousy that is exif metadata. This little library makes it easy for you to extract the metadata tag and works on all the samples that I have linked so far.
You can install it into your project with NuGet if you are using VisualStudio.
The resulting List<string> that you get when calling the library gives you each tag separately.
As examples, the image Arena Chapel frescoes (example027.jpg) and Getty Villa (GettyVilla0001.JPG) produce the below lists with the shown code:
JpegMetadataAdapter metaAdapter = new JpegMetadataAdapter(#"C:\Dev\example027.JPG");
foreach (string item in metaAdapter.Metadata.Keywords)
{
outputString += string.Format("{0}{1}", item, Environment.NewLine);
}
Medieval
Italian
paintings (visual works)
frescoes (paintings)
fresco painting (technique)
allegorical
architectural interiors
cycles or series
New Testament
Old Testament and Apocrypha
saints
Jesus Christ
Mary, Blessed Virgin
Saint
Christian iconography
the Passion
Judas Iscariot
disciples
JpegMetadataAdapter metaAdapter = new JpegMetadataAdapter(#"C:\Dev\GettyVilla0001.JPG");
foreach (string item in metaAdapter.Metadata.Keywords)
{
outputString += string.Format("{0}{1}", item, Environment.NewLine);
}
sunset
shadows
mural painting
peristyles (colonnades)
trompe-l'oeil
greek
roman
Getty Villa
Both of these images store their metadata tags differently, so it is fair to say that with that library you should be covered by most of the versions of the metadata tags in jpegs.
given an aspx file, I want to parse its controls. I saw other posts on the same topics, but it doesn't really fit my needs.
Example:
<Ctl:Label id="MyLabel" runat="server" Font-Bold="True">Text of My label</Ctl:Label>
I want to retrieve these informations (the form does not matter, provided that the information are separated):
Tag = "Ctl"
Type = "Label"
id = "MyLabel"
runat = "server"
Font-Bold = "True"
Content = "Text of My label"
This post is close to what I want, but it does not allow me to retrieve "Text of My label" for example.
Note: I don't need to put this information in a xml file, but why not.
Any idea? Thanks.
EDIT: I need to parse a lot of aspx pages with a desktop program written in C#, not with asp.net.
EDIT 2: Why? Because I have a lot of asp.net pages, that contain server controls that I maintain. I need to retrieve information about in which pages I can find these controls, and most information about them (name, id, what inner controls they eventually have, etc., to say only that), but just focus on the main question, then I will handle the rest.
It sounds like you are trying to achieve automation; to me, that suggests looking for the most reliable solution, otherwise the automation isn't worth much because you will have to assume that any part of it might have failed.
There are a few ways I can think of to approach this:
1 - Regex
A regex will work fine as long as you aren't trying to turn the whole thing into a perfectly-structured result. While a typical ASCX is a little more structured than plain HTML (or else it won't run), it still has the potential to be malformed. As such, it suffers from the usual HTML parsing foibles.
2 - Parser
A simple state machine parser could help you identify specific controls and account for scenarios which a regex can't handle. This could be as simple or as elaborate as you wanted to make it. An advanced parser could build an entire hierarchy; a simple parser might just retrieve all the controls and perhaps inline code blocks (<%= %>) and data binding information.
3 - Use the compiled assemblies
As you probably know, an ASP.Net application is converted from markup into c# classes, and then to assemblies. These assemblies are available in the %System%\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files directory. While they aren't well-named (since they aren't intended for direct consumption) they do contain an extremely accurate representation of how .Net views the page/control. You could use reflection to access this data.
The contents of one of these files contains data such as this declaration of a ValidationSummary:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private global::System.Web.UI.WebControls.ValidationSummary #__BuildControlvs() {
global::System.Web.UI.WebControls.ValidationSummary #__ctrl;
#line 6 "C:\Development\VS 2010\..."
#__ctrl = new global::System.Web.UI.WebControls.ValidationSummary();
#line default
#line hidden
this.vs = #__ctrl;
#__ctrl.TemplateControl = this;
#__ctrl.ApplyStyleSheetSkin(this.Page);
#line 6 "C:\Development\VS 2010\..."
#__ctrl.ID = "vs";
#line default
#line hidden
#line 6 "C:\Development\VS 2010\..."
#__ctrl.ValidationGroup = "Group1";
return #__ctrl;
}
As you can see, these classes contain complete data for the page or control. They also contain line numbers and file names to the original code (might be useful to you).
Summary
Option #1 is the quickest for simple scenarios. Option #3 is very powerful but tightly coupled to the application's physical infrastructure and somewhat coupled to how the autogenerated code is structured. Option #2 will require the most work.
Lastly, there may be a way to programmatically access the markup-to-c# converter and compile the assemblies yourself (as is done when an application is first accessed - similar to option #3).
You may be able to parse the files using the HTML Agility Pack, and then locate the controls by element name.
I am going to create a function in my application that is going to send som status mails to several receivers in a list.
Earlier i used plane text format on the email, but now i want to send the mail based on som html templates. I need tips reguarding a good way to insert data into these templates before sending them.
eks
%CpuStatus%
%HardriveStatus%
and so on. I have the solution for everything except a way to fill anchors like that with data. This is a WinForm application so i dont have access to the ASP functionality
Maybe this sort of thing would be the simplest?
// This would most likely be loaded from a file or database.
string emailBody = "CPU Status: %CpuStatus%\nHard Drive Status: %HardriveStatus%";
string cpuStatus = MyService.GetCpuStatus();
emailBody.Replace("%CpuStatus%", cpuStatus);
If you really wanted to make a big project out of it, you can use a webbrowser control, load it with your html file and then use the WebBrowser's Document property to get an HtmlDocument object. You can then loop through it's children (recursively) to find the tags you want to change.
Personally, I would do the .Replace method suggested previously.
My application sends notification mails to users so I created a html template for each type of notification. I set something like fields in the template as {n} in order to use something like this when I'm sending the message:
string bodyTemplate = GetBodyTemplate(); //gets the html template with {n} in it
message.Body = String.Format(bodyTemplate, fieldZeroValue, fieldOneValue);
For example, the template can have a piece of this:
<td style="vertical-align:middle;padding:0.5em 0.5em 0.5em 0.5em;">
Go to page
</td>
In this example I would use:
message.Body = String.Format(bodyTemplate, IdValue.ToString());
Here is my question: Where should I store those very long string with the html templates?
I don't want to connect to the the database to get them and storing them in string constants looks awful.
Please tell me what is the recomended practice to store those strings.
I create text files and store the templates in the project. If there aren't too many, you could build them into your assembly and get them out of the Resources. If there are a lot, I would point to a directory that contains the files and use a string reader to pick out a filename.
I heard a pearl of wisdom the other day over an argument of using #regions or not. The "pro #regions" guy said, "But I want to hide ugly code" and the "anti #regions" guy said "if it's so ugly, you shouldn't have written it."
It made me laugh to see that you were getting a code smell from ugly constants.
What is the aversion to putting it in the db? is it the cost of having to get it frequently? If that's so, then perhaps cache it and re-pull from the db at a reasonable interval. That way you can maintain the flexibility of being able to add or change templates without having to hit the db every time.
If constants would genuenly work and you're bawlking that they're ugly, then hide them with a #region :)
I have developed a large business portal. I just realized I need my website in another language. I have researched the solutions available like
Used third party control on my website. (Does fit in my design. Not useful regarding SEO point of view. Dont want to show third party brand names.)
Create Resource files for each language.( A lot of work required to restructure pages to use text from resource files. What about the data entered by the user like Business Description. )
Are there any Other options available.
I was thinking of a solution like a when a page is created on server side then I could translate it before sending back to client. Is there any way I can do that?(to translate everything including data added from databases or through a code. And without effecting design. )
If you really need to translate your application, it's going to take a lot of hard, tedious work. There is no magic bullet.
The first thing you need to do is convert your plain text in your markup to asp:Localize controls. By using the Localize control, you can leave your existing <span> tags in place and just replace the text inside of them. There's really no way around this. Visual Studio's search and replace supports regular expression matching that may help you with this, or you can use Resharper (see below).
The first approach would be to download the open source shopping application nopCommerce and see how they handle their localization. They store their strings in a database and have a UI for editing languages. A similar approach may work well for you.
Alternatively, if you want to use Resource Files, there are two tools that I would recommend using in addition to Visual Studio: Resharper 5 (Localization Features screencast) and Zeta Resource Editor. These are the steps I would take to accomplish it using this method:
Use the "Generate Local Resource" tool in visual studio for each page
Use Resharper's "Move HTML to resource" on the text in your markup to make them into Localize controls.
Use Resharper to search out any localizable strings in your code behind and move them to the resource file as well.
Use the Globalization Rules of Code Analysis / FXCop to help find any additional problems you might face formatting numbers, dates, etc.
Once all text is in the resx files, use Zeta Resource Editor to load up all of your resx files, add new languages, and export for translation (or auto translate if you're brave enough).
I've used this approach on a site translated into 8 languages (and growing) with dozens of pages (and growing). However, this is not a user-editable site; the pages are solely controlled by the programmers.
a large switch case? use a dictionary/hashtable (seperate instance for each a language), it is much, much more effective and fast.
To Convert The Page To Arabic Language Or Other Language .
Go to :
1-page design
2-Tools
3-Generate Local Resource
4-obtain "App_LocalResources" include "filename.aspx.resx"
5-copy the file and change the name to "filename.aspx.ar.resx" to convert the page to arabic language or other .
hope to helpful :)
I found a good solution, see in http://www.nopcommerce.com/p/1784/nopcommerce-translator.aspx
this project is open source and source repository is here: https://github.com/Marjani/NopCommerce-Translator
good luck
Without installing any 3rd party tool, APIs, or dll objects, I am able to utilize the App_LocalResources. Although I still use Google Translate for the words and sentences to be translated and copy and paste it to the file as you can see in one of the screenshots below (or you can have a person translator and type manually to add). In your Project folder (using MS Visual Studio as editor), add an App_LocalResources folder and create the English and other language (resx file). In my case, it's Spanish (es-ES) translation. See screenshot below.
Next, on your aspx, add the meta tags (meta:resourcekey) that will match in the App_LocalResources. One for English and another to the Spanish file. See screenshots below:
Spanish: (filename.aspx.es-ES.resx)
English: (filename.aspx.resx)
.
Then create a link on your masterpage file with a querystring that will switch the page translation and will be available on all pages:
<%--ENGLISH/SPANISH VERSION BUTTON--%>
<asp:HyperLink ID="eng_ver" runat="server" Text="English" Font-Underline="false"></asp:HyperLink> |
<asp:HyperLink ID="spa_ver" runat="server" Text="EspaƱol" Font-Underline="false"></asp:HyperLink>
<%--ENGLISH/SPANISH VERSION BUTTON--%>
.
On your masterpage code behind, create a dynamic link to the Hyperlink tags:
////LOCALIZATION
string thispage = Request.Url.AbsolutePath;
eng_ver.NavigateUrl = thispage;
spa_ver.NavigateUrl = thispage + "?ver=es-ES";
////LOCALIZATION
.
Now, on your page files' code behind, you can set a session variable to make all links or redirections to stick to the desired translation by always adding a querystring to urls.
On PageLoad:
///'LOCALIZATION
//dynamic querystring; add this to urls ---> ?" + Session["add2url"]
{
if (Session["version"] != null)
{
Session["add2url"] = "?ver=" + Session["version"]; //SPANISH version
}
else
{
Session["add2url"] = ""; // ENGLISH as default
}
}
///'LOCALIZATION
.
On Click Events sample:
protected void btnBack_Click(object sender, EventArgs e)
{
Session["FileName.aspx"] = null;
Response.Redirect("FileName.aspx" + Session["add2url"]);
}
I hope my descriptions were easy enough.
If you don't want to code more and if its feasible with google translator then You can try with Google Translator API. you can check below code.
<script src="http://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
<script>
function googleTranslateElementInit() {
$.when(
new google.translate.TranslateElement({pageLanguage: 'en', includedLanguages: 'en',
layout: google.translate.TranslateElement.FloatPosition.TOP_LEFT}, 'google_translate_element')
).done(function(){
var select = document.getElementsByClassName('goog-te-combo')[0];
select.selectedIndex = 1;
select.addEventListener('click', function () {
select.dispatchEvent(new Event('change'));
});
select.click();
});
}
$(window).on('load', function() {
var select = document.getElementsByClassName('goog-te-combo')[0];
select.click();
var selected = document.getElementsByClassName('goog-te-gadget')[0];
selected.hidden = true;
});
</script>
Also, Find below code for <body> tag
<div id="google_translate_element"></div>
It will certainly be more work to create resource files for each language - but this is the option I would opt for, as it gives you the opportunity to be more accurate. If you do it this way you can have the text translated, manually, by someone that speaks the language (there are many companies out there that offer this kind of service).
Automatic translation systems are often good for giving a general impression of what something in another language means, but I would never use them when trying to portray a professional image, as often what they output just doesn't make sense. Nothing screams 'unprofessional!' like text that just doesn't make sense because it's been automatically translated.
I would take the resource file route over the translation option because the meaning of words in a language can be very contextual and even one mistake could undermine your site's credibility.
As you suggest Visual Studio can generate the meta resource file keys for most controls containing text but may leave you having to do the rest manually but I don't see an easier, more reliable solution.
I don't think localisation is an easy-to-automate thing anyway as text held in the database often results in schema changes to allow for multiple languages, and web HTML often need restructuring to deal with truncated or wrapped label and button text because, for example, you've translated into German or something.
Other considerations:
Culture settings - financial delimitors, date formats.
Right-to-left - some languages like arabic are written right to left meaning that the pages require rethinking as to control positioning like images etc.
Good luck whatever you go with.
I ended up doing it the hard way:
I wrote an extension method on the string class called TranslateInto
On the Page's PreRender method I grab all controls recursively based on their type (the types that would have text)
Foreach through them and text.TranslateInto(SupportedLanguages.CurrentLanguage)
In my TranslateInto method I have a ridiculously large switch statement with every string displayed to the user and its associated translation.
Its not very pretty, but it worked.
We work with a Translation CAT tool (Computer Assisted Translation) called MemoQ that allows us to translate the text while leaving all the tags and coding in place. This is very helpful when the order of words change when you translate from one language to another.
It is also very useful because it allows us to work with translators from around the world, without the need for them to have any technical expertise. It also allows us to have the translation proof read by a second translator.
We use this translation environment to translate html, xml, InDesign, Word, etc.
I think you should try Google Translate.
http://translate.google.com/translate_tools
Very easy and very very effective.
HTH