在開發 WPF 類庫時,由於類庫里面沒有存在 App.xaml.cs 文件,而在對單個 XAML 進行開發時,設計器將會因為找不到資源文件的存在,而拿不到資源。本文告訴大家簡單的方法,給設計器設置僅在設計時引用的資源
在 WPF 的 XAML 中,如果對每個 XAML 控件都引用相同的資源,此時設計時將可以愉快的跑起來,然而在運行時將會重復創建資源影響性能。在開發 WPF 應用時,在入口項目里面,因為入口處有 App.xaml 文件,在這個文件里面加上了各個項目的引用,此時設計器就能知道當前項目引用的 XAML 資源字典,因此設計器就能工作
但是在開發類庫的時候,類庫不知道最終的入口項目是哪個,因此也就不知道當前程序在運行的時候,將會找不到引用
最佳的方法是和 Blend 一樣,在設計時讓設計器引用上某些資源,這樣設計器就能工作
實現方法是在類庫里面添加特殊的文件,這個特殊的文件有文件夾和命名的要求,這是在 VisualStudio 的設計器里面寫常量固定的路徑
在項目里面新建 Properties
文件夾,在 Properties
文件夾里面新建 DesignTimeResources.xaml 資源字典文件,大概如下
這個文件的命名規則是有約定的,不推薦自己修改。在 csproj 上添加如下代碼
<ItemGroup>
<Page Update="Properties\DesignTimeResources.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<ContainsDesignTimeResources>true</ContainsDesignTimeResources>
</Page>
</ItemGroup>
上面代碼核心就是 ContainsDesignTimeResources 這個屬性。理論上可以給任意的 xaml 文件設置這個屬性,但是 XAML 設計器在很多 VS 版本上只讀取此路徑的文件
在 DesignTimeResources.xaml 資源字典添加對其他資源字典的引用,即可實現讓類庫的設計器找到資源,而在運行時是不會加載資源到內存
例如我新建了類庫項目 JeenalerenenearWerjilakaw 項目。我在 JeenalerenenearWerjilakaw 項目里面添加了資源字典 ColorBrushResourcesDictionary.xaml 資源字典,在里面存放顏色畫刷,代碼如下
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:JeenalerenenearWerjilakaw">
<SolidColorBrush x:Key="Brush.ColorBrush.ThemeColorBrush">#FF565656</SolidColorBrush>
</ResourceDictionary>
而我期望在 JeenalerenenearWerjilakaw 項目的自定義控件 UserControl1.xaml 上使用這個 Brush.ColorBrush.ThemeColorBrush 資源,如下面代碼
<Grid>
<Border Background="{StaticResource Brush.ColorBrush.ThemeColorBrush}" Margin="10,10,10,10"></Border>
</Grid>
此時的設計器和代碼都不能工作,將會在設計器提示找不到資源
接下來新建 Properties\DesignTimeResources.xaml
資源字典文件,在這個資源字典文件里面添加如下代碼
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../ColorBrushResourcesDictionary.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
在 JeenalerenenearWerjilakaw 的 csproj 添加如下代碼
<ItemGroup>
<Page Update="Properties\DesignTimeResources.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<ContainsDesignTimeResources>true</ContainsDesignTimeResources>
</Page>
</ItemGroup>
當前的 csproj 的所有代碼看起來如下
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<Page Update="Properties\DesignTimeResources.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<ContainsDesignTimeResources>true</ContainsDesignTimeResources>
</Page>
</ItemGroup>
</Project>
以上代碼是用在 SDK 風格的 csproj 文件上,如果當前項目文件非 sdk 風格,請參閱 從以前的項目格式遷移到 VS2017 新項目格式
接下來保存代碼,然后關閉 VisualStudio 清理緩存文件,打開 VisualStudio 可以看到,當前設計器和代碼都能工作
WPF Design Time Support (Part 2) · jbe2277/waf Wiki
然而在有 Resharper 的 VisualStudio 上,這個配置在 Resharper 2020 的一些版本是不認識的,此時將會讓 Resharper 找不到資源引用,而沒有自動跳轉和補全的功能。好在 Resharper 對於項目格式的支持不夠好,咱可以使用黑科技來解決此問題。在 Resharper 的 2021 版本也修了這個問題,因此在新版本的 Resharper 是不需要加以下代碼
在 Resharper 中,將會根據 ApplicationDefinition 定義去讀取應用資源文件,但是在讀取時將會忽略 Condition 內容。於是咱就可以嘗試創建一個叫 App_MakeReshaperHappy.xaml
的文件,用來讓 Resharper 開森。在 csproj 中添加如下代碼,用來引用應用資源,但實際上又不會用上此資源
<ApplicationDefinition Include="App_MakeReshaperHappy.xaml" Condition="false">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
上面代碼中,使用了 Condition 為 false 讓構建時實際忽略了此文件,否則將會在構建時報告不能在 WPF 類庫中定義 ApplicationDefinition 元素。為什么不能叫 App.xaml 文件?原因是在 SDK 風格的 csproj 中,默認將會加上 EnableDefaultApplicationDefinition 自動加上默認的 ApplicationDefinition 元素。只要有文件叫 App.xaml 的,那么將會自動識別為 ApplicationDefinition 元素,此元素將會讓 WPF 構建時報告不能在類庫中定義。而如果設置 EnableDefaultApplicationDefinition 為 false 那 Resharper 又會不認此文件,經過了 lsj 工具人的摸索,發現使用上文方法是最穩的
在 App_MakeReshaperHappy.xaml
文件里面存放以下代碼,用來讓 Resharper 智能感知能在設計時找到資源
<Application x:Class="App_MakeReshaperHappy" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--這個文件只是為了讓Reshaper的智能感知開心-->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--以下替換為你自己的路徑,我推薦引用的是設計時資源文件-->
<ResourceDictionary Source="Properties/DesignTimeResources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
以上代碼必須要自己定義類型,然后繼承 Application 同時有 x:Class
內容才可以。這個文件的文件名和類名要求一定包含 App 這三個字符,同時文件名和類名需要相同