sobota, 26 grudnia 2009

Wysyłanie meili w .NET

Wysyłanie maila w .NET to jedna z prostszych czynności jakie możemy wymagać od naszego programu.

1. Przestrzeń System.Net.Mail

Przestrzeń ta zawiera wszystko co jest nam potrzebne aby w łatwy i szybki sposób wysłać maila. Najczęściej używanymi klasami z pewnością są:
Attachment – reprezentuje załącznik wiadomości
MailAddress – klasa reprezentująca mail nadawcy lub odbiorcy
MailMessage – reprezentuje wiadomość do wysłania
SmtpClient – pozwala na wysłanie utworzonego maila protokołem SMTP
SmtpException – wyjątek w przypadku błędu podczas wysyłania wiadomości


2. Tworzenie wiadomości email

 
Korzystając z wymienionych wcześniej klas możemy utworzyć naszą wiadomość. Rozpoczniemy od utworzenia obiektu klasy MailMessage:
    public MailMessage();
    public MailMessage(MailAddress from, MailAddress to);
    public MailMessage(string from, string to);
    public MailMessage(string from, string to, string subject, string body);

Jak widać mamy do dyspozycji 4 konstruktory. Parametry które przyjmują to:
From – mail osoby wysyłającej
To – mail osoby do której kierujemy wiadomość
Subject – temat wiadomości
Body – treść wiadomości
Wszystkie te parametry można ustawić po utworzeniu obiektu klasy MailMessage:
            MailMessage message = new MailMessage();
            //właściwość To jest typu MailAdressCollection tak więc możemy
            //dodać wiele osób do których kierujemy naszą wiadomość:
            //message.To.Add(new MailAddress("do_kogo1@o2.pl");
            //message.To.Add(new MailAddress("do_kogo2@o2.pl");
            //message.To.Add(new MailAddress("do_kogo3@o2.pl");
            message.To.Add(new MailAddress("do_kogo@o2.pl"));
            message.From = new MailAddress("od_kogo@o2.pl");
            message.Subject = "Temat naszej wiadomości";
            message.Body = "Treść naszej wiadomości";

Oprócz tych podstawowych właściwości możemy także ustawić takie rzeczy jak:
            //Wysłanie ukrytej kopii
            message.Bcc.Add(new MailAddress("do_kogo@o2.pl"));
            //Wysłanie kopi
            message.CC.Add(new MailAddress("do_kogo@o2.pl"));
            //Powiadomienie o stanie wysłania; opóźnienie, dostarczenie, niepowodzenie
            message.DeliveryNotificationOptions = DeliveryNotificationOptions.OnSuccess;
            //Priorytet wiadomości
            message.Priority = MailPriority.High;


3. Dodawanie załączników do wiadomości
 
Wiele wysyłanych przez nas wiadomości zawiera dodatkowe dane w postaci zdjęć, plików PDF czy też innego rodzaju dane audio/wideo. Aby do wiadomości dodać załączniki tworzymy obiekty klasy Attachment a w jego konstruktorze podajemy ścieżkę do pliku na dysku:
            message.Attachments.Add(new Attachment(@"C:\Users\Patryk\Documents\CV.docx"));
            message.Attachments.Add(new Attachment(@"C:\Users\Patryk\Documents\Lista_Zakupow.docx"));



4. Wsparcie dla znaczników HTML
 
Aby włączyć w wiadomości wsparcie dla znaczników HTML należy najpierw ustawić właściwość IsBodyHtml na true:
message.IsBodyHtml = true;
Po tej operacji możemy używać znaczników HTML w naszej wiadomości:
message.Body = @"<html><body><h1>Hello</h1><br><br>Co u ciebie słychać?</body></html>";
Aby dodać jakąś grafikę bezpośrednio w wiadomości (nie jako załącznik) należy skorzystać z klas AlternateView oraz LinkedResource:
            //treść naszej wiadomości ze znacznikami HTML
            string body = "<html><body><h1>Hello</h1><br><br>Co u ciebie słychać?<br>Tutaj moje zdjęcie z zeszłych wakacji: <br> <img src=\"cid:Zd1\"></body></html>";
            //tworzymy alternatywny widok
            AlternateView htmlView = AlternateView.CreateAlternateViewFromString(body);
            //wybieramy zdjęcie które zostanie dodane do naszej wiadomości HTML
            LinkedResource image = new LinkedResource(@"C:\Users\Patryk\Pictures\przyklad_graphic.png", "image/png");
            //nadajemy Id naszemu zdjęciu (później wstawiamy je jako cid:"Id"
            image.ContentId = "Zd1";
            htmlView.LinkedResources.Add(image);
            //dodajemy widok do wiadomości
            message.AlternateViews.Add(htmlView);



5. Wysyłanie wiadomości
 
Czas teraz na najważniejszą część czyli wysłanie wiadomości. Wysyłamy za pomocą protokołu SMTP więc tworzymy najpierw obiekt tej klasy:
SmtpClient smtp = new SmtpClient("poczta.o2.pl", 587);
W konstruktorze jak widać podałem port SMTP wykorzystywany przez o2.pl oraz nazwę serwera.
Jeśli serwer wymaga uwierzytelnienia, należy stworzyć obiekt klasy NetworkCredential i w konstruktorze podać wymagane parametry:
smtp.UseDefaultCredentials = false;
smtp.Credentials = new NetworkCredential("uzytkownik", "haslo");
Na końcu oczywiście wysyłamy nasze dzieło:
smtp.Send(message);

Jeśli zrobiliśmy wszystko zgodnie z instrukcją, nasza wiadomość powinna zostać wysłana bez żadnych błędów.
Wracając jeszcze do portów SMTP to należy sprawdzić jaki jest używany przez danego dostawcę poczty. Najczęściej to 587 od kiedy TP SA zablokowała port 25.

Tyle odnośnie wysyłania wiadomości. Miłego spamowania J

sobota, 12 grudnia 2009

Factory Method

aplikacja
Wzorcem obiektowym którym dzisiaj się zajmiemy jest Factory Method Pattern (Metoda Wytwórcza). Jest jeden z kreacyjnych wzorców projektowych, a jak sama nazwa sugeruje służy do tworzenia obiektów. Ściśle ujmując dostarcza on interfejsu do tworzenia obiektów, ale o typie obiektów decydują jego podklasy.
Zobaczmy na schemat klas:



Jak widać mamy tu do czynienia z dwoma klasami abstrakcyjnymi: Product czyli obiekt który chcemy utworzyć oraz Creator definiujący sposób jego utworzenia.

Zobaczmy na przykład. Rozważmy produkcję laptopów. Mamy dwie fabryki wytwarzające gotowe laptopy. Jedna z fabryk zlokalizowana jest w Polsce druga w Japonii. Fabryki produkują dwa rodzaje laptopów: modelA i modelB. Diagram klas:


Typ wyliczeniowy LaptopModel pozwala nam w prosty sposób tworzyć obiekty potrzebnego nam typu. Można także robić to w inny sposób jak np. przekazać string czy daną typu int. Enum wydał mi się tu jednym z najbardziej uniwersalnych i prostych rozwiązań.

Kod aplikacji można pobrać stąd: aplikacja

środa, 25 listopada 2009

Drukowanie

Drukowanie jest naturalną czynnością. W przypadku C# dysponujemy wieloma gotowymi metodami i klasami ułatwiającymi to zadanie. Artykuł rozpocznę od najbardziej elementarnych czynności które należy podjąć w celu wydrukowania strony, aż po bardziej zaawansowane mechanizmy. 



 

Każda operacja drukowania rozpoczyna się od utworzenia obiektu klasy PrintDocument. Cały proces drukowania rozpoczyna się w momencie wywołania metody Print klasy PrintDocument. Wywołanie tej metody pociąga za sobą wywołanie zdarzeń: BeginPrint, PrintPage, EndPrint. Jak z samych nazw można się domyślić kolejne zdarzenia oznaczają: rozpoczęcie procesu drukowania, drukowanie strony, zakończenie drukowania. Szczególnie ciekawe jest zdarzenie PrintPage. Jest ono wywoływane podczas drukowania każdej nowej strony. Zdarzenie to, na podstawie właściwości HasMorePages przesyła do drukarki informację, czy do drukowania pozostały jeszcze jakieś strony. Oprócz tego zdarzenie PrintPage zawiera dane które zamierzamy wydrukować.

Stwórzmy dla przykładu najprostszą aplikację pozwalającą na wydrukowanie, krótkiej ankiety osobowej:




Na formatkę rzucamy 4 labele i textboxy oraz jeden button (nazwy kontrolek pozostawiłem standardowe tj. label1, textBox1…). Po naciśnięciu na klawisz Drukuj, chcielibyśmy aby nasza drukarka zabrała się do pracy:) (po drodze oczywiście dodamy okienko umożliwiające wybranie która drukarka ma wydrukować dla nas stronę).
Oczywiście nasz interfejs należy oprogramować:



    1         private PrintDocument printDocument;
    2         private PrintDialog printDialog; //Okno dialogowe wyboru drukarki
    3         public Form1()
    4         {
    5             InitializeComponent();
    6         }
    7
    8         private void button1_Click(object sender, EventArgs e)
    9         {
   10             printDocument = new PrintDocument();
   11             printDialog = new PrintDialog();
   12             printDialog.Document = printDocument;
   13             printDocument.PrintPage += new PrintPageEventHandler(printDocument_PrintPage);
   14             printDocument.Print();
   15         }
   16
   17         void printDocument_PrintPage(object sender, PrintPageEventArgs e)
   18         {
   19             Graphics g = e.Graphics;
   20             string textToPrint = string.Format("Imie: {0}\nNazwisko: {1}\nRok urodzenia: {2}\nMiasto: {3}", textBox1.Text, textBox2.Text, textBox3.Text, textBox4.Text);
   21             using (Font font = new Font("Arial", 10))
   22             {
   23                 g.DrawString(textToPrint, font, Brushes.Black, 50, 50);
   24             }
   25         }

Dzięki obiektowi Graphics, niejako "malujemy" to co chcemy zobaczyć na wydrukowanej stronie. 


Całym procesem drukowania zarządza klasa PrintDocument. Dzięki niej możemy zarządzać wszystkimi aspektami drukowania. Spójrzmy na diagram przedstawiający podstawowe właściwości klas zarządzających procesem drukowania:


Oczywiście praktycznie wszystkie te wartości można ustawić w okienku PrintDialog, jednak warto wiedzieć, że można je ustawić także z kodu.

Aby utrwalić zdobytą wiedzę, proponuję stworzyć prostą aplikację, tworzącą raport zarobków pracowników firmy. Dane pobierzemy z pliku txt (można równie dobrze pobrać je z bazy danych). Struktura pliku jest następująca:

Jak widać mamy w naszym pliku kolumny takie jak: imię, nazwisko, wiek oraz zarobki. Interfejs naszej aplikacji może wyglądać tak:

Kod:


    1 using System;
    2 using System.Collections.Generic;
    3 using System.ComponentModel;
    4 using System.Data;
    5 using System.Drawing;
    6 using System.Linq;
    7 using System.Text;
    8 using System.Windows.Forms;
    9
   10 using System.IO;
   11 using System.Drawing.Printing;
   12
   13 namespace WindowsFormsApplication7
   14 {
   15     public partial class Form1 : Form
   16     {
   17         private StreamReader sr;
   18         private float lineHeight;
   19         private PrintDocument pd;
   20
   21         public Form1()
   22         {
   23             InitializeComponent();
   24             pd = new PrintDocument();
   25         }
   26
   27         private void button1_Click(object sender, EventArgs e)
   28         {
   29             using (OpenFileDialog openFile = new OpenFileDialog())
   30             {
   31                 openFile.Filter = "Text Files |*.txt";
   32                 if (openFile.ShowDialog() == DialogResult.OK)
   33                 {
   34                     richTextBox1.LoadFile(openFile.FileName, RichTextBoxStreamType.PlainText);
   35                     sr = new StreamReader(openFile.FileName, Encoding.UTF8);
   36                     textBox1.Text = openFile.FileName;
   37                     button3.Enabled = true;
   38                 }
   39             }
   40
   41         }
   42
   43         private void button3_Click(object sender, EventArgs e)
   44         {
   45             pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
   46             pd.Print();
   47         }
   48
   49         void pd_PrintPage(object sender, PrintPageEventArgs e)
   50         {
   51             Graphics g = e.Graphics;
   52             int lineWrite = 2;
   53             string actualLine;
   54             string[] printLine;
   55             float y = 0;
   56             using (Font f = new Font("Arial", 10))
   57             using (Font f2 = new Font("Arial",10, FontStyle.Bold))
   58             {
   59                 lineHeight = f.GetHeight(g);
   60                 int linesPerPage = (int)((e.MarginBounds.Bottom - e.MarginBounds.Top) / lineHeight);
   61                 g.DrawString("Raport zarobków", f2, Brushes.Black, new PointF(0, y));
   62                 y = 2 * lineHeight;
   63                 while ((actualLine = sr.ReadLine()) != null)
   64                 {
   65                     printLine = actualLine.Split(';');
   66                     string firstName = printLine[0];
   67                     string lastName = printLine[1];
   68                     int age = int.Parse(printLine[2]);
   69                     decimal wage = decimal.Parse(printLine[3]);
   70                     g.DrawString(firstName, f, Brushes.Black, new PointF(0, y));
   71                     g.DrawString(lastName, f, Brushes.Black, new PointF(100, y));
   72                     g.DrawString(age.ToString(), f, Brushes.Black, new PointF(200, y));
   73                     g.DrawString(wage.ToString(), f, Brushes.Black, new PointF(300, y));
   74                     y += lineHeight;
   75                     lineWrite++;
   76                     if (lineWrite > linesPerPage)
   77                     {
   78                         e.HasMorePages = true;
   79                         break;
   80                     }
   81                 }
   82             }
   83         }
   84     }
   85 }

 Dla przykładu zamieszczam całą aplikację (wraz z kodami źródłowymi) gotową do użycia: pobierz. Odnośnie samego drukowania nie jest ono takie trudne jak się wydaje. Istnieje wiele bibliotek wspomagających ten proces (jedną z popularniejszych jest z pewnością Crystal Reports).

sobota, 14 listopada 2009

Kontrolka w kontrolce, czyli o Content w WPF

Zapewnie wielu z was myślało o zwariowanych pomysłach umieszczenia w np. Buttonie TextBoxa. Dzięki nowemu modelowi, który wprowadzono w WPF takie sztuczki są możliwe. Prawie każda kontrolka w WPF dziedziczy po klasie ContentControl. Dzięki temu kontrolki uzyskują możliwość przechowywania w sobie innych kontrolek. Najłatwiej zobaczyć to na przykładzie. Wspominałem na początku o możliwości umieszczenia w Buttonie kontrolki TextBox. Spróbujmy więc osiągnąć taki efekt:


Kod potrzebny aby wygenerować taki efekt jest niezwykle prosty:
   21         <Button>
   22             <TextBox MinWidth="50"></TextBox>
   23         </Button>

Oczywiście w Buttonie możemy umieścić inne rzeczy jak np. jakiś obrazek, TextBlock itd.

Przyjrzyjmy się teraz kontrolką, które mogą przyjąć tylko jedną kontrolkę.

ScrollViewer
W WPF nie ma innego kontenera który umożliwia przewijanie swojej zawartości w tak prosty sposób jaki to robi ScrollViewer. Jako wewnętrzny kontener, stosujemy przeważnie dla niego Grid. Przykład:

 Efekt ten uzyskałem dzięki położeniu w ScrollViewerze Grida, a w nim 9 wierszy i 9 przycisków. Kod:

    6         <ScrollViewer>
    7             <Grid>
    8                 <Grid.RowDefinitions>
    9
   10                     <RowDefinition MinHeight="25"></RowDefinition>
   11                     <RowDefinition MinHeight="25"></RowDefinition>
   12                     <RowDefinition MinHeight="25"></RowDefinition>
   13                     <RowDefinition MinHeight="25"></RowDefinition>
   14                     <RowDefinition MinHeight="25"></RowDefinition>
   15                     <RowDefinition MinHeight="25"></RowDefinition>
   16                     <RowDefinition MinHeight="25"></RowDefinition>
   17                     <RowDefinition MinHeight="25"></RowDefinition>
   18                     <RowDefinition MinHeight="25"></RowDefinition>
   19                     <RowDefinition MinHeight="25"></RowDefinition>
   20                 </Grid.RowDefinitions>
   21                 <Button Grid.Row="0"></Button>
   22                 <Button Grid.Row="1"></Button>
   23                 <Button Grid.Row="2"></Button>
   24                 <Button Grid.Row="3"></Button>
   25                 <Button Grid.Row="4"></Button>
   26                 <Button Grid.Row="5"></Button>
   27                 <Button Grid.Row="6"></Button>
   28                 <Button Grid.Row="7"></Button>
   29                 <Button Grid.Row="8"></Button>
   30                 <Button Grid.Row="9"></Button>
   31             </Grid>
   32         </ScrollViewer>


Scroll jest aktywny w momencie gdy jest zbyt mało miejsca aby wyświetlić zawartość Grida. Gdy rozciągniemy okno Scroll przestaje być aktywny.

GroupBox
Reprezentuje prosty prostokąt z tytułem. Można w nim zgrupować kontrolki które np służą do wybierania jakichś opcji. Przykład:

Kod aby taki efekt uzyskać:

 
    5     <Grid>
    6         <Grid.RowDefinitions>
    7             <RowDefinition></RowDefinition>
    8             <RowDefinition></RowDefinition>
    9             <RowDefinition></RowDefinition>
   10         </Grid.RowDefinitions>
   11         <Grid.ColumnDefinitions>
   12             <ColumnDefinition></ColumnDefinition>
   13             <ColumnDefinition></ColumnDefinition>
   14             <ColumnDefinition></ColumnDefinition>
   15         </Grid.ColumnDefinitions>
   16         <GroupBox Grid.Column="1" Grid.Row="1" Header="Opcje">
   17             <StackPanel>
   18                 <CheckBox>
   19                     <CheckBox.Margin>
   20                         <Thickness Top="4" Bottom="4"></Thickness>
   21                     </CheckBox.Margin>
   22                     Opcja pierwasz
   23                 </CheckBox>
   24                 <CheckBox>
   25                     <CheckBox.Margin>
   26                         <Thickness Top="4" Bottom="4"></Thickness>
   27                     </CheckBox.Margin>
   28                     Opcja druga
   29                 </CheckBox>
   30                 <CheckBox>
   31                     <CheckBox.Margin>
   32                         <Thickness Top="4" Bottom="4"></Thickness>
   33                     </CheckBox.Margin>
   34                     Opcja trzecia
   35                 </CheckBox>
   36                 <Button>
   37                     <Button.Margin>
   38                         <Thickness Top="20"></Thickness>
   39                     </Button.Margin>
   40                     Ok
   41                 </Button>
   42             </StackPanel>
   43         </GroupBox>
   44     </Grid>

TabItem
TabItem reprezentuje pojedynczą stronę w kontrolce TabControl. Za pomocą tej kontrolki można uzyskać modne w dzisiejszych aplikacjach zakładki:

 Kod dzięki któremu uzyskamy taki efekt:
 

    5     <Grid>
    6         <TabControl>
    7             <TabItem Header="Opcje czcionki">
    8                 <StackPanel>
    9                     <Button>Kliknij mnie</Button>
   10                     <CheckBox>Zaznacz mnie</CheckBox>
   11                     <RadioButton>Wybierz mnie</RadioButton>
   12                 </StackPanel>
   13             </TabItem>
   14             <TabItem Header="Opcje wideo"></TabItem>
   15             <TabItem Header="Opcje dzwiku"></TabItem>
   16         </TabControl>
   17     </Grid>

Expander
Pozwala użytkownikowi na ukrywanie/otwieranie jego zawartości poprzez klikanie na małą strzałkę. Najlepiej wyjaśni to przykład:

 Kod:

    5     <StackPanel>
    6         <Expander Grid.Row="0" Background="Azure">
    7             <StackPanel>
    8                 <Button>Przycisk</Button>
    9                 <RadioButton>RadioButton</RadioButton>
   10                 <CheckBox>CheckBox</CheckBox>
   11             </StackPanel>
   12         </Expander>
   13         <Expander Grid.Row="1" Background="BlanchedAlmond"></Expander>
   14     </StackPanel>

Dekoratory
Border - czyli najprościej ujmując kolor brzegu. Zawiera takie właściwości jak Background (kolor tła), BorderBrush i BorderThickness (kolor brzegu i jego grubość), CornerRadius (zaokrąglenie brzegów), Padding (dodaje przestrzeń pomiędzy brzegiem a zawartością kontrolki) .

Vievbox - umożliwia przeskalowywanie grafiki zawartej w nim (najlepiej do tego nadaje się oczywiście grafika wektorowa); rzadko przechowuje się w nim inne kontrolki.

Tyle na dziś. W następnej części kursu poznamy specjalny rodzaj zdarzeń i właściwości wykorzystywanych w WPF.