无需复制即可复用XAML组件及其.cs代码的实现方法
WPF 自定义XAML组件复用最优方案
认知纠正
你对ResourceDictionary的理解没有错误,ResourceDictionary的定位是存储共享XAML资源(样式、控件模板、转换器、画刷等),本身不支持关联带业务逻辑的后台类,所以无法直接替代独立自定义Page/UserControl的复用效果。
方案1:实现自定义加载控件,匹配你要求的调用语法
WPF原生没有提供你示例中的<ExternalCustomControl>语法,你需要自行实现这个动态加载控件来支持松散文件调用:
- 先实现继承自
ContentControl的ExternalCustomControl类,添加Source(XAML文件路径)、Implementation(后台CS文件路径)两个依赖属性 - 在属性变更回调中完成两个操作:
- 用
XamlReader.Load()方法加载指定路径的XAML文件,得到控件实例 - 用C#动态编译API编译指定的CS文件,创建后台逻辑类实例,将XAML控件的
DataContext绑定到后台实例,再把XAML控件赋值给ExternalCustomControl的Content属性
代码示例:
- 用
// 简化版实现逻辑 public class ExternalCustomControl : ContentControl { public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(ExternalCustomControl), new PropertyMetadata(OnPathChanged)); public static readonly DependencyProperty ImplementationProperty = DependencyProperty.Register("Implementation", typeof(string), typeof(ExternalCustomControl), new PropertyMetadata(OnPathChanged)); public string Source { get => (string)GetValue(SourceProperty); set => SetValue(SourceProperty, value); } public string Implementation { get => (string)GetValue(ImplementationProperty); set => SetValue(ImplementationProperty, value); } private static void OnPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = d as ExternalCustomControl; if (string.IsNullOrEmpty(control.Source) || string.IsNullOrEmpty(control.Implementation)) return; // 加载XAML using var fs = new FileStream(control.Source, FileMode.Open); var xamlInstance = XamlReader.Load(fs) as FrameworkElement; // 动态编译CS、创建实例、绑定逻辑(这里省略动态编译的具体代码) var viewModel = CompileAndGetInstance(control.Implementation); xamlInstance.DataContext = viewModel; control.Content = xamlInstance; } }
注意事项:
- 该方案属于运行时动态加载,没有编译时检查,语法、路径错误只会在运行时抛出
- 路径建议用绝对路径,或者相对于当前项目输出目录的相对路径,避免找不到文件
方案2:更推荐的工程化复用方案,完全避免命名空间/引用报错
如果可以接受不用自定义松散加载语法,更建议用以下两种原生支持的方案,不需要复制任何文件:
子方案2.1 WPF类库打包复用
- 将自定义Page/UserControl的所有XAML、CS文件放到单独的WPF类库项目中,编译生成dll
- 目标解决方案添加对该类库的项目引用(或直接引用编译好的dll)
- 在目标项目的XAML头部引入命名空间即可直接调用:
<!-- 头部引入命名空间 --> <Window x:Class="YourProject.MainWindow" xmlns:custom="clr-namespace:YourCustomControlNamespace;assembly=YourCustomControlLibraryName"> <!-- 直接调用自定义组件 --> <custom:YourCustomPage /> </Window>
优势:
- 完整编译时检查,命名空间、属性错误在编译阶段就能发现,不会运行时报错
- 组件更新后所有引用的项目自动同步,不需要手动修改文件
- 支持完整调试能力,断点可以直接打到组件的后台代码中
子方案2.2 添加为链接文件
如果不想单独维护类库项目,可以用添加链接的方式直接引用外部文件:
- 目标项目右键 -> 添加 -> 现有项
- 找到自定义组件对应的XAML、CS文件,点击「添加」按钮旁边的下拉箭头,选择「添加为链接」
- 编译时会将链接文件当做项目本地文件处理,不会复制文件到目标项目目录,只要保证组件的命名空间在目标项目中可访问,就不会出现引用报错
内容的提问来源于stack exchange,提问作者Ivan Silkin
相关产品推荐
相关产品推荐

