While WPF and XAML make the common 90% of UI programming quite easy, sometimes it gets a little odd in those other 10%. For instance - the visual tree. Most of the time it works great, and you never need to do anything special with it. But what about when you specifically want to give a user control children? Or maybe you want to give an adorner an actual visual child, instead of using OnRender? It isn't really obvious right away how to go about doing that.

But while it is not obvious, it is possible - and so today we are going to take a look at using VisualCollection to do those things. Specifically, we are going to create an Adorner to be used in an AdornerLayer that accepts a Visual as content. This is not possible right out of the gate, because Adorners don't have any built in way to have Visual children, and we can't use any of the normal controls that do (PanelContentPresenter, etc...), because only Adorners can be placed on an AdornerLayer.

Now, the example we are going to build today to use this adorner that can present content is pretty silly, but it is actually quite useful. I've used it a number of times for drag and drop (giving visual representations of what is being dragged and what will happen when it drops). Ok, now for some code:

public class AdornerContentPresenter : Adorner
{
private VisualCollection _Visuals;
private ContentPresenter _ContentPresenter; public AdornerContentPresenter(UIElement adornedElement)
: base(adornedElement)
{
_Visuals = new VisualCollection(this);
_ContentPresenter = new ContentPresenter();
_Visuals.Add(_ContentPresenter);
} public AdornerContentPresenter(UIElement adornedElement, Visual content)
: this(adornedElement)
{ Content = content; } protected override Size MeasureOverride(Size constraint)
{
_ContentPresenter.Measure(constraint);
return _ContentPresenter.DesiredSize;
} protected override Size ArrangeOverride(Size finalSize)
{
_ContentPresenter.Arrange(new Rect(0, 0,
finalSize.Width, finalSize.Height));
return _ContentPresenter.RenderSize;
} protected override Visual GetVisualChild(int index)
{ return _Visuals[index]; } protected override int VisualChildrenCount
{ get { return _Visuals.Count; } } public object Content
{
get { return _ContentPresenter.Content; }
set { _ContentPresenter.Content = value; }
}
}

That is the entirety of the code for the AdornerContentPresenter. Not a whole lot of code, but a lot is going on, so I'll go though each function. First off, the VisualCollection. As you can see in the constructor, we create a VisualCollection right off the bat. The constructor for a VisualCollection takes a single argument - the owner. The owner is always the object that will be displaying the items in the collection, so in this case it is the Adorner itself. Next, we make a ContentPresenter and add it to the VisualCollection. You might think that is a little odd, but it actually makes our life a good bit easier.

You see, the content that is given to this will always be handed to this ContentPresenter. The ContentPresenter is good at exactly one thing - presenting pretty much any type of content. This way, we don't have to worry about how to display any type of content - we just have to worry about displaying a ContentPresenter, and the ContentPresenter worries about all the complex stuff. So this ContentPresenter will always be the only member of the VisualCollection for the Adorner.

Ok, now on to that second constructor - nothing special here, this is just a convenience to set the content right during creation.

Next, the measure and arrange. This is where using the ContentPresenter comes in handy. Pretty much, we just pass through the Measure and Arrange calls straight to the ContentPresenter, and return the values that comes back (the DesiredSize property for Measure, and the RenderSize property for arrange).

Following those two methods are two items that you have probably never had a reason to mess with before: GetVisualChild and VisualChildrenCount. These are extremely important to override whenever you start playing with the children in a control. Since we have an internal VisualCollection, we can just pass the requests on to that collection. For GetVisualChild, we return the item at the requested index from the VisualCollection, and for VisualChildrenCount we return the number of items in the VisualCollection.

Finally, the Content property. All that this property does is set or get the content of our internal ContentPresenter. And thats it!

Now, you are probably wondering why I have the VisualCollection in this class at all - I sure did at first. On the surface it seems that it serves no purpose at all - and in fact stuff would "mostly" work if you weren't using it. Actually, for a while, in that Drag+Drop Adorner I was talking about earlier, I didn't use a VisualCollection, and nothing seemed wrong - until certain stuff just didn't work (like hit testing and global styles). It turns out that the VisualCollection does do some very important work underneath the surface - it maintains the parent-child connections between the owner of the collection and any items added to it. And, after some digging, it turns out that using a VisualCollection is one of two possible ways to maintain these connections. The other way is to use the protected methods AddVisualChild and RemoveVisualChild, although Microsoft generally recommends using a VisualCollection over using those two methods.

Ok, now for the silly example for how to use this AdornerContentPresenter:

<Window x:Class="VisualCollectionExample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="100" Width="300">
<AdornerDecorator>
<Rectangle Fill="Black" Width="50" Height="50" x:Name="Block" />
</AdornerDecorator>
</Window> public partial class Window1 : Window
{
public Window1()
{
InitializeComponent(); AdornerLayer layer =
AdornerLayer.GetAdornerLayer(Block);
AdornerContentPresenter adc =
new AdornerContentPresenter(Block); adc.Content = new TextBlock() {
Text = "Hi There",
Foreground = Brushes.White
};
layer.Add(adc);
}
}

Exactly what you probably expected. That code will show a black rectangle, and on top of the rectangle will the the text "Hi There" in white in the adorner layer.

Well, that is it for using a VisualCollection. Just is case you were wondering, there is an equivalent collection called UIElementCollection that is used when you want to automatically maintain the connections for both the WPF visual and logical trees - but perhaps that is content for a future tutorial. For now, you can grab the Visual Studio solution for the code above here.

Source Files:

WPF Tutorial - Using A Visual Collection的更多相关文章

  1. WPF设置VistualBrush的Visual属性制作图片放大镜效果

    原文:WPF设置VistualBrush的Visual属性制作图片放大镜效果 效果图片:原理:设置VistualBrush的Visual属性,利用它的Viewbox属性进行缩放. XAML代码:// ...

  2. WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片

    原文:WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片 我们知道,在 WPF 中的坐标单位不是屏幕像素单位,所以如果需要知道某个控件的像素尺寸,以便做一些与屏幕像素尺寸相关 ...

  3. wpf异常:指定的 Visual 不是此 Visual 的上级问题处理解析

    WPF在画线的时候,调用Control0.TransformToAncestor(Control1).Transform(new System.Windows.Point(0, 0))方法转换坐标的时 ...

  4. WPF 只读集合在 XAML 中的绑定(WPF:Binding for readonly collection in xaml)

    问题背景 某一天,我想做一个签到打卡的日历.基于 Calendar,想实现这个目标,于是找到了它的 SelectedDates 属性,用于标记签到过的日期. 问题来了. 基于MVVM模式,想将其在xa ...

  5. Tutorial: WPF User Control for AX2012

    原作者: https://community.dynamics.com/ax/b/goshoom/archive/2011/10/06/tutorial-wpf-user-control-for-ax ...

  6. WPF/MVVM Quick Start Tutorial - WPF/MVVM 快速入门教程 -原文,翻译及一点自己的补充

    转载自 https://www.codeproject.com/articles/165368/wpf-mvvm-quick-start-tutorial WPF/MVVM Quick Start T ...

  7. WPF - Visual调试工具Snoop

    原文:WPF - Visual调试工具Snoop Snoop经过很长一段时间,最近更新到支持NET 3.5了,它是一个WPF运行时对Visual UI调试的一个工具,最近我用过它调试修改过一个bug, ...

  8. [No000012E]WPF(6/7):概念绑定

    WPF 的体系结构,标记扩展,依赖属性,逻辑树/可视化树,布局,转换等.今天,我们将讨论 WPF 最重要的一部分——绑定.WPF 带来了优秀的数据绑定方式,可以让我们绑定数据对象,这样每次对象发生更改 ...

  9. [No000012D]WPF(5/7)依赖属性

    介绍 WPF带来了很多传统 Windows 应用程序没有的新特性和选择.我们已经讨论了一些 WPF 的特性,是时候更进一步介绍其他特性了.当你读完这个系列之前的文章,我希望你已经或多或少地了解了 WP ...

随机推荐

  1. 关于Eclipse如何加入Gradle文件与Android Studio两个平台一起开发,工作目录不发生变化

    前言: 本来很久之前想弄这玩意,不过因为各种原因,所以没弄. 今天有位前辈提出需求.说想让Eclipse的Android项目逐步走向Android Studio,但是又担心Android Studio ...

  2. React 设计思想

    https://github.com/react-guide/react-basic React 设计思想 译者序:本文是 React 核心开发者.有 React API 终结者之称的 Sebasti ...

  3. [Windows Azure] Windows Azure Web Sites, Cloud Services, and VMs: When to use which?

    This document provides guidance on how to make an informed decision in choosing between Windows Azur ...

  4. shell编程:for 循环

    hell 编程——for in 循环   -------for in 格式-------     for 无$变量 in 字符串 do $变量 done   一简单的字符串 枚举遍历法,利用for i ...

  5. [iOS]终极横竖屏切换解决方案

    [iOS]终极横竖屏切换解决方案 大家的项目都是只支持竖屏的吧?大多数朋友(这其中当然也包括博主),都没有做过横屏开发,这次项目刚好有这个需求,因此把横竖屏相关的心得写成一遍文章供诸位参考. 01.综 ...

  6. 【Linux】Linux根目录下各文件夹的意义

    [root@localhost /]# ll / total 102 dr-xr-xr-x. 2 root root 4096 Dec 1 07:37 bin # binary file,二进制执行文 ...

  7. 关于go1.6使用vendor的坑

    基本命令是:$ govendor init$ govendor add +external 坑:如果在govendor之前,修改过go get的包源代码,那么必须在该包目录里git commit这个修 ...

  8. 【ARM】ARM体系结构-GPIO

    GPIO    Gerneral-Purpose IO ports,即通用IO口. 在嵌入式系统中常常有数量众多,但是却比较简单的外部设备/电路. 对这些设备/电路,有的需要CPU为之提供控制手段,有 ...

  9. vue2.0的生命周期

    生命周期先上图 什么是生命周期 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.卸载等一系列过程, 我们称这是Vue的生命周期.通俗说就是Vue实 ...

  10. JS操作MongoDB

    JavaScript处理MongoDB,更新数据: #!/bin/bash mongo=/home/zhangzhenghai/cluster/mongodb/bin/mongo if true; t ...