2010年9月3日 星期五

WPF 應用程式,App.xaml 不使用 StartupUri 時無法套用 ResourceDictionary

前言

WPF 實在是很棒的一個 Framework,而且在開發設計時,Visual Studio IDE 的輔助,讓我們的開發更具生產力。但也因為如此,當我們不朝預設的開發方式時,就容易遇到一些「地雷」。今天我就踩到一個。

問題

問題的由來是有演變過程的。以下就一步步的演進吧。

步驟1: 標準的 Style

我按標準步驟產生了一個 WPF Application後,在 MainWindow.xaml  上放了一個 Button。

image

Xaml 如下, 非常簡單

<Window x:Class="WpfApplication2.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Title="MainWindow" Height="350" Width="525">
        <Grid>
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="40,36,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
    </Grid>
</Window>

 

 

步驟2: 套用標準的 Style 到每一個 Button

為了讓整個程式的按鍵看來有一致的外觀,我在 App.xaml 上增加了 Button Style。App.xaml 如下,也非常的 easy

<Application x:Class="WpfApplication2.App"
                         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                         StartupUri="MainWindow.xaml">
        <Application.Resources>
        <ResourceDictionary>
            <Style TargetType="Button">
                <Setter Property="Background" Value="LightBlue" />
            </Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>

而無論在開發階段或執行階段,Button 的樣式果然成我預期的背景顏色LightBlue 淺藍色。

image image

步驟3:移除 StartupUri

因故,我必須移除 App.xaml 中的 StartupUri,並自行建立 MainWindow 的 instance。修改後的 App.xaml如下

<Application x:Class="WpfApplication2.App"
                         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                         > <!--注意到我移除了 StartupUri-->
        <Application.Resources>
        <ResourceDictionary>
            <Style TargetType="Button">
                <Setter Property="Background" Value="LightBlue" />
            </Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>

而 App.xaml.cs 如下

using System.Windows;

namespace WpfApplication2
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var mainWindow = new MainWindow();
            mainWindow.Show();
        }
    }
}

程式可以執行,但問題來了:執行時期,Button 的樣式跑掉了。

image

這是怎麼一回事?開發的Designer 仍然是淺藍色背景--我要的樣式啊!

原因

原因是:開發工具太強了,幫我們做了太多的事。將原來的 StartupUri 加回去,並在 Solution Explorer 下找到 obj\Debug\ 可以找到一堆由 Visual Studio 幫我們產生的程式碼。

image

打開 App.g.i.cs ,可以找到 InitializeComponent 這個方法。可以看到它是如何幫我們自動載入 Resource 的.

image

一旦我們將 StartupUri 移除掉,這段程式就會變化。整個 InitializeComponent 方法就不見了!!

這就是原因所在。

解決

既然找到了原因,當然就知道要怎麼解了。方法有二。

第一個方法是自己補上消失的程式,並且自己將消失的 style 補上。這個方法難度較高,而且不好維護。我採用第二個方法:自訂一個 ResourceDictionary。

新增一個 ResourceDictionary 並命名為 AppResource.xaml, 並將原來在 App.xaml 的 style 移到這裡。

SNAGHTML8e2c6c

AppResource.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="Button">
        <Setter Property="Background" Value="LightBlue" />
    </Style>
</ResourceDictionary>

改用 。修改App.xaml 如下

<Application x:Class="WpfApplication2.App"
                         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                         > <!--注意到我移除了 StartupUri-->
        <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="AppResource.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

然後,執行時期的 Button 樣式就回來了。

image

結論

WPF 實在設計的很棒,而 IDE 的開發輔助也讓我們快速的完成常見的需求。只是在 IDE 快速開發的背後,我們常常忽略了原來應該是我們要做的事。一旦不符合原 IDE 的快速開發條件時,就摸不著頭緒了。

沒有留言:

Share with Facebook