我写了一个特殊的控件,我期望了解到有哪些 VisualBrush 捕获了此控件,或者说有哪些 VisualBrush 用了此控件的界面

本文的方法需要用到反射,需要使用 WPF 框架里面没有公开的字段获取某个 Visual 控件被引用的 VisualBrush 有哪些,代码如下

    class MyUserControl : UserControl
{
public bool IsInVisualBrush()
{
return GetVisualBrushes().Any();
} private List<VisualBrush> GetVisualBrushes()
{
var type = typeof(Visual);
var cyclicBrushToChannelsMapField = type.GetField("CyclicBrushToChannelsMapField", BindingFlags.Static | BindingFlags.NonPublic);
var cyclicBrushToChannelsMap = cyclicBrushToChannelsMapField.GetValue(null); var getValueMethod = cyclicBrushToChannelsMap.GetType().GetMethod("GetValue");
var cyclicBrushToChannelsMapDictionary = getValueMethod.Invoke(cyclicBrushToChannelsMap, new object[] { this });
var dictionary = cyclicBrushToChannelsMapDictionary as IDictionary; var visualBrushes = dictionary?.Keys.OfType<VisualBrush>().ToList() ?? new List<VisualBrush>(0);
return visualBrushes;
}
}

通过上面代码不仅可以获取某个控件,是否被作为 VisualBrush 的 Visual 作为画刷,还可以获取当前有哪些 VisualBrush 捕获了这个控件

写一个简单的界面,将这个控件设置为某个 VisualBrush 的 Visual 内容,然后将这个 VisualBrush 作为背景

  <Grid x:Name="Grid">
<Border x:Name="Border">
<Border.Background>
<VisualBrush Visual="{Binding ElementName=Container}"></VisualBrush>
</Border.Background>
</Border> <Grid x:Name="Container">
<local:MyUserControl x:Name="MyUserControl"></local:MyUserControl>
</Grid>
</Grid>

在界面的构造里面,在 InitializeComponent 方法之后,调用 IsInVisualBrush 方法,返回的是不被 VisualBrush 捕获。但是如果在 Loaded 事件获取,返回的是没有被捕获。只有在 Loaded 事件加上 Dispatcher 延迟返回的才是被捕获

        public MainWindow()
{
InitializeComponent(); Loaded += MainWindow_Loaded;
} private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Dispatcher.InvokeAsync(() =>
{
MyUserControl.IsInVisualBrush();
});
}

而如果在点击按钮的时候,将使用了 VisualBrush 作为背景的 Border 移除,那么立刻就可以获取到没有被捕获

        private void Button_OnClick(object sender, RoutedEventArgs e)
{
Grid.Children.Remove(Border); MyUserControl.IsInVisualBrush(); // 返回 false 没有被捕获
}

上面代码其实用到了 WPF 的机制,在 WPF 里面,所有的控件都继承了 Visual 类型(无视3D部分)而在此类型里面,将会在被 VisualBrush 使用的时候,调用 AddRefOnChannelForCyclicBrush 方法

        internal virtual void AddRefOnChannelForCyclicBrush(
ICyclicBrush cyclicBrush,
DUCE.Channel channel)
{
// 忽略代码 Dictionary<ICyclicBrush, int> cyclicBrushToChannelsMap =
CyclicBrushToChannelsMapField.GetValue(this); if (cyclicBrushToChannelsMap == null)
{
cyclicBrushToChannelsMap = new Dictionary<ICyclicBrush, int>();
CyclicBrushToChannelsMapField.SetValue(this, cyclicBrushToChannelsMap);
} if (!cyclicBrushToChannelsMap.ContainsKey(cyclicBrush))
{
cyclicBrushToChannelsMap[cyclicBrush] = 1;
}
else
{
Debug.Assert(cyclicBrushToChannelsMap[cyclicBrush] > 0); cyclicBrushToChannelsMap[cyclicBrush] += 1;
} //
// Render the brush's visual.
// cyclicBrush.RenderForCyclicBrush(channel, false);
}

上面的 ICyclicBrush 定义如下

    internal interface ICyclicBrush
{
void FireOnChanged(); void RenderForCyclicBrush(DUCE.Channel channel, bool skipChannelCheck);
}

所有 VisualBrush 继承了这个接口

  public sealed partial class VisualBrush : TileBrush, ICyclicBrush
{ }

在设置 VisualBrush 的 Visual 属性的时候,将会触发 VisualPropertyChanged 函数

        static VisualBrush()
{
// Initializations
Type typeofThis = typeof(VisualBrush);
VisualProperty =
RegisterProperty("Visual",
typeof(Visual),
typeofThis,
null,
new PropertyChangedCallback(VisualPropertyChanged),
null,
/* isIndependentlyAnimated = */ false,
/* coerceValueCallback */ null);
} private static void VisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ }

在这个函数里面将会调用 VisualBrush 的 AddRefResource 方法

    public sealed partial class VisualBrush : TileBrush, ICyclicBrush
{
private static void VisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// 忽略代码
System.Windows.Threading.Dispatcher dispatcher = target.Dispatcher; if (dispatcher != null)
{
DUCE.IResource targetResource = (DUCE.IResource)target;
using (CompositionEngineLock.Acquire())
{
int channelCount = targetResource.GetChannelCount(); for (int channelIndex = 0; channelIndex < channelCount; channelIndex++)
{
DUCE.Channel channel = targetResource.GetChannel(channelIndex);
Debug.Assert(!channel.IsOutOfBandChannel);
Debug.Assert(!targetResource.GetHandle(channel).IsNull);
target.ReleaseResource(oldV,channel);
target.AddRefResource(newV,channel);
}
}
} target.PropertyChanged(VisualProperty);
}
}

在 AddRefResource 函数里面将会调用上文的具体的 Visual 的 AddRefOnChannelForCyclicBrush 方法

    public sealed partial class VisualBrush : TileBrush, ICyclicBrush
{
internal void AddRefResource(Visual visual, DUCE.Channel channel)
{
if (visual != null)
{
visual.AddRefOnChannelForCyclicBrush(this, channel);
}
}
}

因此在 Visual 里面是可以了解到当前的的对象被哪些 VisualBrush 捕获

而在 Visual 里面存放的字典是不开放的,需要使用本文的反射的方式才能拿到对象从而了解这个控件是否作为 VisualBrush 的内容

本文所有代码放在 githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 04a3f64cc878d8e4890e72877ff08e473b4fc1a8

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 CalbuhewaNallrolayrani 文件夹

参考:c# - How to know whether my control be used in VisualBrush - Stack Overflow

WPF 如何获取有哪些 VisualBrush 用了某个控件的更多相关文章

  1. 《Programming WPF》翻译 第3章 3.内嵌控件

    原文:<Programming WPF>翻译 第3章 3.内嵌控件 WPF提供了一系列内嵌控件.其中大多数符合标准的你已经熟悉的Windows控件类型.注意到没有一个是包装在旧的Win32 ...

  2. Atitit  项目界面h5化静态html化计划---vue.js 把ajax获取到的数据 绑定到表格控件 v2 r33.docx

    Atitit  项目界面h5化静态html化计划---vue.js 把ajax获取到的数据 绑定到表格控件 v2 r33.docx 1. 场景:应用在项目列表查询场景下1 1.1. 预计初步掌握vue ...

  3. WPF 从程序集中检索图片资源stream给Image控件使用

    原文:WPF 从程序集中检索图片资源stream给Image控件使用 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/nihang1234/artic ...

  4. WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?

    原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? 在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标 ...

  5. 《Programming WPF》翻译 第3章 1.什么是控件

    原文:<Programming WPF>翻译 第3章 1.什么是控件 对于一个应用程序而言,控件是搭建用户界面的积木.它们具备交互式的特征,例如文本框.按钮以及列表框.尽管如此,WPF还有 ...

  6. WPF编程,通过【帧】动态更改控件属性的一种方法。

    原文:WPF编程,通过[帧]动态更改控件属性的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/detail ...

  7. 精通 WPF UI Virtualization (提升 OEA 框架中 TreeGrid 控件的性能)

    原文:精通 WPF UI Virtualization (提升 OEA 框架中 TreeGrid 控件的性能) 本篇博客主要说明如何使用 UI Virtualization(以下简称为 UIV) 来提 ...

  8. WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法

    原文:WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法 问题描述 今天发现如果我想在一个TextBlock弄一个右键菜单,并且使用Command绑定,结果发 ...

  9. WPF通过不透明蒙板切割显示子控件

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/Backspace110/article/ ...

  10. WPF 模仿 UltraEdit 文件查看器系列一 用户控件

    WPF 模仿 UltraEdit 文件查看器系列一 用户控件 运行环境:Win10 x64, NetFrameWork 4.8, 作者:乌龙哈里,日期:2019-05-10 章节: 起步 添加用户控件 ...

随机推荐

  1. 记录--for in 和 for of的区别详解以及为for in的输出顺序

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 for in 和 for of 相对于大家肯定都不陌生,都是用来遍历属性的没错.那么先看下面的一个例子: 例1 const obj = { ...

  2. flink scala 从Oracle同步数据到MySql

    pom <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> ...

  3. 修改debian apt搜索的软件包颜色(原本是绿色)

    sudo nano /etc/apt/apt.conf 加入以下内容 apt::color::highlight "#"; 再搜索软件包会变成白色 不足之处是包的前面会加上#号

  4. 【已解决】git reset命令误删本地文件怎么恢复

    执行 git  reflog 命令可以看到曾经执行过的操作,还有版本序号. 执行 git reset --hard HEAD@{[填那个序号]} 就可以恢复本地删除的文件了!

  5. 5W1H聊开源之Who和How——谁、如何参与开源?

    上次Who的主体是谁"发明"了开源,这一次主体转换,来看看开源发明之后,还有哪些人为开源做贡献?作为普通程序员的我们,又能以怎样的形式参与到开源项目中? 很多人都以为参与开源是一件 ...

  6. 【WCH以太网接口系列芯片】STM32+CH390+Lwip协议栈简单应用测试

    本篇文章基于STM32F103和CH390H芯片进行例程移植及相关注意事项,简单验证TCP\UDP\Ping基础功能. 硬件:STM32F103开发板+沁恒CH390H的评估版图一示,SPI使用接口为 ...

  7. Python企业面试题2 —— 基础篇

    1. re 的match和search区别? re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none. re.search 扫描整个字符串并返 ...

  8. 双端队列的基本实现【数据结构与算法—TypeScript 实现】

    笔记整理自 coderwhy 『TypeScript 高阶数据结构与算法』课程 特性 本质:允许队列在两端进行 入队 和 出队 操作 设计 实现方式:基于 数组 实现 属性: data:存放队列元素 ...

  9. 基于Canvas实现的简历编辑器

    基于Canvas实现的简历编辑器 大概一个月前,我发现社区老是给我推荐Canvas相关的内容,比如很多 小游戏.流程图编辑器.图片编辑器 等等各种各样的项目,不知道是不是因为我某一天点击了相关内容触发 ...

  10. szfpga Lattice高速下载器HW-USBN-2B 常见问题解答

    .产品特点 1). 支持windows7,Windows10 操作系统,两个操作系统非常稳定不断线. 2). 支持JTAG 模式,速度快,最高30Mb/s,调试serdes core,不会像hw-us ...