1. 程式人生 > >用WPF做一個簡易瀏覽器

用WPF做一個簡易瀏覽器

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

WPF雖然出來也有大概十來年了,但是它的很多設計思想還是非常先進的,配合C#這門語言的話更加順手。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程式碼中新增一點程式碼就可以將事件和處理程式繫結起來。下面程式碼中的ClickKeyDown就是兩個事件,用於處理單擊滑鼠和鍵盤按鍵。

<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中我們只需要選擇控制元件,然後點選屬性中的相應事件,即可自動生成處理函式,我們只需要編寫程式碼即可。

VS截圖

瀏覽器控制元件

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

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();就可以搞定了。

當然問題又來了,這行程式碼應該往哪裡加呢?第一種辦法是在所有處理程式中新增這行程式碼, 也就是說,前進、後退的處理程式都需要進行修改。這樣並不是一個好辦法,萬一將來需求發生了變化,有好幾處地方都要修改,更容易出錯。解決辦法還是剛才說的事件。經過一番查詢,我發現了WebBrowserNavigated事件,顧名思義,這個事件會在呼叫了Navigate方法後觸發。這樣,只需要把這一行程式碼繫結到這個事件上就行了,程式碼非常優雅,酷斃了!

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

這樣,一個簡易的瀏覽器就實現完畢了。程式碼我放在了Csdn Code,有興趣的同學可以看看。