抱歉,格式還是不夠順眼,從word復制過來,格式有了一些變化,雖然用Live Writer重排,但實在是麻煩。改天,我放Word附件上來。^_^
另外,請各位指點,教程怎樣寫,讀者會更有興趣。
01. 創建軟件雛形
這個雛形包括4個項目(Project):
- 一個主程序(WPF Application)項目,命名為:DataTracer
- 一個組件庫(WPF User Control Library)項目,命名為:K3UcLib
- 一個控件庫(WPF Custom Control Library)項目,命名為:K3CcLib
- 一個算法庫(Class Library)項目,命名為:K3Helper
作為入門,這看起來有點復雜,不過實現起來還算簡單。
菜單:File > New > Project… |
|
菜單:File > Add > New Project… |
THINKING:
通常,復雜是由簡單構成的,只要我們合理的分解它;
復雜的設計,並不意味着復雜的實現;同樣,簡單的設計,也不意味着簡單的實現;
不過,沒有准備迎接復雜挑戰的簡單設計,卻通常是小小噩夢的開始。
F5 運行,看看結果!
02. 制作一個組件
我習慣將User Control稱為組件,將Custom Control稱為控件。
他們的區分,簡單說:User Control通常通過組合多個控件產生,Custom Control通常通過擴展單個控件產生。
現在,我們先制作一個准備用來替換現有窗口標題的組件,命名為:K3WindowHeader。
Project à Add User Control,然后修改XAML,並編譯(Shift F6)
<Border CornerRadius="0, 0, 10, 10" Background="DarkBlue"> <DockPanel LastChildFill="True"> <Button Content="關閉" Width="75" Height="23" DockPanel.Dock="Right" /> <TextBlock Text="Window Header" DockPanel.Dock="Left" Foreground="White"/> </DockPanel> </Border> |
這個組件包括兩個控件Button,TextBlock。 CornerRadius用於設定Border四個角的弧度 = ”LeftTop, RightTop, RightBottom, LeftBottom” DockPanel.LastChildFill,很有用的屬性。 注意DockPanel所包含組件的順序,並在組件中正確的使用DockPanel.Dock屬性。 |
在DataTracer中使用這個組件。
Project à Add References,添加K3UcLib,然后修改MainWindow.XAML:
<Window … xmlns:K3UcLib="clr-namespace:K3UcLib;assembly=K3UcLib" WindowStyle="None" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <K3UcLib:K3WindowHeader Height="25" /> </Grid> |
將WindowStyle設為None,將窗口標題隱掉,從而為DIY新的窗口標題做好准備。 Height, Width可取值包括: Auto(自適應),*(全部剩余),NaN(相當於Auto),數值 |
F5 à 運行,看看結果!
這個窗口怎么關掉?Alt + F4!
這個窗口好丑!別着急,如果你真的很急,可以先試着使用 Margin屬性及xxxAlignment屬性,調整一下位置(試一試我的調整,如下)。
<Border CornerRadius="0, 0, 10, 10" Background="DarkBlue" Margin="3,0,3,0"> <DockPanel LastChildFill="True"> <Button Content="關閉" Width="75" Height="23" DockPanel.Dock="Right" Margin="0, 0, 10, 0" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Text="Window Header" DockPanel.Dock="Left" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" /> </DockPanel> </Border> |
F5 à 運行,看看結果!
03. 添加事件,關閉窗口
需求:當用戶點擊“關閉”按鈕時,關閉主窗口。
實現:為“關閉”按鈕添加Click事件處理。有兩種實現方式:
(1) 通過XAML文件中實現:<Button Name=”buttonClose” Content=”” …… Click=”buttonClose_Click” …… />
(2) 通過后台代碼實現:this.buttonClose.Click += new new RoutedEventHandler(buttonClose_Click);
這里,我們采用后者,即后台代碼方式,完整代碼如下:
public K3WindowHeader() { InitializeComponent(); this.Init(); } void Init() { this.buttonClose.Click += new RoutedEventHandler(Button_Click); } void Button_Click(object sender, RoutedEventArgs e) { throw new NotImplementedException(); } |
注: 使用Init()方法,是為了使代碼整潔。 在你鍵入“+=”時,系統將為你自動生成事件代碼。你可以先修改所生成的事件處理方法的名字,然后再按“TAB”鍵,這樣就可以生成你想要的名字的方法。在這里,我將默認的buttonClose_click改為Button_Click。 |
接下來,我們添加事件處理的內容,以實現關閉主窗體。
注意,因為我們使用的是另一個組件項目中的組件,而非主程序自身項目中的組件,所以實現起來稍微有點麻煩,不是簡單的一個Close()就可以完成。
在這種情況下,有多重實現方式:
(1) 假定組件自身知道自己是如何被使用的,如本案例中,它被放在主窗體的Grid中,那么可以這樣實現:
((this.Parent as Grid).Parent as Window).Close(); |
這種實現方式,屬於hard code,使組件失去了靈活性,不是一個好方法,但它是一個思路,即:只要你能找到你想要的,你就可以做你想做的。 |
(2) 將點擊“關閉”按鈕事件,轉化成一個新的事件,告訴引用這個組件的窗體:有人請求關掉你!這個設計有點麻煩,但使組件具有了組件該有的特性:靈活性,或稱為適應性,以保證你可以相對隨意的使用它。
#region >>>>> 自定義事件 ... public static readonly RoutedEvent PreviewWindowHeaderEvent; public event RoutedEventHandler PreviewWindowHeaderEventHandler { add { this.AddHandler(PreviewWindowHeaderEvent, value); } remove { this.RemoveHandler(PreviewWindowHeaderEvent, value); } } static K3WindowHeader() { PreviewWindowHeaderEvent = EventManager.RegisterRoutedEvent( "PreviewWindowHeaderEventHandler", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(K3WindowHeader)); } #endregion …… void Button_Click(object sender, RoutedEventArgs e) { RoutedEventArgs eventArgs = new RoutedEventArgs(); eventArgs.RoutedEvent = PreviewWindowHeaderEvent; RaiseEvent(eventArgs); } |
這段代碼,加在K3WindowHeader中。 #region #endregion的合理使用,有利於提升代碼的可讀性。你可以加上注釋。BTW,我添加>>>>> … 是為了美觀,讀者可以試試去掉它,或改變它,看看你自己更喜歡哪一個。 注意,我添加了static 類型的構造函數,以便注冊自定義事件。 Event有兩種類型,其關鍵參數是:RoutingStrategy,其取值的不同,將生成兩種傳遞方向不同事件。詳述如下: |
|
public static readonly RoutedEvent PreviewWindowHeaderEvent; public event RoutedEventHandler PreviewWindowHeaderEventHandler { add { this.AddHandler(PreviewWindowHeaderEvent, value); } remove { this.RemoveHandler(PreviewWindowHeaderEvent, value); } } static MainWindow() { PreviewWindowHeaderEvent = K3WindowHeader.PreviewWindowHeaderEvent.AddOwner(typeof(MainWindow)); } void Init() { this.PreviewWindowHeaderEventHandler += new RoutedEventHandler(CustomEventHandler); } void CustomEventHandler(object sender, RoutedEventArgs e) { this.Close(); } |
這段代碼,加在DataTracer中。 注意這段代碼與上段代碼的不同,在於靜態構造中,我們通過事件的AddOwner方法,將這兩個自定義事件關聯起來,從而保證了可以捕獲到該事件。 同樣,我使用了Init來使代碼整潔,並在其中添加了對該特定事件的處理程序。別忘了將Init()放入構造函數中。 |
F5 à 運行,看看結果!