类库(第三方软件插件)场景下,如何将DrawingImage绑定到ItemsControl生成的自定义ToolButtonControl对应属性
兄弟,太懂你这种在第三方插件类库里面搞WPF绑定的憋屈了——常规WPF应用的好多顺手功能用不了,还得硬啃自定义控件的绑定问题。针对你要把DrawingImage绑定到ItemsControl动态生成的ToolButtonControl的需求,我给你捋个落地的解决方案:
第一步:先把你的ToolButtonControl的依赖属性搞对
WPF的绑定认的是依赖属性,普通CLR属性在ItemsControl的模板里根本触发不了更新,这点一定要注意。你得给ToolButtonControl加一个DrawingImage类型的依赖属性,比如叫IconSource,代码这么写:
public class ToolButtonControl : UserControl { public static readonly DependencyProperty IconSourceProperty = DependencyProperty.Register(nameof(IconSource), typeof(DrawingImage), typeof(ToolButtonControl), new PropertyMetadata(null)); public DrawingImage IconSource { get => (DrawingImage)GetValue(IconSourceProperty); set => SetValue(IconSourceProperty, value); } // 剩下的你的自定义控件逻辑... }
第二步:给ItemsControl配好ItemTemplate和分组(如果需要)
你已经用了CollectionViewSource做分组,那接下来要在ItemsControl的ItemTemplate里把ToolButtonControl放进去,直接绑定它的IconSource到你插件数据模型里的DrawingImage属性就行。比如你的插件模型里有个Icon属性存的就是DrawingImage,那XAML这么写:
<ItemsControl ItemsSource="{Binding Source={StaticResource PluginsCollection}}"> <!-- 每个插件项的模板:把ToolButtonControl和数据源绑定 --> <ItemsControl.ItemTemplate> <DataTemplate> <local:ToolButtonControl Width="80" Height="80" IconSource="{Binding Icon}" <!-- 这里绑定到你插件模型的DrawingImage属性 --> ToolTip="{Binding PluginName}" /> </DataTemplate> </ItemsControl.ItemTemplate> <!-- 如果你要分组显示(比如按文件夹分组),配好GroupStyle --> <ItemsControl.GroupStyle> <GroupStyle> <!-- 分组的标题模板 --> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" <!-- 绑定分组的名称,比如文件夹名 --> Margin="5,10,0,0" FontWeight="Bold" FontSize="14" /> </DataTemplate> </GroupStyle.HeaderTemplate> <!-- 分组内的控件排版,比如用WrapPanel自动换行 --> <GroupStyle.Panel> <ItemsPanelTemplate> <WrapPanel Margin="5" /> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </ItemsControl.GroupStyle> </ItemsControl>
第三步:类库插件场景下的特殊坑要避开
因为你是做第三方软件的插件,不是独立WPF应用,有几个坑必须踩对:
- 资源加载别用Application.Current:宿主软件的Application资源和你的类库资源不互通,要在你的Page或者Grid的局部资源里合并资源字典。比如你的DrawingImage存在类库的
Resources/Icons.xaml里,那在Page的Resources里加:
<Page.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/你的类库程序集名称;component/Resources/Icons.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Page.Resources>
- CollectionViewSource的分组要明确:你用的
PluginsCollection要指定分组依据,比如按插件的GroupName(对应文件夹名)分组,那CollectionViewSource的XAML要补全:
<CollectionViewSource x:Key="PluginsCollection" Source="{Binding YourPluginList}"> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="GroupName" /> </CollectionViewSource.GroupDescriptions> </CollectionViewSource>
- 别依赖全局静态资源:尽量把所有需要的资源(比如你的MainBackgroundBrush)都放到Page或者Grid的Resources里,避免和宿主软件的资源冲突。
最后给你个调试小技巧(插件场景下特别有用)
插件环境下很难弹错误提示,你可以打开Visual Studio的输出窗口,把筛选器改成Debug,然后找System.Windows.Data开头的日志——如果绑定失败,这里会明确告诉你是找不到属性,还是资源加载失败,比瞎猜靠谱多了。另外也可以给Binding加个FallbackValue,比如IconSource="{Binding Icon, FallbackValue={StaticResource DefaultIcon}}",这样绑定失败时会显示默认图标,能快速定位问题。
备注:内容来源于stack exchange,提问作者Boris

