微軟的WPF(Windows Presentation Foundation)是目前Windows平臺上最好用的圖形界面框架了。如果想在Windows平臺上編寫圖形界面程序,而且沒有跨平臺且性能需求比較高,而且對C#語言比較熟悉,那么WPF就是最適合你的了。
WPF雖然出來也有大概十來年了,但是它的很多設(shè)計思想還是非常先進(jìn)的,配合C#這門語言的話更加順手。WPF的界面設(shè)計和程序功能完全解耦,也就是說設(shè)計界面和編寫程序功能可以互不干擾的同時進(jìn)行。
好了,廢話不多說,下面直接開始吧。當(dāng)然需要說明,這篇文章不是講如何實現(xiàn)瀏覽器的,而是利用WPF的一個瀏覽器控件,讓大家了解一下WPF的一些簡單功能。由于WPF組件龐大,沒辦法在一篇文章中詳細(xì)介紹。所以如果大家通過這篇文章對WPF有了一些興趣,那么這篇文章的目的就達(dá)到了。
先來看看效果圖吧。當(dāng)然功能比較簡陋,只有前進(jìn)、后退、刷新幾個功能。當(dāng)然如果太復(fù)雜,就沒辦法在一篇文章中說完了。

界面設(shè)計
布局
不管是什么圖形界面框架,首先討論的都是界面布局了。布局負(fù)責(zé)組織界面元素如何排列和顯示。合適的界面布局可以降低我們程序界面的復(fù)雜度。如果需要了解布局的話,可以看看這篇英文文章,或者查找其他中文文章。
這里簡單說一下常用的幾種布局:
- StackPanel。將部件按照垂直或水平順序依次排列。
- WrapPanel。和前者差不多,不過如果部件太多,會自動安排到下一行顯示。
- DockPanel??梢灾付ㄉ舷伦笥抑形鍌€方位的組件。
- Grid。網(wǎng)格布局,可以按照網(wǎng)格形式排列組件。
現(xiàn)在返回來看看這個瀏覽器的布局。首先第一行是按鈕和地址欄,第二行就是瀏覽器控件了。所以在這里我使用了DockPanel,第一行我指定為Top;第二行不指定,也就是充滿整個剩余空間。
然后來看看第一行的布局,這里我希望前三個按鈕按順序排列,最后的地址欄充滿整個剩余空間。所以第一行本身也需要使用DockPanel來實現(xiàn)。
最后來看看相應(yīng)的XAML代碼,雖然說得比較多,但是代碼倒是很少。
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<Button Name="ForwardButton" Content="前進(jìn)" 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>
控件
其實關(guān)于控件我倒是沒什么說的。不管是哪種圖形界面,反正控件總是按鈕、文本域、標(biāo)簽?zāi)切〇|西。這里我用到的就是按鈕和文本框,當(dāng)然最重要的是WPF提供的瀏覽器控件WebBrowser,它封裝了瀏覽器的操作以便我們直接使用。
當(dāng)然WPF還有一個非常重要的特點就是代碼可以無縫引用界面控件,這一點將在后面體現(xiàn)。這個特點可是很多圖形界面框架不提供的,比方說安卓的代碼要引用界面元素的話就得使用getElementById方法。
樣式
最后要說的就是樣式了。WPF的樣式和HTML的樣式在語法上很相似,我們既可以直接在界面元素上指定它的樣式,也可以在其他地方統(tǒng)一管理。當(dāng)然如果要符合軟件設(shè)計的最佳實踐,樣式當(dāng)然需要在一個地方統(tǒng)一指定比較好。
當(dāng)然,WPF的樣式非常豐富,可以對一個控件進(jìn)行深度定制,讓它“重新做人”。所以我就不做介紹了,等到需要的時候在查閱就行了。這里只設(shè)置了按鈕和文本框的寬度和外邊距,外邊距的4個值分別代表上、左、右、下外邊距。如果不在這里統(tǒng)一設(shè)置,那么就要針對每個按鈕設(shè)置一次外邊距,這是件很麻煩的事情。
<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>
功能編寫
事件處理
說完了界面的部分,下面來說說如何編寫程序功能。利用強(qiáng)大的XAML,我們可以非常方便的將界面組件和功能代碼對應(yīng)起來。C#有一個特性叫做事件,WPF也利用了事件來處理程序響應(yīng)。WPF的控件都包含了大量事件,可以處理鼠標(biāo)、鍵盤、觸屏等等各種事件,而且僅需要在XAML代碼中添加一點代碼就可以將事件和處理程序綁定起來。下面代碼中的Click和KeyDown就是兩個事件,用于處理單擊鼠標(biāo)和鍵盤按鍵。
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<Button Name="ForwardButton" Content="前進(jìn)" 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>
每個事件的處理函數(shù)簽名都不相同,比方說單擊鼠標(biāo)的事件簽名就是Click(object sender, RoutedEventArgs e),而按下鍵盤的事件簽名是KeyDown(object sender, KeyEventArgs e)。在Visual Studio中我們只需要選擇控件,然后點擊屬性中的相應(yīng)事件,即可自動生成處理函數(shù),我們只需要編寫代碼即可。

瀏覽器控件
說完了事件機(jī)制,下面我們來看看如何用它來搞點事情。由于WPF提供了方便的瀏覽器控件,所以這里的代碼非常簡單,只需要調(diào)用瀏覽器控件的相應(yīng)方法即可。由于沒有單獨的處理按下回車的事件,所以這里用的是按下鍵盤的事件,然后在處理程序中判斷按下的是否是回車鍵,如果是的話再進(jìn)行下一步處理,也就是讓瀏覽器導(dǎo)航到對應(yīng)網(wǎng)址。
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);
}
}
自動補(bǔ)全URL
上面的代碼已經(jīng)基本可以使用,不過還是有一個小問題。那就是如果輸入的不是URL格式(http://www.baidu.com),而是網(wǎng)址(www.baidu.com),那么程序就會崩掉。因為瀏覽器控件只能接受URL形式的字符串,如果不是合法的URL,那么BrowserControl.Navigate(url)這一句代碼就會拋出異常。
那么這個問題該如何解決呢?我在這里直接使用正則表達(dá)式做一下測試,如果如果輸入的不是有效的URL,那么我就手動在網(wǎng)址前面添加一個 http://。實現(xiàn)方法很簡單,直接看代碼就行了。
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就會發(fā)現(xiàn),在下面的代碼中直接引用了XAML中瀏覽器控件的名字BrowserControl,并可以調(diào)用它的屬性和方法。這也是瀏覽·WPF一個非常方便的特性。
更改地址欄URL
下面就剩下最后一個問題了。一般瀏覽器的地址欄,會隨著訪問網(wǎng)址的變化而變化。但是我們這個瀏覽器卻沒有這個功能,地址欄的地址永遠(yuǎn)是輸入的那個地址?,F(xiàn)在我們希望不論是前進(jìn)、后退,還是從瀏覽器中點擊其他鏈接,地址欄的地址都會跟著更新。
當(dāng)然實現(xiàn)這個功能也很簡單,查閱一下瀏覽器控件就可以發(fā)現(xiàn),它有一個屬性叫做Source,恰好就是當(dāng)前頁面的URL,所以利用這個屬性就可以完美的實現(xiàn)我們的功能了。這樣,只需要一行代碼UrlTextBox.Text = BrowserControl.Source.ToString();就可以搞定了。
當(dāng)然問題又來了,這行代碼應(yīng)該往哪里加呢?第一種辦法是在所有處理程序中添加這行代碼, 也就是說,前進(jìn)、后退的處理程序都需要進(jìn)行修改。這樣并不是一個好辦法,萬一將來需求發(fā)生了變化,有好幾處地方都要修改,更容易出錯。解決辦法還是剛才說的事件。經(jīng)過一番查找,我發(fā)現(xiàn)了WebBrowser的Navigated事件,顧名思義,這個事件會在調(diào)用了Navigate方法后觸發(fā)。這樣,只需要把這一行代碼綁定到這個事件上就行了,代碼非常優(yōu)雅,酷斃了!
public MainWindow()
{
InitializeComponent();
BrowserControl.Navigated += BrowserControl_Navigated;
}
private void BrowserControl_Navigated(object sender, NavigationEventArgs e)
{
UrlTextBox.Text = BrowserControl.Source.ToString();
}
這樣,一個簡易的瀏覽器就實現(xiàn)完畢了。代碼我放在了Csdn Code,有興趣的同學(xué)可以看看。