上一篇水文中,老周說了一下純代碼編寫 WPF 的大概過程。不過,還是不夠的,本篇水文中咱們還要更進一步。 XAML 文件預設是作為資源打包進程式中的,而純代碼編寫又導致一些常改動的東西變成硬編碼了。為了取得二者平衡,咱們還要把一些經常修改的東西放到 XAML 文件中,不過 XAML 文件不編譯進程式 ...
上一篇水文中,老周說了一下純代碼編寫 WPF 的大概過程。不過,還是不夠的,本篇水文中咱們還要更進一步。
XAML 文件預設是作為資源打包進程式中的,而純代碼編寫又導致一些常改動的東西變成硬編碼了。為了取得二者平衡,咱們還要把一些經常修改的東西放到 XAML 文件中,不過 XAML 文件不編譯進程式里,而是放到外部,運行階段載入。比如一些對象屬性、畫刷、樣式、字體之類的,直接改文件保存就行,修改之後不用重新編譯項目。
要在運行階段載入 XAML,咱們只需認識一個類就OK—— XamlReader,調用它的 Load 方法就能從 XAML 文件載入對象了。
下麵老周就邊演示邊嘮叨一下相關的問題。
一、新建項目。可以參照上一篇中的做法,用控制台應用程式項目,然後修改項目文件。也可以直接建 WPF 項目。都可以。
二、自定義視窗類,從 Window 派生。當然,你直接用 Window 類也可以的。
public class MyWindow : Window { const string XAML_FILE = "MyWindow.xaml"; public MyWindow() { Title = "載入外部XAML"; Height = 150; Width = 225; // 從XAML文件載入 using FileStream fsIn = new(XAML_FILE, FileMode.Open, FileAccess.Read); FrameworkElement layout = (FrameworkElement)XamlReader.Load(fsIn); // 兩個按鈕要處理事件 Button btn1 = (Button)layout.FindName("btn1"); Button btn2 = (Button)layout.FindName("btn2"); btn1.Click += OnClick1; btn2.Click += OnClick2; Content = layout; } private void OnClick2(object sender, RoutedEventArgs e) { MessageBox.Show("第二個按鈕"); } private void OnClick1(object sender, RoutedEventArgs e) { MessageBox.Show("第一個按鈕"); } }
這個不複雜,咱們關註載入 XAML 部分。通過文件流 FileStream 讀取文件,而後在 XamlReader.Load 中載入。Load 方法返回的是 object 類型的對象,咱們要適當地進行類型轉換。這個例子裡面其實載入上來的是 Grid 類,但這裡我只轉換為 FrameworkElement 就可以了,畢竟我後面只用到了 FindName 方法。Find 出來的是兩個 Button 對象,最後處理一下 Click 事件。
三、在項目中添加 MyWindow.xaml 文件。
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Margin="12"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Button Name="btn1" Grid.Row="0" Margin="5,8">按鈕A</Button> <Button Name="btn2" Grid.Row="1" Margin="5,8">按鈕B</Button> </Grid>
這裡順便說一下,保存 XAML 文件時最好用 UTF-8 編碼,不然可能會報錯。方法是在 VS 里,【文件】-【XXX 另存為】。在保存文件對話框中,點“保存”按鈕右邊的箭頭,選擇“編碼保存”。
編碼選 UTF-8 無簽名(或帶簽名的也行)。
另一種方法是用記事本打開,再以 UTF-8 保存。
四、在項目中添加 styles.xaml。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <Style TargetType="Button"> <Setter Property="Background" Value="Red"/> <Setter Property="Foreground" Value="White"/> <Setter Property="Padding" Value="5"/> <Setter Property="FontFamily" Value="楷體"/> <Setter Property="FontSize" Value="17"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="BorderBrush" Value="Yellow"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5"> <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Green"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" Value="DarkSlateGray"/> </Trigger> </Style.Triggers> </Style> </ResourceDictionary>
裡面包含一個 Button 控制項模板。
五、在 Main 方法中,初始化 Application 類,並且從外部 XAML 中載入資源字典。
[STAThread] static void Main(string[] args) { Application app = new(); using FileStream extFile = new FileStream("styles.xaml", FileMode.Open, FileAccess.Read); ResourceDictionary dic = (ResourceDictionary)XamlReader.Load(extFile); app.Resources = dic; app.Run(new MyWindow()); }
由於是在 app 處載入的資源,所以按鈕樣式會應用到整個程式。
六、打開項目文件(*.csproj),我們要做點手腳。
<ItemGroup> <Page Remove="*.xaml"/> <None Include="*.xaml"/> </ItemGroup> <Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Exec Command="copy /y *.xaml $(OutDir)" /> </Target>
老周解釋一下加了顏色的部分。
1、Page 表示XAML文件最終生成二進位文件,且塞進目標程式集中。加了 Remove 表示排除這一行為。說人話就是:本項目不編譯 XAML 文件。
2、None 表示該項目中 XAML 文件“啥也不是”,編譯時不做任何處理。
3、PostBuild 任務指定一條命令,在生成項目之後執行。此處是把項目目錄下的 XAML 文件複製到輸出目錄。$(OutDir) 在 VS 中表示巨集(也是 MSBuild 的屬性)。在命令實際執行時,替換為實際目錄路徑,如 bin\Debug\net7.0-windows。
也可以用 $(TargetDir),不過 TargetDir 替換的是完整路徑,OutDir 是用相對路徑的。
現在生成一下項目,若沒有問題,在輸出目錄下除了程式文件,還有那兩個 XAML 文件。運行一下。
關閉程式,用記事本打開 styles.xaml 文件,把按鈕的背景色改成橙色。
保存並關閉文件,重新運行程式。
咱們並沒有重新編譯程式。接下來用記事本打開 MyWindow.xaml 文件,改一下按鈕上的文本。
保存並關閉文件,不用編譯代碼,再次運行程式。
這樣就很方便修改了,不必每次都重新編譯。
下一篇老周還會說說純代碼寫 WPF 的模板問題。三維圖形就看心情了。因為 3D 圖形的構造和一般控制項應用差不多,就是用代碼建立 WPF 對象樹。