Is it possible to send an email message in a Windows Universal App for Windows 8.1 and Windows Phone 8.1?
await Launcher.LaunchUriAsync(new Uri("mailto:abc#abc.com?subject=MySubject&body=MyContent"));
With this code line I can send an email, but I want to send a message with attachment.
Since Microsoft missed to add EmailMessage and EmailManager to the Windows Store Apps libraries it seems as if there are only two unsatisfactory solutions: You could use sharing or initiate email sending via the mailto protocol. Here is how I did it:
/// <summary>
/// Initiates sending an e-mail over the default e-mail application by
/// opening a mailto URL with the given data.
/// </summary>
public static async void SendEmailOverMailTo(string recipient, string cc,
string bcc, string subject, string body)
{
if (String.IsNullOrEmpty(recipient))
{
throw new ArgumentException("recipient must not be null or emtpy");
}
if (String.IsNullOrEmpty(subject))
{
throw new ArgumentException("subject must not be null or emtpy");
}
if (String.IsNullOrEmpty(body))
{
throw new ArgumentException("body must not be null or emtpy");
}
// Encode subject and body of the email so that it at least largely
// corresponds to the mailto protocol (that expects a percent encoding
// for certain special characters)
string encodedSubject = WebUtility.UrlEncode(subject).Replace("+", " ");
string encodedBody = WebUtility.UrlEncode(body).Replace("+", " ");
// Create a mailto URI
Uri mailtoUri = new Uri("mailto:" + recipient + "?subject=" +
encodedSubject +
(String.IsNullOrEmpty(cc) == false ? "&cc=" + cc : null) +
(String.IsNullOrEmpty(bcc) == false ? "&bcc=" + bcc : null) +
"&body=" + encodedBody);
// Execute the default application for the mailto protocol
await Launcher.LaunchUriAsync(mailtoUri);
}
Windows Phone 8.1
You could use the following to send email with attachment:
var email = new EmailMessage();
email.To = ...;
email.Body = ...;
email.Attachments.Add( ... );
var ignore = EmailManager.ShowComposeNewEmailAsync(email);
Windows 8.1
On Windows 8.1, unfortunately, there is no way to send email with attachment. The mailto protocol is all you have and it doesn't not officially supports attachment. However, you can add attachment as the following:
mailto:xxx#xxx.com?subject=xxx&body=xxx&attach=C:\path\to\file
or
mailto:xxx#xxx.com?subject=xxx&body=xxx&Attachment=C:\path\to\file
But it is up to the client to decide whether it will handle the attachment or not. See this thread for more detail https://msdn.microsoft.com/en-us/library/aa767737(v=vs.85).aspx
To send emails with attachment You would be needed to use the EmailMessage and EmailManager class.
1. EmailMessage:
The EmailMessage class defines the actual email that will be sent. You can specify the recipients (To , CC , BC) , Subject and the Body of the email .
2. EmailManager:
The EmailManager class is defined in the Windows.ApplicationModel.Email namespace . The EmailManager class provides a static method ShowComposeNewEmailAsync which accepts the EmailMessage as argument . The ShowComposeNewEmailAsync will launch the Compose email Screen with the EmailMessage which allows the users to send an email message.
You can find more reference here windows-phone-8-1-and-windows-runtime-apps-how-to-2-send-emails-with-attachment-in-wp-8-1
On this page: https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh871373.aspx
Microsoft supplies a example how to share with your application. Download the source example app: http://go.microsoft.com/fwlink/p/?linkid=231511
Open the solution and add a file: test.txt to the project root.
Then open ShareFiles.xaml.cs and replace the class with:
public sealed partial class ShareText : SDKTemplate.Common.SharePage
{
public ShareText()
{
this.InitializeComponent();
LoadList();
}
List<Windows.Storage.IStorageItem> list { get; set; }
public async void LoadList()
{
var uri = new Uri("ms-appx:///test.txt");
var item = await StorageFile.GetFileFromApplicationUriAsync(uri);
list = new List<IStorageItem>();
list.Add(item);
}
protected override bool GetShareContent(DataRequest request)
{
bool succeeded = false;
string dataPackageText = TextToShare.Text;
if (!String.IsNullOrEmpty(dataPackageText))
{
DataPackage requestData = request.Data;
requestData.Properties.Title = TitleInputBox.Text;
requestData.Properties.Description = DescriptionInputBox.Text; // The description is optional.
requestData.Properties.ContentSourceApplicationLink = ApplicationLink;
requestData.SetText(dataPackageText);
requestData.SetStorageItems(list);
succeeded = true;
}
else
{
request.FailWithDisplayText("Enter the text you would like to share and try again.");
}
return succeeded;
}
}
Might not be the best code, but it helped me :)
Related
I'm using MailKit to send emails for account confirmation and password reset. These operations work as intended, but now I'm trying to modify the same code to also send email notifications to all registered users but this is where I'm having issues.
I've built a List collection using the following code:
public List<string> UserList {
get {
var allUsers = userManager.Users;
return allUsers.Select(x => x.Email).ToList();
}
}
this collection returns all emails within my AspNetUsers table successfully.
Next I create 3 variables:
var toAddress = UserList;
var subject = "New Asset Added" + " " + model.Name;
var body = model.Name + model.AssetType + model.AssetURL;
When debugging and stepping through the code I do see Count = 2 and then the list of emails in the variable. So far this looks good
After the 3 variables I have this line of code that passes the collected information to the Send method:
_emailSender.Send(toAddress[0], subject, body);
Again, the toAddress[0] when stepping through code shows Count = 2 so I'm assuming the way I have it written is valid.
Once I reach the Send Method code and I check the toAddress variable again, it is now stripped of the Count = 2 and becomes just the first email address in the list which makes sense since I passed the variable with an index of 0. What I really need is to pass all emails not just 1.
If I remove the brackets or leave the brackets empty I get an error:
cannot convert from 'System.Collections.Generic.List<string>' to 'string'
So for now I'm forced to add an index.
My send method seems fairly simple but I do believe my foreach loop might also not be well formulated. Here's my Send Method in it's entirety.
public async void Send(string toAddress, string subject, string body, bool sendAsync = true) {
var mimeMessage = new MimeMessage(); // MIME : Multipurpose Internet Mail Extension
mimeMessage.From.Add(new MailboxAddress(_fromAddressTitle, _fromAddress));
foreach (char toAddresses in toAddress) {
mimeMessage.To.Add(new MailboxAddress(toAddresses)); //I get error saying cannot convert char to string.
}
mimeMessage.Subject = subject;
var bodyBuilder = new MimeKit.BodyBuilder {
HtmlBody = body
};
mimeMessage.Body = bodyBuilder.ToMessageBody();
using (var client = new MailKit.Net.Smtp.SmtpClient()) {
client.Connect(_smtpServer, _smtpPort, _enableSsl);
client.Authenticate(_username, _password); // If using GMail this requires turning on LessSecureApps : https://myaccount.google.com/lesssecureapps
if (sendAsync) {
await client.SendAsync(mimeMessage);
}
else {
client.Send(mimeMessage);
}
client.Disconnect(true);
}
}
My end goal is to use a foreach loop on the mimeMessage.To.Add(new MailboxAddress(toAddresses)); so that everyone can receive an email if there is more than 1 email address, if not it can loop once.
Thanks in advance!
In your method, Send(), you are taking in a string for toAddress and then splitting its characters... that wont work. If you want to to use one or more email addresses, you will need to send in email address(es) separated by a delimiter or as a complete list.
Two ways to go about it.
Change the argument from string to List
change your method to take in a List
public async void Send(List<string> toAddress, string subject, string body, bool sendAsync = true)
{
var mimeMessage = new MimeMessage(); // MIME : Multipurpose Internet Mail Extension
mimeMessage.From.Add(new MailboxAddress(_fromAddressTitle, _fromAddress));
toAddress.ForEach(address => mimeMessage.To.Add(new MailboxAddress(address)));
...
if you use this method, you will need to send in the list as well.. not a single email address string.
List<string> toAddress = new List<string>() { "firstEmail", "Second" ...};
_emailSender.Send(toAddress, subject, body);
// You will not be able to do send in single string
_emailSender.Send("firstemail#only.com", subject, body); //Exception no method found.
Send in a delimited list
Always send in comma separated email addresses and translate those to multiple addresses when adding them to To field of your mimeMessage.
public async void Send(string toAddress, string subject, string body, bool sendAsync = true)
{
var mimeMessage = new MimeMessage(); // MIME : Multipurpose Internet Mail Extension
mimeMessage.From.Add(new MailboxAddress(_fromAddressTitle, _fromAddress));
// HERE -> FOREACH string, not char.
foreach (string toAddresses in toAddress.Split(',')) // Split on ,
{
mimeMessage.To.Add(new MailboxAddress(toAddresses));
}
and you will need to use this method the following way...
List<string> toAddress = new List<string>() {"first", "..."};
_emailSender.Send(string.Join(',',toAddress), subject, body);
I have a scenario, to convert the valid email address to test account by adding "test-" in the email address domain. So that on the testing environment it won't reach to the actual recipients. Also I adding my email address in the Bcc to verify that email content.
What I have tried so far:
{
MailMessage msg = new MailMessage();
// already valided the "emailAddress" is the valid one or not
msg.To.Add(new MailAddress(ToTestAccount(emailAddress)));
// other code ...
}
Calling the method ToTestAccount() to add the "test-" in the email address.
private string ToTestAccount(string emailAddress)
{
var userAlias = emailAddress.Split('#')[0];
var host = emailAddress.Split('#')[1].Split('.')[0];
var hostDomain = emailAddress.Split('#')[1];
var indexOfDot = hostDomain.IndexOf('.');
var domain = hostDomain.Substring(indexOfDot, hostDomain.Length - indexOfDot);
return userAlias + "#test-" + host + domain;
}
The functionality produce my expected result. Sample data and expected output:
Email Address | Expected
----------------------------------------------------------
arulkumar#gmail.com | arulkumar#test-gmail.com
arul.kumar#gmail.com | arul.kumar#test-gmail.com
arulkumar4#gmail.co.in | arulkumar4#test-gmail.co.in
arul.kumar4#gmail.co.in | arul.kumar4#test-gmail.co.in
But is there any other way to achieve the expected result, I mean using less string funtions or the best practice to achieve it?
C# Fiddle for the same: https://rextester.com/LPZI50439
For some reason, I'm not able to use SpecifiedPickupDirectory option, so I'm triggering the actual email.
You can use the MailAddress properties which would result in less string manipulation and handle some of the weird varieties of valid email addresses.
private string ToTestAccount(string emailAddress)
{
var originalEmailAddress = new MailAddress(emailAddress);
return $"{originalEmailAddress.User}#test-{originalEmailAddress.Host}";
}
Or if you need to support display names you could have an extension method:
public static MailAddress Testing(this MailAddress address)
{
return string.IsNullOrEmpty(address.DisplayName)
? new MailAddress($"{address.User}#test-{address.Host}")
: new MailAddress($"{address.User}#test-{address.Host}", address.DisplayName);
}
Which would allow usage like this:
{
MailMessage msg = new MailMessage();
// already valided the "emailAddress" is the valid one or not
msg.To.Add(new MailAddress(emailAddress).Testing()));
msg.To.Add(new MailAddress(aDifferentEmailAddress, displayName).Testing()));
// other code ...
}
You can use replace in your case.
private string ToTestAccount(string emailAddress)
{
return emailAddress == null ? emailAddress : emailAddress.Replace("#","#test-");
}
Please note that this solution is valid only in case if you need not to deliver the message to the real user. If you need to change domain to deliver it it may not work as email may contain more than one # symbol. As #andyb952 mentioned in a comment it's very rare but possible.
I am working on a uwp app to send html email with embedded image. I was using EASendMail nuget pakage and it was fine after some time my app shows error:
A connection attempt failed because the connected party did not
properly respond after a period of time, or established connection
failed because connected host has failed to respond. (Exception from
HRESULT: 0x8007274c)
I think the trial period has expired what should I do?
using EASendMailRT;
https://www.emailarchitect.net/easendmail/kb/csharp.aspx?cat=8
I can't find any alternative
try
{
string ToAddress = MailSendPage.toAddressTxtBox;
string Subject = MailSendPage.subjectTxtBox;
SmtpMail oMail = new SmtpMail("TryIt");
oMail.From = new MailAddress(username);
if(!String.IsNullOrEmpty(ToAddress)&& !String.IsNullOrEmpty(Subject))
{
oMail.To.Add(new MailAddress(ToAddress));
oMail.Subject = Subject;
EASendMailRT.SmtpClient oSmtp = new EASendMailRT.SmtpClient();
SmtpServer oServer = new SmtpServer(host);
oServer.User = username;
oServer.Password = password;
oServer.Port = port;
if (IsStackPanalHasImg() == true)
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
string[] files = Directory.GetFiles(localFolder.Path + #"\ProjectImages");
foreach (string eachfile in files)
{
foreach (string name in covertToHtml.ControlName)
{
string pattern = string.Format("{0}.jpeg", name);
if (Regex.IsMatch(eachfile, pattern))
{
Attachment oAttachment = await oMail.AddAttachmentAsync(eachfile);
oAttachment.ContentID = name;
}
}
}
}
await oSmtp.SendMailAsync(oServer, oMail);
popUpMsgs.popup(" The Mail has been sent");
}
}
catch (Exception ep)
{
popUpMsgs.popup(String.Format("Failed to send email with the following error: {0}", ep.Message));
}
The built-in e-mail API only support sending plain text e-mail messages as Docs state:
This method only sends plain text messages. You can't set the body of the message to the HTML format.
What you can do is attach images to the e-mail:
EmailMessage mail = new EmailMessage();
mail.Sender = new EmailRecipient("test#example.com");
mail.To.Add(new EmailRecipient("someone#example.com"));
mail.Subject = "Hello";
mail.Body = "World";
var file = await StorageFile.GetFileFromApplicationUriAsync(
new Uri("ms-appx:///Assets/StoreLogo.png"));
mail.Attachments.Add(new EmailAttachment(file.Name, file));
await Windows.ApplicationModel.Email.EmailManager.ShowComposeNewEmailAsync(mail);
In addition, sending attachments works well only in case of the built-in UWP Outlook Mail client. Classic Outlook will most likely ignore the attachments altogether.
If you need to embed the image, you will need to use a e-mail service. I can recommend SendGrid or MailGun. Both have C# APIs which work like a breeze. They are also free for limited number of e-mails.
There are several ways you can embed the images in a HTML e-mail message.
The oldest is using CID (Content ID) which you were using in your question.
Second option is using Base64 encoding. You first turn your image into a Base64 string. There are many tutorials on this, for example in this blogpost. Then you can just embed the image in the src of your <img> tag:
<img src="data:image/jpeg;base64, YOURIMAGEINBASE64"/>
Finally you can embed an image which is hosted somewhere. This scales the best if you need to send the e-mail to many recipients, but of course requires actually hosting the image somewhere. Of the three methods it is also supported in most clients.
All three approaches are described in detail in this post.
I am configuring Sendgird for the first time and I want to send emails using c#.
This is the code I am using, and I know for a fact that it is working because I can use it with another sendgird account that was configured by the IT department:
public static async void SendEmailsToUser(string[] mailAddresses, string subject, string fullHtmlEmail)
{
var sendgridMessage = new SendGrid.SendGridMessage();
foreach (var emailAddress in mailAddresses)
sendgridMessage.AddTo(emailAddress);
sendgridMessage.From = new MailAddress("blah#blah.com", "blah");
sendgridMessage.Subject = subject;
sendgridMessage.Html = fullHtmlEmail;
var transportWeb = new SendGrid.Web("sendgrid_key");
await transportWeb.DeliverAsync(sendgridMessage);
}
The code generates no exceptions, but I don't receive the test email and the sendgrid webpage shows no trace of recent activity.
The only idea that comes to mind is that maybe I have not generated the API key correctly? (Settings -> API keys -> Generate new -> full permissions)
When I send an email using AWS SES in a C# application the email names don't show in the received email - only the email addresses show.
The from/to emails I've added are strings in the form " Their Name". It's clearly understanding that as it's sending it to the right place, but just stripping out the names.
internal void SendEmail()
{
try
{
// Construct an object to contain the recipient address.
Destination destination = new Destination();
destination.ToAddresses = toList;
if (ccList.Count > 0) destination.CcAddresses = ccList;
if (bccList.Count > 0) destination.BccAddresses = bccList;
// Create the subject and body of the message.
Body bodyobj = new Body();
if(body!=null) bodyobj.Text = new Content(body);
if(html!=null) bodyobj.Html = new Content(html);
// Create a message with the specified subject and body.
Message message = new Message(new Content(subject), bodyobj);
// Assemble the email.
SendEmailRequest request = new SendEmailRequest();
request.Destination = destination;
request.Message = message;
request.Source = from;
AmazonSimpleEmailServiceClient client = new AmazonSimpleEmailServiceClient(Amazon.RegionEndpoint.EUWest1);
SendEmailResponse ser = client.SendEmail(request);
sent=true;
}
catch (Exception e)
{
errmsg = e.Message;
}
}
You can provide recipients with names in the standard email format
Fred Bloggs <fred.bloggs#exmaple.com>
If you specify your ToAddresses as a list of strings of that format, the name will be set properly.
This conforms to the Internet Message Format (rfc5322) spec. See section 3.4.