内置控件的WPF装饰器

Rya*_*uer 22 .net c# wpf

我试图让一个不寻常的使用Adorner.当您将鼠标悬停在RichTextBox上时,Adorner(请参见下图)将显示在其上方,允许您将字符串列表添加到Adorner中包含的ListBox中.这用于将"标签"(àlaFlickr)添加到装饰元素中包含的通道.

装饰图

首先:这甚至可能吗?

大多数Adorners示例都展示了如何覆盖Adorner的OnRender方法来执行绘制形状之类的微不足道的事情.我能够使用它来渲染一组矩形,这些矩形创建了Adorner的灰色边框,如果RichTextBox的高度由于在显示Adorner时添加了额外的行文本而增加,它也会自动调整大小.

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

}
Run Code Online (Sandbox Code Playgroud)

但是,添加控件稍微有点问题.一般来说,WPF的装饰需要在代码而不是XAML中添加子控件.使用DrawingContext adorner中描述的技术- 可以绘制stackpanel?,我已经学会了如何在Adorner的初始化程序中将子控件(如TextBox)添加到Adorner而没有任何问题.

然而,问题是将这些控件放置在Adorner中.

如果我可以创建一个灰色背景的网格并将其放置在Adorner的底部,我应该很高兴.我会假设(希望)自动调整基于随着标签的大小变化而自动调整Adorner大小的事情.

简而言之,假定这是可能的,任何人都可以建议创建该下部标记控制区的一个方式的装饰器和定位它相对于装饰器的底部(其可能必须调整为在RichTextBox内容调整大小)?

Rya*_*uer 42

Huzah!在Ghenadie Tanasiev的帮助下,我得到了答案.

与WPF中的大多数控件不同,装饰器没有任何开箱即用的方式来分配子元素(例如我想添加的控件).在不向装饰者添加任何内容的情况下,您只能覆盖他们的OnRender方法并在DrawingContext传递给它的内容中绘制内容.说实话,这可能适合99%的装饰用例(比如在对象周围创建拖动手柄),但我需要为我的装配工添加一些适当的控件.

这样做的诀窍是创建一个VisualCollection并将您的装饰器设置为其所有者,方法是将其传递到集合的构造函数中.

本博客文章对此进行了全面的描述.不幸的是,VisualCollection由于Ghenadie的指导,在我知道要搜索之前,我的重复谷歌搜索并没有把这篇文章翻过来.

这篇文章中没有提到,但请注意,可以将VisualCollection技术与绘图在装配器的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; }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 博客已经死了,但 wayback 有一个快照。https://web.archive.org/web/20130126075829/http://www.switchonthecode.com/tutorials/wpf-tutorial-using-a-visual-collection (4认同)
  • 另见 http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/81eca7d5-88d7-477a-8cdb-cfb9e8b75379/ (2认同)