C# 内部带有控件的WPF装饰器

C# 内部带有控件的WPF装饰器,c#,.net,wpf,C#,.net,Wpf,我试图实现一个装饰物的不同寻常的用途。当您将鼠标悬停在RichTextBox上时,它上方会出现一个装饰器(参见下图),允许您将字符串列表添加到装饰器中包含的列表框中。这用于将“标签”(即Flickr)添加到装饰元素中包含的段落中 首先:这可能吗 大多数装饰器示例都显示了如何覆盖装饰器的OnRender方法来完成诸如绘制形状之类的琐事。我能够使用它来渲染一组矩形,这些矩形创建装饰器的灰色边框,如果在显示装饰器时由于添加了额外的行文本而导致RichTextBox的高度增加,该边框也会自动调整大小

我试图实现一个装饰物的不同寻常的用途。当您将鼠标悬停在RichTextBox上时,它上方会出现一个装饰器(参见下图),允许您将字符串列表添加到装饰器中包含的列表框中。这用于将“标签”(即Flickr)添加到装饰元素中包含的段落中


首先:这可能吗

大多数装饰器示例都显示了如何覆盖装饰器的OnRender方法来完成诸如绘制形状之类的琐事。我能够使用它来渲染一组矩形,这些矩形创建装饰器的灰色边框,如果在显示装饰器时由于添加了额外的行文本而导致RichTextBox的高度增加,该边框也会自动调整大小

protected override void OnRender(DrawingContext drawingContext)
{
    SolidColorBrush grayBrush = new SolidColorBrush();
    grayBrush.Color = Color.FromRgb(153, 153, 153);

    // left
    drawingContext.DrawRectangle(grayBrush, null, new System.Windows.Rect(1, 1, 5, ActualHeight));
    // right
    drawingContext.DrawRectangle(grayBrush, null, new System.Windows.Rect(ActualWidth - 6, 1, 5, ActualHeight));
    //bottom
    drawingContext.DrawRectangle(grayBrush, null, new System.Windows.Rect(1, ActualHeight, ActualWidth - 2, 5));

    // for reasons unimportant to this example the top gray bar is rendered as part of the RichTextBox

}
但是,添加控件的问题稍微多一些。一般来说,WPF的装饰器需要在代码中添加子控件,而不是XAML。使用中描述的技术,我已经学会了如何将子控件(如文本框)添加到装饰器中,而在装饰器的初始值设定项中没有任何问题

然而,问题是这些控件在装饰器中的位置

如果我能创建一个灰色背景的网格,并将其放置在装饰器的底部,我就可以开始了。我假设(希望)添加标记时,根据网格大小的变化自动调整装饰器大小之类的事情会自动发生

简言之,假设这是可能的,有人能推荐一种方法,在装饰器内创建这个较低的标记控制区域,并将其相对于装饰器底部定位(可能需要随着RichTextBox内容的大小调整而调整大小)

Huzah!在…的帮助下,我得到了一个答案

与WPF中的大多数控件不同,装饰器没有任何现成的方式来指定子元素(例如我想要添加的控件)。在不向装饰器添加任何内容的情况下,您只能重写它们的
OnRender
方法,并在传递给它的
DrawingContext
中绘制内容。老实说,这可能适合装饰器99%的使用情况(比如围绕对象创建拖动手柄),但我需要向装饰器添加一些适当的控件

实现这一点的诀窍是创建一个装饰器,并通过将其传递到集合的构造函数中,将装饰器设置为其所有者

这一切在本文中描述得相当全面。不幸的是,在Ghenadie的指导下,在我知道如何搜索
VisualCollection
之前,我反复的谷歌搜索并没有找到这篇文章

本文中没有提到这一点,但请注意,可以将VisualCollection技术与adorner的OnRender方法中的绘图结合起来。我使用OnRender实现上图中描述的侧边和顶边,并使用VisualCollection放置和创建控件

编辑:以下是上述博文的源代码,因为它不再可用:

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; }
  }
}

另见不明显。Well discoveredblog已经死了,但wayback有一个快照。你不必把你的手弄得那么脏,看看我过去用过的。它允许您在XAML中定义装饰器,进行绑定,等等。实际上,我已经花了几个小时来研究这种方法,试图找到一种实现上述定位的方法,但我认为CodeProject示例中的代码不够健壮,无法适应我要做的事情。例如,它将我创建的任何装饰条剪切到装饰元素的边界(非常奇怪,因为装饰条的z索引总是应该在顶部)。我尝试了一些测试,比如将AdornedControl.AdornerContent下元素的高度绑定到装饰元素的实际高度,但失败了(无提示)。是的,它进行了剪裁,我发现它剪裁到布局,而不是装饰元素。至于你的问题,我再看看你的记忆,狒狒。:)今天下午我将自己继续做下去,并将报告我学到的任何东西。它实际上会剪辑到装饰层的边界。您可以在VisualTree中的任意位置放置AdornerDecorator,当您请求adorner层时,这是树中获取层的部分。如果您的AdorneDecorator的ClipToBounds设置为true,则它将被剪裁到该矩形。如果你不想剪贴,把你的装饰器放在窗口的水平,你的装饰器将有自由支配的位置。(我需要完全相反的方式…在面板周围显式放置一个AdorneDecorator,我希望将子控件的装饰夹到该面板上。