天天看點

用WPF做一個簡易浏覽器

微軟的WPF(Windows Presentation Foundation)是目前Windows平台上最好用的圖形界面架構了。如果想在Windows平台上編寫圖形界面程式,而且沒有跨平台且性能需求比較高,而且對C#語言比較熟悉,那麼WPF就是最适合你的了。

WPF雖然出來也有大概十來年了,但是它的很多設計思想還是非常先進的,配合C#這門語言的話更加順手。WPF的界面設計和程式功能完全解耦,也就是說設計界面和編寫程式功能可以互不幹擾的同時進行。

好了,廢話不多說,下面直接開始吧。當然需要說明,這篇文章不是講如何實作浏覽器的,而是利用WPF的一個浏覽器控件,讓大家了解一下WPF的一些簡單功能。由于WPF元件龐大,沒辦法在一篇文章中詳細介紹。是以如果大家通過這篇文章對WPF有了一些興趣,那麼這篇文章的目的就達到了。

先來看看效果圖吧。當然功能比較簡陋,隻有前進、後退、重新整理幾個功能。當然如果太複雜,就沒辦法在一篇文章中說完了。

用WPF做一個簡易浏覽器
效果圖

界面設計

布局

不管是什麼圖形界面架構,首先讨論的都是界面布局了。布局負責組織界面元素如何排列和顯示。合适的界面布局可以降低我們程式界面的複雜度。如果需要了解布局的話,可以看看

這篇英文文章

,或者查找其他中文文章。

這裡簡單說一下常用的幾種布局:

  • StackPanel。将部件按照垂直或水準順序依次排列。
  • WrapPanel。和前者差不多,不過如果部件太多,會自動安排到下一行顯示。
  • DockPanel。可以指定上下左右中五個方位的元件。
  • Grid。網格布局,可以按照網格形式排列元件。

現在傳回來看看這個浏覽器的布局。首先第一行是按鈕和位址欄,第二行就是浏覽器控件了。是以在這裡我使用了DockPanel,第一行我指定為Top;第二行不指定,也就是充滿整個剩餘空間。

然後來看看第一行的布局,這裡我希望前三個按鈕按順序排列,最後的位址欄充滿整個剩餘空間。是以第一行本身也需要使用DockPanel來實作。

最後來看看相應的XAML代碼,雖然說得比較多,但是代碼倒是很少。

<DockPanel>
    <DockPanel DockPanel.Dock="Top">
        <Button Name="ForwardButton" Content="前進" Click="ForwardButton_Click" />
        <Button Name="BackButton" Content="後退" Click="BackButton_Click" />
        <Button Name="RefreshButton" Content="重新整理" Click="RefreshButton_Click" />
        <TextBox Name="UrlTextBox" KeyDown="UrlTextBox_KeyDown" />
    </DockPanel>
    <WebBrowser Name="BrowserControl" />
</DockPanel>
           

控件

其實關于控件我倒是沒什麼說的。不管是哪種圖形界面,反正控件總是按鈕、文本域、标簽那些東西。這裡我用到的就是按鈕和文本框,當然最重要的是WPF提供的浏覽器控件WebBrowser,它封裝了浏覽器的操作以便我們直接使用。

當然WPF還有一個非常重要的特點就是代碼可以無縫引用界面控件,這一點将在後面展現。這個特點可是很多圖形界面架構不提供的,比方說安卓的代碼要引用界面元素的話就得使用

getElementById

方法。

樣式

最後要說的就是樣式了。WPF的樣式和HTML的樣式在文法上很相似,我們既可以直接在界面元素上指定它的樣式,也可以在其他地方統一管理。當然如果要符合軟體設計的最佳實踐,樣式當然需要在一個地方統一指定比較好。

當然,WPF的樣式非常豐富,可以對一個控件進行深度定制,讓它“重新做人”。是以我就不做介紹了,等到需要的時候在查閱就行了。這裡隻設定了按鈕和文本框的寬度和外邊距,外邊距的4個值分别代表上、左、右、下外邊距。如果不在這裡統一設定,那麼就要針對每個按鈕設定一次外邊距,這是件很麻煩的事情。

<Window.Resources>
    <Style TargetType="Button">
        <Setter Property="Width" Value="45" />
        <Setter Property="Margin" Value="10,10,0,10" />
    </Style>
    <Style TargetType="TextBox">
        <Setter Property="Margin" Value="10,10,10,10" />
    </Style>
</Window.Resources>
           

功能編寫

事件處理

說完了界面的部分,下面來說說如何編寫程式功能。利用強大的XAML,我們可以非常友善的将界面元件和功能代碼對應起來。C#有一個特性叫做事件,WPF也利用了事件來處理程式響應。WPF的控件都包含了大量事件,可以處理滑鼠、鍵盤、觸屏等等各種事件,而且僅需要在XAML代碼中添加一點代碼就可以将事件和處理程式綁定起來。下面代碼中的

Click

KeyDown

就是兩個事件,用于處理單擊滑鼠和鍵盤按鍵。

<DockPanel>
    <DockPanel DockPanel.Dock="Top">
        <Button Name="ForwardButton" Content="前進" Click="ForwardButton_Click" />
        <Button Name="BackButton" Content="後退" Click="BackButton_Click" />
        <Button Name="RefreshButton" Content="重新整理" Click="RefreshButton_Click" />
        <TextBox Name="UrlTextBox" KeyDown="UrlTextBox_KeyDown" />
    </DockPanel>
    <WebBrowser Name="BrowserControl" />
</DockPanel>
           

每個事件的處理函數簽名都不相同,比方說單擊滑鼠的事件簽名就是

Click(object sender, RoutedEventArgs e)

,而按下鍵盤的事件簽名是

KeyDown(object sender, KeyEventArgs e)

。在Visual Studio中我們隻需要選擇控件,然後點選屬性中的相應事件,即可自動生成處理函數,我們隻需要編寫代碼即可。

用WPF做一個簡易浏覽器

VS截圖

浏覽器控件

說完了事件機制,下面我們來看看如何用它來搞點事情。由于WPF提供了友善的浏覽器控件,是以這裡的代碼非常簡單,隻需要調用浏覽器控件的相應方法即可。由于沒有單獨的處理按下回車的事件,是以這裡用的是按下鍵盤的事件,然後在處理程式中判斷按下的是否是Enter鍵,如果是的話再進行下一步處理,也就是讓浏覽器導航到對應網址。

private void ForwardButton_Click(object sender, RoutedEventArgs e)
{
    BrowserControl.GoForward();
}
private void BackButton_Click(object sender, RoutedEventArgs e)
{
    BrowserControl.GoBack();
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
    BrowserControl.Refresh();
}
private void UrlTextBox_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Return)
    {
        var url = UrlTextBox.Text;
        BrowserControl.Navigate(url);
    }
}
           

自動補全URL

上面的代碼已經基本可以使用,不過還是有一個小問題。那就是如果輸入的不是URL格式(

http://www.baidu.com

),而是網址(

www.baidu.com

),那麼程式就會崩掉。因為浏覽器控件隻能接受URL形式的字元串,如果不是合法的URL,那麼

BrowserControl.Navigate(url)

這一句代碼就會抛出異常。

那麼這個問題該如何解決呢?我在這裡直接使用正規表達式做一下測試,如果如果輸入的不是有效的URL,那麼我就手動在網址前面添加一個

http://

。實作方法很簡單,直接看代碼就行了。

public partial class MainWindow : Window
{
    private readonly Regex _urlPattern = new Regex(@"\w*://\w*(.\w*)+");
    public MainWindow()
    {
        InitializeComponent();
    }
    //省略了那些事件處理程式
    private void UrlTextBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Return)
        {
            var url = UrlTextBox.Text;
            if (!_urlPattern.IsMatch(url))
            {
                url = "http://" + url;
            }
            BrowserControl.Navigate(url);
        }
    }
}
           

如果留心一下前面的XAML就會發現,在下面的代碼中直接引用了XAML中浏覽器控件的名字

BrowserControl

,并可以調用它的屬性和方法。這也是浏覽·WPF一個非常友善的特性。

更改位址欄URL

下面就剩下最後一個問題了。一般浏覽器的位址欄,會随着通路網址的變化而變化。但是我們這個浏覽器卻沒有這個功能,位址欄的位址永遠是輸入的那個位址。現在我們希望不論是前進、後退,還是從浏覽器中點選其他連結,位址欄的位址都會跟着更新。

當然實作這個功能也很簡單,查閱一下浏覽器控件就可以發現,它有一個屬性叫做

Source

,恰好就是目前頁面的URL,是以利用這個屬性就可以完美的實作我們的功能了。這樣,隻需要一行代碼

UrlTextBox.Text = BrowserControl.Source.ToString();

就可以搞定了。

當然問題又來了,這行代碼應該往哪裡加呢?第一種辦法是在所有處理程式中添加這行代碼, 也就是說,前進、後退的處理程式都需要進行修改。這樣并不是一個好辦法,萬一将來需求發生了變化,有好幾處地方都要修改,更容易出錯。解決辦法還是剛才說的事件。經過一番查找,我發現了

WebBrowser

Navigated

事件,顧名思義,這個事件會在調用了

Navigate

方法後觸發。這樣,隻需要把這一行代碼綁定到這個事件上就行了,代碼非常優雅,酷斃了!

public MainWindow()
{
    InitializeComponent();
    BrowserControl.Navigated += BrowserControl_Navigated;
}
private void BrowserControl_Navigated(object sender, NavigationEventArgs e)
{
    UrlTextBox.Text = BrowserControl.Source.ToString();
}
           

這樣,一個簡易的浏覽器就實作完畢了。代碼我放在了

Csdn Code

,有興趣的同學可以看看。