WPF Webbrowser, Frame i WebBrowser w WinForms - porównanie

Chcąc wczytać stronę www do naszej aplikacji w WPF'ie mamy trzy możliwości (pod warunkiem posiadania .net 3.5 SP1)

  1. wykorzystać kontrolkę WebBrowser (System.Windows.Controls.WebBrowser)
  2. wykorzystać kontrolkę Frame (System.Windows.Controls.Frame)
  3. oraz skorzystać w kontrolki WebBrowser z winforms (System.Windows.Forms.WebBrowser)

Dzisiejszy wpis będzie porównaniem tych trzech sposobów w kontekście pobierania i przetworznia kodu wczytanej strony.

Zacznijmy od stworzenia projektu (lub ściągnięcia gotowego kodu) gdzie dodajemy Grida z trzema kolumnami, w których kolejno będą kontrolki z punktów 1,2,3. Z dodaniem kontrolek WebBrowser i Frame z WPF'a nie powinno być żadnych problemów (przeciągamy je z ToolBox'a), lecz aby dodać WebBrowser z WinForms na początku musimy dodać dwie referencje do naszego projektu:System.Windows.Forms i WindowsFormsIntegration a następnie kontrolkę WindowsFormsHost w której to umieścimy WebBrowser'a z WinForms.

 

   1: <Grid>
   2:        <Grid.ColumnDefinitions>
   3:            <ColumnDefinition Width="*"></ColumnDefinition>
   4:            <ColumnDefinition Width="*"></ColumnDefinition>
   5:            <ColumnDefinition Width="*"></ColumnDefinition>
   6:        </Grid.ColumnDefinitions>
   7:        <Grid.RowDefinitions>
   8:            <RowDefinition Height="*"></RowDefinition>
   9:        </Grid.RowDefinitions>
  10:        
  11:        <DockPanel Grid.Column="0" Grid.Row="0" >
  12:            <TextBlock x:Name="tbWpfWBDesc" DockPanel.Dock="Top" Height="25" Margin="2" FontWeight="Bold" HorizontalAlignment="Center">Wpf WebBrowser Control</TextBlock>
  13:            <Button x:Name="btnWBPageLoad" DockPanel.Dock="Bottom">New Page</Button>
  14:            <WebBrowser x:Name="wbPage" ></WebBrowser>
  15:            
  16:        </DockPanel>
  17:        
  18:        <DockPanel Grid.Column="1" Grid.Row="0">
  19:            <TextBlock x:Name="tbFrameDesc" DockPanel.Dock="Top" Height="25"  Margin="2" FontWeight="Bold" HorizontalAlignment="Center">Wpf Frame Control</TextBlock>
  20:           <Button x:Name="btnFrmPageLoad" DockPanel.Dock="Bottom" >New Page</Button> 
  21:            <Frame x:Name="frmPage"></Frame>
  22:           
  23:        </DockPanel>
  24:        
  25:        <DockPanel Grid.Column="2" Grid.Row="0">
  26:            <TextBlock x:Name="tbWinWBDesc" DockPanel.Dock="Top" Height="25" Margin="2"  FontWeight="Bold" HorizontalAlignment="Center">Wpf Frame Control</TextBlock>
  27:            <Button x:Name="btnWinFormPageLoad" DockPanel.Dock="Bottom">New Page</Button>
  28:            <WindowsFormsHost>
  29:                <wf:WebBrowser x:Name="wbWinFormPage" />
  30:                
  31:            </WindowsFormsHost>
  32:            
  33:        </DockPanel>
  34:    </Grid>

Mamy już nasz widok, teraz aby wczytać stronę do naszych kontrolek wystarczy wywołać metodę Navigate z adresem strony (każda kontrolka ma trochę inną sygnaturę tej metody, lecz wszystkie udostępniają przeciążoną wersję  Navigate(Uri source) ). Wywołajmy ją zaraz po załadowaniu okna wczytując losową stronę z wikipedi (http://pl.wikipedia.org/wiki/special:random), gdy uruchomimy nasz projekt zobaczymy że załadują się trzy różne strony dodatkowo warto zauważyć że możemy przechodzić do innych stron klikając linki. Do tej pory różnic raczej nie zauważymy (prócz innych pasków przewijania w ostatniej kontrolce) i jeżeli w waszej aplikacji zależy wam tylko na prezentacji danej strony to wszystkie kontrolki są dobre. Warto także zauważyć że wszystkie obsługują historię przeglądanych stron.
Przejdźmy teraz do meritum, czyli po załadowaniu strony chcemy pobrać jej kod i tutaj pojawiają się już znaczące różnice.

WebBrowser z WPF

Pozwala na wyświetlanie strony www, wykonuje kod javascript oraz obsługuje flash’a jeżeli jest zainstalowany. Warto zwrócić na kilka eventów do których możemy się podpiąć:

  • Navigating – pozwala na podjęcie jakiejś akcji przed żądaniem strony, pomocna przy anulowaniu żądania (ustawiająć e.Cancel=true), parametr typu NavigatingCancelEventArgs dostarczy nam podstawowych informacji o żądaniu, lecz co się okazuje jedna z ważniejszych właściwości WebRequest jest ustawiana na NULL :)

NavigatinCancelArgs 

  • Navigated – nawiązano połączenie, rozpoczęto ściąganie dokumentu, może nie być jeszcze w całości
  • LoadCompleted – ściąganie dokumentu już się zakończyło, powinien być w całości (w praktyce korzystać z tego zdarzenia)
  • Dwa kolejne zdarzenia wywoływane po nawiązaniu już połączenia z serwerem, obie w parametrze dostają obiekt NavigationEventArgs. Po przyjrzeniu się jego właściwościom ucieszymy się na widok WebResponse, z którego możemy pobrać strumień i go odczytać (będziemy mieli upragniony kod strony) np. takim kodem

       1: if(e.WebResponse!=null)
       2: {
       3:     //always false
       4:     var response = e.WebResponse;
       5:     var responseStream = (Stream)response.GetResponseStream();
       6:     var readStream = new StreamReader(responseStream, true);
       7:  
       8:  
       9:     //absolut URI 
      10:     var address = response.ResponseUri.AbsoluteUri;
      11:     var webContent = readStream.ReadToEnd();
      12: }

    lecz życie byłoby zbyt piękne gdyż okazuje się że on także jest ustawiany na NULL

    NavigationEventArgs

    co nam zostaje, a może sama kontrolka ma właściwość z której pobierzemy kod? Otóż okazuje się że tak jest to właściwość Document, a więc do dzieła, trzeba dodać referencję do Microsoft.mshtml

       1: mshtml.HTMLDocument document = wbPage.Document as mshtml.HTMLDocument;
       2: string pageHtml = document.documentElement.outerHTML;

    No pięknie jesteśmy w domu mamy pobrany kod HTML. Przyjrzyjmy mu się bliżej, pobraliśmy stronę z wikipedii zgodną z XHTML a tutaj dostajemy HTML’a bez zamkniętych tagów bez DocType itp. jednak nie o to nam chodziło. Kod jest automatycznie modyfikowany i transformowany właśnie do HTML.

       1: <HTML dir=ltr lang=pl xml:lang="pl" xmlns="http://www.w3.org/1999/xhtml"><HEAD><TITLE>Akademia Świętego Łukasza – Wikipedia, wolna encyklopedia</TITLE>
       2: <META content="text/html; charset=utf-8" http-equiv=Content-Type>
       3: <META content=text/css http-equiv=Content-Style-Type>
       4: <META name=generator content="MediaWiki 1.16alpha-wmf">
       5: <META name=keywords content="Akademia Świętego Łukasza,1577,Architekt,Cech,Europa,Haft,Malarstwo,Papież Grzegorz XIII,Rzeźba,Rzym,Wł."><LINK title=Edytuj rel=alternate type=application/x-wiki href="/w/index.php?title=Akademia_%C5%9Awi%C4%99tego_%C5%81ukasza&amp;action=edit">
       6: ....

    Wobec powyższego powstaje pytanie, czy istnieje możliwość pobrania kodu załadowanej strony z kontrolki WebBrowser z WPF’a? Ja nie znalazłem jak to można zrobić :)

    Frame

    Pozwala na wczytywanie stron www oraz WPF Pages. Posiada eventy podobne do WebBrowser, mają one także podobną sygnaturę

    • Navigating
    • Navigated
    • LoadCompleted

    Lecz tym razem obiekty WebRequest i WebResponse w zdarzeniach są ustawiane, hura :)

    frmNavigationEventArgs 

    Zabieramy się do pobierania kodu strony, czytając z strumienia korzystając z kodu

       1: if (e.WebResponse != null)
       2: {
       3:     var response = e.WebResponse;
       4:     var responseStream = (Stream)response.GetResponseStream();
       5:  
       6:     //uwaga wyrzuca wyjątek
       7:     //throw ArgumentException Stream was not readable.
       8:     var readStream = new StreamReader(responseStream, true);
       9:     string webContent = readStream.ReadToEnd();
      10:  
      11:     var address = response.ResponseUri.AbsoluteUri;
      12:  
      13: }

    lecz dostaniemy wyjątek “Stream was not readable” czyli nie odczytamy kodu strony. Poza tym pojawia się drugi problem iż zdarzenia (Navigating, Navigated, LoadCompleted) są wywoływane tylko za pierwszym razem, gdy klikamy w linki i przechodzimy do nowych stron żaden handler nie jest wywoływany. Gdy jawnie wywołamy metodę Navigate to handlery zadziałają lecz obiekt WebResponse będzie równy Null i strona w kontrolce się nie zmieni pomaga wywołanie metody Refresh. Wobec powyższych i ta kontrolka nie pozwala na pobranie kodu załadowanej strony.

    WebBrowser from WinForms

    Działa podobnie jak WebBrowser z WPF’a pozwala załadowanie strony, wykonuje javascript oraz flash’a. Nas będą interesowały zdarzenia:

    • Navigating
    • Navigated
    • DocumentCompleted

    Zdarzenia wywoływane są w podobnych okolicznościach co w powyższych przykładach, lecz dostają inne obiekty odpowiednio WebBrowserNavigatingEventArgs, WebBrowserNavigatedEventArgs, WebBrowserDocumentCompletedEventArgs. Zbadajmy ostatnie zdarzenie i parametry jakie dostaje

    winDocumentCompletedEventArgs

    niestety nie ma obiektu WebResponse tylko sam Url strony którą pobrano. Ale nie załamujmy się otóż okazuje się że WebBrowser z WinForms ma jedną cenną właściowść DocumentText, która zwraca tekst pobranej strony (bez żadnych modyfikacji) aby go pobrać wystarczy

       1: void wbWinFormPage_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
       2: {
       3:     string webContent = wbWinFormPage.DocumentText;
       4: }

    Przy korzystaniu z tej kontrolki zauważyłem dodatkowo że każdy handler wywoływany jest dwa razy. Pierwszy z Url’em == “auto:blank” a drugi z tym właściwym, lecz jakoś mi to nie przeszkadzało.

    Podsumowanie

    Chcąc dać użytkownikowi możliwość przeglądania stron www w naszej aplikacji możemy zastosować te trzy kontrolki, lecz jeżeli chcemy mieć dostęp do źródła strony, wiedzieć kiedy nastąpiło żądanie nowej strony pod jaki adres kieruje się użytkownik to zdecydowanie polecam WebBrowser z WinForms.

    Ściągnij przykład

    Tagi: , ,

    BlipFace klient do blip'a


    Ponad miesiąc temu pisałem o blipface'ie, desktopowym kliencie do sewrisu blip.pl. Projekt cały czas się rozwija raz szybciej raz wolniej, lecz cały czas do przodu. Wpis ten będzie raczej podusmowaniem co udało nam się zrobić od tamtej pory i jednocześnie zachętą do ściągnięcia kodu (tak, tak BlipFace jest OpenSource od samego początku)

    Po krótce co udało nam się zrobić przez ostatni miesiąc:

    • BlipFace ma bloga na którym powiadamiamy o nowych wydaniach 
    • stworzyliśmy także miejsce do zgłaszania błędów i uwag
    • Dodaliśmy funkcjonalności: logowanie, zapisywanie loginu i hasła, możliwość cytowania, odpowiadania, wyświetlanie zdjęć, klikalne linki, minimalizowanie do tray'a 

    Teraz do rzeczy co zrobić aby uruchomić aplikację, potrzebne nam będą:

    Gdy mamy już wszystko zainstalowane, ściągamy kod z serwisu github.com klikając pawym przyciskiem myszy na folderze, w którym chcemy mieć projekt wybieramy GitExtension->Clone a w okienku wpisujemy git://github.com/ksirg/blipface.git całość powinna wyglądać tak:
      

     
     Po uruchomieniu Visual Studio zobaczymy trzy projekty:
    1. BlipFace - główna aplikacja, widok w WPF'ie. W programie zaimplementowałem własną wersję MVP opratą na UserControls
    2. BlipFace.Service - biblioteka opakowująca WCF REST do komunikacji z blipem, można wykorzystać w swoich innych projektach
    3. BlipFace.Install - projekt instalatora
    Zachęcam do zabawy z Debuggerem warto zacząć od klasy HostWindow.xaml.cs od niej wszystko się zaczyna. Dodam iż w folderze projektu znajduje się plik Enterprise Architect z diagramami klas oraz komentarzami. 
     
    Obecnie BlipFace wygląda tak:
     

    Tagi: , ,

    BlipFace kolejny klient do blip'a

    Chciałbym podzielić się z wami informacjami o projekcie, który wraz z Marcinem Czerpakiem zaczęliśmy stosunkowo niedawno. Jest to klient do serwisu http://blip.pl. Otóż od dłuższego czasu używałem blip'a przez GG lecz denerwowały mnie dwie rzeczy:

    1. Nie mogłem oglądać obrazków w statusach - trzeba było wejść na stronę
    2. Trudności z odpowiadaniem i cytowaniem użytkownika
    Wobec powyższych postanowiliśmy zrobić swoją własną aplikację, nazwa kodowa BlipFace :)
     
    Narazie jesteśmy jeszcze w fazie alfa, lecz część funkcjonaloności już jest i działa. W chwili obecnej blipface wygląda tak:
     

     
     
     Funkcjonalności które działają:
    • Pobieranie dashboardu użytkownika
    • Dodawanie wpisów
    Należy zaznaczyć że brakuje jeszcze logowania - narazie na sztywno w kodzie jest wpisany login i hasło użytkownika: blipface :) 
    Cały projekt ma udostępniony kod źródłowy na serwisie GitHub - http://github.com/ksirg/blipface/tree/master
     Aby go uruchomić należy ściągnąć kod, uruchomić plik BlipFace.sln w visual studio 2008, ewentualnie podmnieć login i hasło na swoje w kilku plikach.
     
    Czekamy na wasze uwagi,błędy, sugestie, mogą one być wyrażone w formie komentarzy pod wpisem lub na blipie (^ksirg , ^cnk , ^blipface)

    Tagi: ,

    Git i Github – instalacja, konfiguracja oraz narzędzia pod windows

    W wyniku ostatniego mojego zafascynowania git’em chciałbym się z wami podzielić kilkoma wskazówkami co do instalacji i korzystania(pod Windows) z tego narzędzia do wersjonowania. Swoje repozytoria będę trzymał na GitHub.com.

    Cały proces instalacji git’a omówiony jest dokładnie tu: Using Git and Github for the Windows for newbies. W skrócie:

    1. Rejestrujemy się w serwisie GitHub.com, nie przejmujcie się że podczas rejestracji prosi was o podanie swojego klucza publicznego, na początku można to pole zostawić puste
    2. Ściągamy msysGit – port aplikacji Git na Windows lub możemy od razu ściągnąć Git Extensions Complete SetUP - ściągnie git'a oraz sam dodatek (poniżej opis) 
    3. Generujemy klucze SSH do komunikacji z naszym repozytorium, wydając polecenie w „Git Bash”
      ssh-keygen –C
      user@example.com –t rsa
      Na wszelkie pytania zadawane w procesie rejestracji wygodnie jest nacisnąć tylko enter
    4. Przechodzimy do folderu gdzie znajdują się wygenerowane klucze (publiczny i prywatny)
      1. C:\Documents and Settings\UserName\.ssh\ na XP lub
      1. C:\Users\UserName\.ssh\ na viście
    5. Odnajdujemy tam plik id_rsa.pub, otwieramy go w notatniku, kopiujemy zawartość i wklejamy jako ssh key na naszym koncie na GitHub’ie

    Po tych pięciu krokach możemy już korzystać z dobrodziejstw git’a. Wszelkie akcje można wykonać używać konsoli, lecz ja jestem zwolennikiem raczej graficznych interfejsów użytkownika. Na szczęście instalując msysGit’a prócz „Git Bash” mamy dostęp do (trochę topornej) aplikacji „Git GUI”, aby zapoznać się z jej możliwościami polecam An Illustrated Guide to Git on Windows.
    Dostępna jest także alternatywa w postaci
    Gitextensions prócz tego że jest „ładniejsza” to posiada plugin do Visual Studio 2005 i 2008.

    Drobna uwaga odnośnie konfiguracji GitExtension, po instalacji przy pierwszym uruchomieniu mogą pojawić się problemy (zaznaczono na czerwono na check liście) z ścieżkami do git.exe i git.cmd. Problemy oczywiście pojawiają się jeżeli nie postępujemy zgodnie z domyślnymi ustawieniami, co niestety zazwyczaj nie leży w mojej naturze. U siebie msysGit zainstalowałem na innym dysku niż systemowy C:\

    clip_image002

    Powyższe ścieżki można ustawić w drugiej zakładce „Git extensions”, u mnie to wygląda następująco

    clip_image004

    Warto także zajrzeć na zakładkę SSH i jeżeli nie chcecie korzystać z Putty’ego to zaznaczyć OpenSSH

    clip_image006

    Dla zainteresowanych GitExtension polecam przeczytać GitExtensionUserManual_v03.pdf oraz obejrzeć kilka filmików instruktarzowych przygotowanych przez samego autora

    1. Clone - Git Extensions - http://www.youtube.com/watch?v=TlZXSkJGKF8
    2. Commit changes - http://www.youtube.com/watch?v=B8uvje6X7lo 
    3. Push changes - http://www.youtube.com/watch?v=JByfXdbVAiE 
    4. Pull changes - http://www.youtube.com/watch?v=9g8gXPsi5Ko 
    5. Handle merge conflicts - http://www.youtube.com/watch?v=Kmc39RvuGM8

    A także “How to setup Git Extensions with VS.NET 2008” http://www.youtube.com/watch?v=OcjMg8sQg5I&feature=related

    Tagi: , , , ,

    SVN lub Git w chmurze

    Przeglądając dziś newsy z świata moje oko zatrzymało się na wpisie z codebetter.com - New Backup Strategy to co mnie najbardziej zainteresowało to darmowa (co prawda z ograniczeniami) usługa http://unfuddle.com - Software Project Management Git and Subversion Hosting.

    Wersja darmowa daje nam 200MB przestrzeni dyskowej, dla mnie to w zupełności wystarcza mój największy projekt jak do tej pory zajmuje jak narazie 30MB. Dodatkow dostajemy nielimitowaną ilość repozytoriów(ogranicza nas tylko disk space) oraz dostęp dla dwóch osób.

    Ja już założyłem konto i testuje jak narazie wszystko działa bez zarzutów, szybko i sprawnie. Dlaczego zdecydowałem się na unfuddle.com otóż głównie dzięki backup'owi który mam przy okazji nie ja muszę się o nie troszczyć, a że wszystko działa w chmurze i jest rozproszone mam pewność że co by się nie działo to dostęp do svn będę maił. Oczywiście część projektów nadal trzymam na swoim svn'ie lecz widomo przywracania ewentualnych backupów przeraża mnie. 

    Dla tych co wolą git'a  fajną opcją jest http://github.com/ pozwala tworzyć nieograniczoną ilość repo, daje nieograniczoną ilość disk space pod warunkiem że tworzymy projekt open source.


    Ps. Aby w pełni przetestować subversion polecam kilenta http://tortoisesvn.net/downloads 
    Ps.2 Jeżeli chcecie zasubskrybować bloga eastgroup do swojego czytnika rss oto adres kanału 
    http://feeds2.feedburner.com/eastgroup

    Tagi: , ,

    Eastgroup.pl na facebooku