[WPF疑难] 继承自定义窗口
原文 [WPF疑難] 繼承自定義窗口
[WPF疑難] 繼承自定義窗口
周銀輝
項目中有不少的彈出窗口,按照美工的設(shè)計其外邊框(包括最大化,最小化,關(guān)閉等按鈕)自然不同于Window自身的,但每個彈出框的外邊框都是一樣的。對其中一個窗口而言,我們要取消其Window邊框,并在右上角擺上三個按鈕并編寫其點擊事件等,但若每個彈出窗口都按照這種方式做一遍就太土了。我們想避免重復(fù)勞動,最自然的聯(lián)想到了“繼承”。但WPF給我們找了若干麻煩,被挫敗了幾次。今天經(jīng)過2小時的奮戰(zhàn),終于搞定了,分享一下。
挫敗1,繼承時編譯錯誤
假設(shè)我們寫好的父窗口類為BaseWindow,對應(yīng)BaseWindow.cs和BaseWindow.xaml, 要繼承它的窗口為Window1,對應(yīng)Window1.cs和Window1.xaml,我們常常進行的動作是將VS為我們自動生成的代碼中的如下語句:
public partial class Window1 : Window
修改成:
public partial class Window1 : BaseWindow
但編譯后,你會得到一個錯誤:Window1有著不同的基類。
這是因為在window1.xaml中
<Window
?? xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
?? xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
?? x:Class="InheritWindowDemo.Window1"
?? Width="300" Height="300">
?? <Grid x:Name="LayoutRoot"/>
</Window>
我們的Window繼承了Window類,打開Window1.g.cs也可以看到這一點(這是VS自動生成的一個中間文件,可以在Window1的InitializeComponent()方法上“轉(zhuǎn)到定義”來跳轉(zhuǎn)到該文件,也可以在Obj"Debug目錄下找到)。這就使得我們的Window1同時繼承Window和BaseWindow類,多繼承是不被允許的。
那么自然地,需要修改Window1.xaml,將其中的根“Window”,修改成我們的BaseWindow:
<src:BaseWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
????????????? xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
?????????????? ?x:Class="InheritWindowDemo.Window1"
?????????????? ?xmlns:src="clr-namespace:InheritWindowDemo"
??????????? ????Height="300"
?????????????? ?Width="300">
??? <Grid>
??? </Grid>
</src:BaseWindow>
心想,這下可以編譯通過了吧,抱歉,不行,又得到另一個編譯錯誤:src:BaseWindow不能是Xaml文件的根,因為它是由Xaml定義的,目前我避免這個問題的辦法是讓BaseWindow僅僅在C#中定義(即,沒有BaseWindow.xaml,只有BaseWindow.cs)。
OK,編譯順利通過,繼承成功。
挫敗2,外邊框(包括最小化,最大化和關(guān)閉按鈕)放在哪里
明顯,不能作為BaseWindow的內(nèi)容,這是因為繼承了BaseWindow的子類窗口(比如Window1)會覆蓋BaseWindow的內(nèi)容。
假設(shè)BaseWindow這樣編寫:
??????? public BaseWindow()
??????? {
??????????? Grid grid = new Grid();
??????????? Button minBtn = new Button();
??????????? Button maxBtn = new Button();
??????????? Button closeBtn =new Button();
??????????? //something to ini these buttons
??????????? grid.Children.Add(minBtn);
??????????? grid.Children.Add(maxBtn);
??????????? grid.Children.Add(closeBtn);
??????????? this.Content = grid;
??????? }
當(dāng)子類Window1如下定義時:
<src:BaseWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
?????????????? ?xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
?????????????? ?x:Class="InheritWindowDemo.Window1"
?????????????? ?xmlns:src="clr-namespace:InheritWindowDemo"
?????????????? ?Height="300"
?????????????? ?Width="300">
??? <Grid>
??????? <TextBlock Text="hi , i am window1"/>
??? </Grid>
</src:BaseWindow>
這樣以來Window1中的Grid和TextBlock會覆蓋BaseWindow的內(nèi)容而僅僅看到“hi,I am window1”的文本塊而沒有最小化最大化以及關(guān)閉按鈕了。
事實上,我們應(yīng)該反過來想,Window也是一個控件,與其他控件一樣其外觀及其外觀中的視覺元素仍然是由其Style和ControlTemplate來定義的。想到這里,一切就變得簡單了,我們應(yīng)該將窗口外邊框(包括最小化,最大化和關(guān)閉按鈕)定義在其Template中,其他一些屬性(比如是否支持透明等)定義在Style中
其Template如下:
??? <ControlTemplate x:Key="BaseWindowControlTemplate" TargetType="{x:Type Window}">
??????? <DockPanel LastChildFill="True">
??????????? <!--外邊框-->
??????????? <Border Width="Auto"
?????????????????? ?Height="Auto"
?????????????????? ?DockPanel.Dock="Top"
?????????????????? ?Background="#FF7097D0"
?????????????????? ?CornerRadius="4,4,0,0"
??????????????? ????x:Name="borderTitle">
??????????????? <StackPanel HorizontalAlignment="Right"
?????????????????????????? ?Orientation="Horizontal">
??????????????????? <!--最小化按鈕-->
??????????????????? <Button Content="Min"
?????????????????????????? ?x:Name="btnMin" />
??????????????????? <!--最大化按鈕-->
??????????????????? <Button Content="Max"
?????????????????????????? ?x:Name="btnMax" />
??????????????????? <!--關(guān)閉按鈕-->
??????????????????? <Button Content="Close"
?????????????????????????? ?x:Name="btnClose" />
??????????????? </StackPanel>
??????????? </Border>
??????????? <Border Background="{TemplateBinding Background}"
?????????????????? ?BorderBrush="{TemplateBinding BorderBrush}"
?????????????????? ?BorderThickness="{TemplateBinding BorderThickness}"
?????????????????? ?Width="Auto"
?????????????????? ?Height="Auto"
?????????????????? ?DockPanel.Dock="Top"
?????????????????? ?CornerRadius="0,0,4,4">
??????????????? <AdornerDecorator>
??????????????????? <ContentPresenter />
??????????????? </AdornerDecorator>
??????????? </Border>
??????? </DockPanel>
</ControlTemplate>
其Style如下:
??? <Style x:Key="BaseWindowStyle"
????????? ?TargetType="{x:Type Window}">
??????? <Setter Property="Template" Value="{StaticResource BaseWindowControlTemplate}"/>
???????????
??????? <Setter Property="AllowsTransparency"
?????????????? ?Value="True" />
??????? <Setter Property="WindowStyle"
?????????????? ?Value="None" />
??????? <Setter Property="BorderBrush"
?????????????? ?Value="#FF7097D0" />
??????? <Setter Property="BorderThickness"
?????????????? ?Value="4,0,4,4" />
??????? <!—Something else-->
??? </Style>
然后在BaseWindow的構(gòu)造函數(shù)中指定其Style為我們定義的樣式:
??????? private void InitializeStyle()
??????? {
??????????? this.Style = (Style) App.Current.Resources["BaseWindowStyle"];
??????? }
這樣一來,所有繼承了BaseWindow的窗體,都有我們統(tǒng)一定義的外觀了。
挫敗3,讓外邊框(包括最小化,最大化和關(guān)閉按鈕)響應(yīng)事件
只有外觀還不夠,至少得有鼠標事件吧。那最小化事件來說,要做的事情是找到定義在ControlTemplate中的btnMin這個Button控件,然后當(dāng)其被點擊時該ControlTemplate被應(yīng)用到的那個窗體被最小化。
FrameworkTemplate.FindName(string name, FrameworkElement templatedParent)方法可以做幫助我們找到指定的FrameworkTemplate被應(yīng)用到templatedParent上后具有name名稱的控件。
??????????? ControlTemplate baseWindowTemplate = (ControlTemplate)App.Current.Resources["BaseWindowControlTemplate"];
??????????? Button minBtn = (Button)baseWindowTemplate.FindName("btnMin", this);
??????????? minBtn.Click += delegate
??????????? {
??????????????? this.WindowState = WindowState.Minimized;
??????????? };
其他事件同理:)不過值得提醒的是,上面這樣的代碼應(yīng)該在窗體的Style和Template被應(yīng)用之后,比如你可以在Loaded后編寫使用上面的代碼而不是直接放在構(gòu)造方法中,否則FrameworkTemplate.FindName()方法將返回null。
至此,問題搞定。下載DEMO:http://files.cnblogs.com/zhouyinhui/InheritWindowDemo.zip
posted on 2013-11-26 19:57 NET未來之路 閱讀(...) 評論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/3444011.html
總結(jié)
以上是生活随笔為你收集整理的[WPF疑难] 继承自定义窗口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java keyadapter_在jav
- 下一篇: 【MySQL建表语句转PostgreSQ