【问题标题】:How to access an encompassing Canvas control from its children in WPF如何从 WPF 中的子级访问包含的 Canvas 控件
【发布时间】:2026-01-05 21:25:01
【问题描述】:

我有一个Canvas,其 XAML 定义如下:

<Canvas Height="201" VerticalAlignment="Top"
        Name="myCanvas"
        KeyDown="KeyEvent" >
    <local:MyGlyphsElement x:Name="mge" />
    <Line Name="myLine" Stroke="Blue" StrokeThickness="2"></Line>
</Canvas>

MyGlyphsElement 控件的代码隐藏文件中,如何访问myLinemyCanvas? (MyGlyphsElement 派生自 FrameworkElement。)

我的目的是能够在运行时将控件添加到myCanvas 子级以及操纵myLine 属性,例如笔划宽度等。

编辑:

public partial class MyGlyphsElement: FrameworkElement
    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);  // Good practice.
        ...
        Canvas cp = (Canvas)this.Parent;
        // Now what? How do I access myLine?

【问题讨论】:

  • 请参阅My Example,了解如何使用适当的 WPF 技术(例如 XAML、DataBinding、ItemsControls 和 DataTemplates,而不是过程代码)来执行此操作。
  • HighCore 是对的,但除此之外,FrameworkElement 有一个 Parent 属性,您可以将其转换为 Canvas。画布反过来(与任何其他面板一样)具有Children 属性。
  • @Clemens:当我使用父属性时,我收到“枚举错误/集合已更改”异常。
  • 您或许应该将相关代码添加到您的问题中。否则很难知道发生了什么。
  • @HighCore:你以前帮助过我,我已经剖析和研究了你的许多解决方案,你是我的知识来源。但有时 MVVM 模式过于矫枉过正,人们只是想要一个快速的解决方案。

标签: c# wpf


【解决方案1】:

为解析可视化树做一些漂亮的扩展,就像这样(这会让你得到一个指定名称的元素):

public static class DependentObjectExtensions
{
    public static IEnumerable<DependencyObject> GetChildren(this DependencyObject parent)
    {
        for (int i = 0, length = VisualTreeHelper.GetChildrenCount(parent); i < length; i++)
        {
            yield return VisualTreeHelper.GetChild(parent, i);
        }
    }

    public static T FindChild<T>(this DependencyObject parent, string name, bool drillDown = false)
        where T : FrameworkElement
    {
        if(parent != null)
        {
            var elements = parent.GetChildren().OfType<T>();
            return drillDown
                ? elements.Select(x => FindChild<T>(x, name, true)).FirstOrDefault(x => x != null)
                : elements.FirstOrDefault(x => x.Name == name);
        }
        return null;
    }
}

然后,将您的 MyGlyphsElement.OnRender 更改为如下所示:

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        var myLine = Parent.FindChild<Line>("myLine");
        //TODO: Do stuff with your line.
    } 

如果您需要进一步澄清,请告诉我,我会根据您的反馈编辑我的回复。

【讨论】:

  • +1 非常感谢,您的代码有效,并为我提供了对 myLine 对象的引用。很酷的解决方案。但是,我知道这是一个单独的问题,在调整窗口大小之前,myLine 控件不会显示。你知道为什么吗?
  • 如果您正在对OnRender 中的myLine 进行更改并且在调整大小之前不会刷新,请尝试将更改移动到myLine 上方的base.OnRender。或者,如果这不起作用,我怀疑您的 KeyEvent 中的某些逻辑正在影响渲染。如果是这种情况,如果您在其中发布逻辑,我将很乐意提供帮助。否则,请不要忘记将答案标记为已解决;)
  • 谢谢。将代码移至base.OnRender() 上方并没有帮助。稍后将不得不发布一个单独的问题,我现在太累了。希望新问题能引起您的注意。
  • Tyler,我已经发布了新问题:*.com/questions/24438483/…FYI,感谢您对这个问题的帮助。