I am trying to email a checklist but I can't get the rows to line up.
string aheading = "Defects:";
string bheading = "Comments:";
string result = aheading.PadRight(20, ' ') + bheading.PadRight(20, ' ') + Environment.NewLine;
foreach (var item in chkList.CheckItems)
{
if (item.Defect == true)
{
result += item.ItemTitle.Trim().PadRight(20,' ') + item.Comment.Trim().PadRight(20,' ') + Environment.NewLine ;
}
}
In the database there is a list of defects and comments, so the code loops around to display each defect and it's comment on separate lines but it looks like this:
Defects: Comments:
Vehicle Secure comment1
Brakes comment2
Is there a way to get the comments to line up? It's like the vehicle secure string is pushing the comment out. There could be a long list of defects and I won't know how long each string will be so can I set the comments to display in a certain position?
Format your string as HTML, then you could use tables to format. the mailmessage has a property to set its content as Html.
var htmlstring = "<table>";
htmlstring += "<tr><th>Header</th><th>Header</th></tr>";
foreach (var row in content)
{
htmlstring += string.Format("<tr><td>text</td><td>{0}</td></tr>", row.data);
}
htmlstring += "</table>";
var message = new MailMessage();
message.IsBodyHtml = true;
message.Body = htmlString;
implementing the code from the question
string aheading = "Defects:";
string bheading = "Comments:";
string result = string.Format("<table><tr><th>{0}</th><th>{1}</th></tr>", aheading, bheading);
foreach (var item in chkList.CheckItems)
{
if (item.Defect == true)
{
result += string.Format("<tr><td>{0}</td><td>{1}</td></tr>", item.ItemTitle.Trim(), item.Comment.Trim());
}
}
result += "</table>";
var message = new MailMessage();
message.IsBodyHtml = true;
message.Body = result;
// SEND MESSAGE
var client = new SmtpClient("mailhost");
// If auth is needed
client.Credentials = new NetworkCredential("username", "password");
client.Send(message);
You should count the length of string.
First, go through all your CheckItems and find the longest ItemTitle
Second, make a variable padding to put the max padding.
Finally, you can count each line's padding by using String.Format
string aheading = "Defects:";
string bheading = "Comments:";
int maxlength = 0;
foreach (var item in chkList.CheckItems)
{
if (item.ItemTitle.Length > maxlength)
maxlength = item.ItemTitle.Length;
}
int padding = maxlength + 10; //10 spaces between the longest 'Defects' and its 'Comments'
string format = "{0,-" + padding + "} {1,-" + padding + "}\r\n"
string result = String.Format(format, "Defects:", "Comments:");
foreach (var item in chkList.CheckItems)
{
if (item.Defect == true)
{
result += String.Format(format, item.ItemTitle, Item.Comment);
}
}
Note
Each of the characters in your current font should have the same width.
You should use Monospaced Font in your mail to make this work properly.
If you wish it to work on every font. See #Gelootn's answer instead.
Use String.Format, rather than .padright.
Create a string such as:
string title = String.Format("{0,-10} {1,-10}\n", "Defects:", "Comments:");
Then in your loop use:
string result = String.Format("{0,-10} {1,-10}\n", item.ItemTitle, item.Comment);
You will have to adjust the values to get it to look how you are happy with.
Related
I am using Mailkit to send an email.
So in the body of the email, I am passing data like this:
message.Body = new TextPart("plain")
{
foreach (var item in model.Transaction)
{
Console.WriteLine(item.Account +"-"+item.Amount+"-"+item.Date);
}
Text = #"";
};
but I wanted to put item.Account, item.Amount, item.Date in #""
How can I do that?
you should use $
$"{item.Account}, {item.Amount}, {item.Date}";
Because # is used to escaping specials symbols
You won't be able to access item.* outside of foreach*.
To create a single string from multiple strings you could use string.Join:
List<string> l = new ();
foreach (var item in model.Transaction)
{
var fromSingleItem = $"{item.Account}-{item.Amount}-{item.Date}";
l.Append(fromSingleItem);
}
var fromAllItems = string.Join(", ", l);
* = What would time outside of foreach mean? Would it be the first item's data, or the last one's, or from the middle?
I would use a string builder for this
var sb= new StringBuilder();
foreach (var item in model.Transaction)
{
sb.Append( item.Account + "-" + item.Amount.ToString() + "-"
+ item.Date.ToString() + "\n");
}
message.Body = new TextPart("plain")
{
Text = sb.ToString();
};
or if you have only several items
var sb= string.Empty;
foreach (var item in model.Transaction)
{
sb+= item.Account + "-" + item.Amount.ToString() + "-" + item.Date.ToString() + "\n");
}
Serge has the right idea, but I would tweak it a bit as there is a performance penalty every time you concatenate using the '+' symbol (a new String object is instantiated when you concatenate with '+'). I would suggest you continue to use StringBuilder even if you only have a single item in your foreach loop.
var builder = new StringBuilder();
foreach (var item in model.Transaction)
{
builder.Append(item.Account);
builder.Append("-");
builder.Append(item.Amount);
builder.Append("-");
builder.AppendLine(item.Date);
}
message.Body = new TextPart("plain")
{
Text = builder.ToString()
};
will appreciate any Help on this one.
I am trying to replace text in an email template using Mailkit. The issue is that in Mailkit there is at least a text part and and Html part.
I can get then set the text part using code such as:
var textPart = message.BodyParts.OfType<TextPart>().FirstOrDefault();
I can get the htmlbody using
var htmlPart = message.htmlBody
but once I modify it I do not know how to set the htmlpart to the message's htmlBody.
My code so far:
FileStream sr = new FileStream(fileLocation + "\\" + fileName, FileMode.Open, FileAccess.Read);
message = MimeMessage.Load(sr);
sr.Close();
//Get the text Part
var textPart = message.BodyParts.OfType<TextPart>().FirstOrDefault();
//Get the HtmlBody
var htmlPart = message.HtmlBody;
string regexPattern;
string regexReplacement;
Regex regexText;
foreach (var replaceSet in replaceArray)
{
regexPattern = "#" + replaceSet["Key"].ToString() + "#";
regexReplacement = (string)replaceSet["Value"].ToString();
//bool test = Regex.IsMatch(docText, regexPattern);
if (regexReplacement != "")
{
regexText = new Regex(regexPattern);
textPart.Text = regexText.Replace(textPart.Text, regexReplacement);
//Set, modify the text part
htmlPart = regexText.Replace(htmlPart, regexReplacement);
}
}
try
{
message.WriteTo(fileLocation + "\\output\\" + fileName);
}
catch (Exception Ex)
{
result = Ex.Message;
return BadRequest(result);
}```
Something like this will probably work:
// Get the text Part
var textPart = message.BodyParts.OfType<TextPart>().FirstOrDefault(x => x.IsPlain);
var htmlPart = message.BodyParts.OfType<TextPart>().FirstOrDefault(x => x.IsHtml);
string regexPattern;
string regexReplacement;
Regex regexText;
foreach (var replaceSet in replaceArray)
{
regexPattern = "#" + replaceSet["Key"].ToString() + "#";
regexReplacement = (string)replaceSet["Value"].ToString();
//bool test = Regex.IsMatch(docText, regexPattern);
if (regexReplacement != "")
{
regexText = new Regex(regexPattern);
textPart.Text = regexText.Replace(textPart.Text, regexReplacement);
//Set, modify the text part
htmlPart.Text = regexText.Replace(htmlPart.Text, regexReplacement);
}
}
So I have a long string containing pointy brackets that I wish to extract text parts from.
string exampleString = "<1>text1</1><27>text27</27><3>text3</3>";
I want to be able to get this
1 = "text1"
27 = "text27"
3 = "text3"
How would I obtain this easily? I haven't been able to come up with a non-hacky way to do it.
Thanks.
Using basic XmlReader and some other tricks to do wrapper to create XML-like data, I would do something like this
string xmlString = "<1>text1</1><27>text27</27><3>text3</3>";
xmlString = "<Root>" + xmlString.Replace("<", "<o").Replace("<o/", "</o") + "</Root>";
string key = "";
List<KeyValuePair<string,string>> kvpList = new List<KeyValuePair<string,string>>(); //assuming the result is in the KVP format
using (XmlReader xmlReader = XmlReader.Create(new StringReader(xmlString))){
bool firstElement = true;
while (xmlReader.Read()) {
if (firstElement) { //throwing away root
firstElement = false;
continue;
}
if (xmlReader.NodeType == XmlNodeType.Element) {
key = xmlReader.Name.Substring(1); //cut of "o"
} else if (xmlReader.NodeType == XmlNodeType.Text) {
kvpList.Add(new KeyValuePair<string,string>(key, xmlReader.Value));
}
}
}
Edit:
The main trick is this line:
xmlString = "<Root>" + xmlString.Replace("<", "<o").Replace("<o/", "</o") + "</Root>"; //wrap to make this having single root, o is put to force the tagName started with known letter (comment edit suggested by Mr. chwarr)
Where you first replace all opening pointy brackets with itself + char, i.e.
<1>text1</1> -> <o1>text1<o/1> //first replacement, fix the number issue
and then reverse the sequence of all the opening point brackets + char + forward slash to opening point brackets + forward slash + char
<o1>text1<o/1> -> <o1>text1</o1> //second replacement, fix the ending tag issue
Using simple WinForm with RichTextBox to print out the result,
for (int i = 0; i < kvpList.Count; ++i) {
richTextBox1.AppendText(kvpList[i].Key + " = " + kvpList[i].Value + "\n");
}
Here is the result I get:
This is far from bulletproof, but you could use a combination of split and Regex matching:
string exampleString = "<1>text1</1><27>text27</27><3>text3</3>";
string[] results = exampleString.Split(new string[] { "><" }, StringSplitOptions.None);
Regex r = new Regex(#"^<?(\d+)>([^<]+)<");
foreach (string result in results)
{
Match m = r.Match(result);
if (m.Success)
{
string index = m.Groups[1].Value;
string value = m.Groups[2].Value;
}
}
The most non-bulletproof example I can think of is if your text contains a "<", that would pretty much break this.
Currently I am building an agenda with extra options.
for testing purposes I store the data in a simple .txt file
(after that it will be connected to the agenda of a virtual assistant.)
To change or delete text from this .txt file I have a problem.
Although the part of the content that needs to be replaced and the search string are exactly the same it doesn't replace the text in content.
code:
Change method
public override void Change(List<object> oldData, List<object> newData)
{
int index = -1;
for (int i = 0; i < agenda.Count; i++)
{
if(agenda[i].GetType() == "Task")
{
Task t = (Task)agenda[i];
if(t.remarks == oldData[0].ToString() && t.datetime == (DateTime)oldData[1] && t.reminders == oldData[2])
{
index = i;
break;
}
}
}
string search = "Task\r\nTo do: " + oldData[0].ToString() + "\r\nDateTime: " + (DateTime)oldData[1] + "\r\n";
reminders = (Dictionary<DateTime, bool>) oldData[2];
if(reminders.Count != 0)
{
search += "Reminders\r\n";
foreach (KeyValuePair<DateTime, bool> rem in reminders)
{
if (rem.Value)
search += "speak " + rem.Key + "\r\n";
else
search += rem.Key + "\r\n";
}
}
// get new data
string newRemarks = (string)newData[0];
DateTime newDateTime = (DateTime)newData[1];
Dictionary<DateTime, bool> newReminders = (Dictionary<DateTime, bool>)newData[2];
string replace = "Task\r\nTo do: " + newRemarks + "\r\nDateTime: " + newDateTime + "\r\n";
if(newReminders.Count != 0)
{
replace += "Reminders\r\n";
foreach (KeyValuePair<DateTime, bool> rem in newReminders)
{
if (rem.Value)
replace += "speak " + rem.Key + "\r\n";
else
replace += rem.Key + "\r\n";
}
}
Replace(search, replace);
if (index != -1)
{
remarks = newRemarks;
datetime = newDateTime;
reminders = newReminders;
agenda[index] = this;
}
}
replace method
private void Replace(string search, string replace)
{
StreamReader reader = new StreamReader(path);
string content = reader.ReadToEnd();
reader.Close();
content = Regex.Replace(content, search, replace);
content.Trim();
StreamWriter writer = new StreamWriter(path);
writer.Write(content);
writer.Close();
}
When running in debug I get the correct info:
content "-- agenda --\r\n\r\nTask\r\nTo do: test\r\nDateTime: 16-4-2012 15:00:00\r\nReminders:\r\nspeak 16-4-2012 13:00:00\r\n16-4-2012 13:30:00\r\n\r\nTask\r\nTo do: testing\r\nDateTime: 16-4-2012 9:00:00\r\nReminders:\r\nspeak 16-4-2012 8:00:00\r\n\r\nTask\r\nTo do: aaargh\r\nDateTime: 18-4-2012 12:00:00\r\nReminders:\r\n18-4-2012 11:00:00\r\n" string
search "Task\r\nTo do: aaargh\r\nDateTime: 18-4-2012 12:00:00\r\nReminders\r\n18-4-2012 11:00:00\r\n" string
replace "Task\r\nTo do: aaargh\r\nDateTime: 18-4-2012 13:00:00\r\nReminders\r\n18-4-2012 11:00:00\r\n" string
But it doesn't change the text. How do I make sure that the Regex.Replace finds the right piece of content?
PS. I did check several topics on this, but none of the solutions mentioned there work for me.
You missed a : right after Reminders. Just check it again :)
You could try using a StringBuilder to build up you want to write out to the file.
Just knocked up a quick example in a console app but this appears to work for me and I think it might be what you are looking for.
StringBuilder sb = new StringBuilder();
sb.Append("Tasks\r\n");
sb.Append("\r\n");
sb.Append("\tTask 1 details");
Console.WriteLine(sb.ToString());
StreamWriter writer = new StreamWriter("Tasks.txt");
writer.Write(sb.ToString());
writer.Close();
I am working on a ASP.NET 4.0 web application, the main goal for it to do is go to the URL in the MyURL variable then read it from top to bottom, search for all lines that start with "description" and only keep those while removing all HTML tags. What I want to do next is remove the "description" text from the results afterwords so I have just my device names left. How would I do this?
protected void parseButton_Click(object sender, EventArgs e)
{
MyURL = deviceCombo.Text;
WebRequest objRequest = HttpWebRequest.Create(MyURL);
objRequest.Credentials = CredentialCache.DefaultCredentials;
using (StreamReader objReader = new StreamReader(objRequest.GetResponse().GetResponseStream()))
{
originalText.Text = objReader.ReadToEnd();
}
//Read all lines of file
String[] crString = { "<BR> " };
String[] aLines = originalText.Text.Split(crString, StringSplitOptions.RemoveEmptyEntries);
String noHtml = String.Empty;
for (int x = 0; x < aLines.Length; x++)
{
if (aLines[x].Contains(filterCombo.SelectedValue))
{
noHtml += (RemoveHTML(aLines[x]) + "\r\n");
}
}
//Print results to textbox
resultsBox.Text = String.Join(Environment.NewLine, noHtml);
}
public static string RemoveHTML(string text)
{
text = text.Replace(" ", " ").Replace("<br>", "\n");
var oRegEx = new System.Text.RegularExpressions.Regex("<[^>]+>");
return oRegEx.Replace(text, string.Empty);
}
Ok so I figured out how to remove the words through one of my existing functions:
public static string RemoveHTML(string text)
{
text = text.Replace(" ", " ").Replace("<br>", "\n").Replace("description", "").Replace("INFRA:CORE:", "")
.Replace("RESERVED", "")
.Replace(":", "")
.Replace(";", "")
.Replace("-0/3/0", "");
var oRegEx = new System.Text.RegularExpressions.Regex("<[^>]+>");
return oRegEx.Replace(text, string.Empty);
}
public static void Main(String[] args)
{
string str = "He is driving a red car.";
Console.WriteLine(str.Replace("red", "").Replace(" ", " "));
}
Output:
He is driving a car.
Note: In the second Replace its a double space.
Link : https://i.stack.imgur.com/rbluf.png
Try this.It will remove all occurrence of the word which you want to remove.
Try something like this, using LINQ:
List<string> lines = new List<string>{
"Hello world",
"Description: foo",
"Garbage:baz",
"description purple"};
//now add all your lines from your html doc.
if (aLines[x].Contains(filterCombo.SelectedValue))
{
lines.Add(RemoveHTML(aLines[x]) + "\r\n");
}
var myDescriptions = lines.Where(x=>x.ToLower().BeginsWith("description"))
.Select(x=> x.ToLower().Replace("description",string.Empty)
.Trim());
// you now have "foo" and "purple", and anything else.
You may have to adjust for colons, etc.
void Main()
{
string test = "<html>wowzers description: none <div>description:a1fj391</div></html>";
IEnumerable<string> results = getDescriptions(test);
foreach (string result in results)
{
Console.WriteLine(result);
}
//result: none
// a1fj391
}
static Regex MyRegex = new Regex(
"description:\\s*(?<value>[\\d\\w]+)",
RegexOptions.Compiled);
IEnumerable<string> getDescriptions(string html)
{
foreach(Match match in MyRegex.Matches(html))
{
yield return match.Groups["value"].Value;
}
}
Adapted From Code Project
string value = "ABC - UPDATED";
int index = value.IndexOf(" - UPDATED");
if (index != -1)
{
value = value.Remove(index);
}
It will print ABC without - UPDATED