我写了一个特殊的控件,我期望了解到有哪些 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. vector的Erase相关

    vector<int>Vect; Vect.insert(Vect.begin()+2, 50); for (auto it=Vect.begin();it!=Vect.end();++i ...

  2. 《Go程序设计语言》学习笔记之defer

    <Go程序设计语言>学习笔记之defer 一. 环境 Centos8.5, go1.17.5 linux/amd64 二. 概念 语法上,一个 defer 语句就是一个普通的函数或方法调用 ...

  3. 一文讲透Java核心技术之高可扩展利器SPI

    大家好,我是冰河~~ SPI的概念 JAVA SPI = 基于接口的编程+策略模式+配置文件 的动态加载机制 SPI的使用场景 Java是一种面向对象语言,虽然Java8开始支持函数式编程和Strea ...

  4. Java SM2

    pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http:// ...

  5. C# 使用AForge调用摄像头

    AForge官网地址:http://www.aforgenet.com/framework/ using System; using System.Collections.Generic; using ...

  6. QT数据库学习笔记

    简介 QT通过模块化管理,对于某种模块需要添加对应的模块实现.QT SQL也是需要增加对应的模块来实现.QT数据库的层次关系为: 驱动层:数据库到SQL语言之间的桥梁 SQL API层: SQL语句的 ...

  7. Scala 不可变集合Set

    1 package chapter07 2 3 object Test06_ImmutableSet { 4 def main(args: Array[String]): Unit = { 5 // ...

  8. Go 语言中切片的使用和理解

    切片与数组类似,但更强大和灵活.与数组一样,切片也用于在单个变量中存储相同类型的多个值.然而,与数组不同的是,切片的长度可以根据需要增长和缩小.在 Go 中,有几种创建切片的方法: 使用[]datat ...

  9. C# 报错 Lc.exe已退出 代码为-1 如何解决

    完美解决方案:把项目文件夹下Properties文件夹下的licenses.licx文件删除,重新编译即可:

  10. 华为帐号为AITO问界M5助力,打造懂你的智能座舱

    12月23日,在华为冬季旗舰新品发布会上,AITO问界M5正式发布.华为赋能的AITO问界M5搭载HUAWEI DriveONE纯电驱增程平台和HarmonyOS智能座舱,并且带来华为终端云服务软硬协 ...