【问题标题】:Why can't I assign a canvas-object to an existing canvas?为什么我不能将画布对象分配给现有画布?
【发布时间】:2013-12-10 10:26:29
【问题描述】:

我的软件创建了相当复杂的 petrinet,它们将绘制在画布上。由于这显示了多次(预览窗口,更大的视图,......)我想缓存绘制的画布。 我的第一次尝试是

XAML:

<Canvas Name="MyCanvas" />

后面的代码:

public MainWindow()
{
    InitializeComponent();

    Canvas NewCanvas = new Canvas();
    TextBlock txt1 = new TextBlock();
    txt1.Text = "Hello World!";
    Canvas.SetTop(txt1, 10);
    Canvas.SetLeft(txt1, 10);
    NewCanvas.Children.Add(txt1);

    MyCanvas = NewCanvas;
}

但这根本没有显示任何东西。但是,我可以将最后一行替换为

MyCanvas.Children.Add(NewCanvas);

现在我可以看到我的画布,但是如果我尝试打开另一个显示相同画布的窗口

Window NewWindow = new Window();
NewWindow.Content = NewCanvas;
NewWindow.Show();

我得到一个 System.ArgumentException:

“必须先断开指定子对象与当前父视觉对象的连接 附加到新的父视觉对象。”

我考虑克隆画布对象,但画布对象不可序列化,并且 XamlWriter 需要自定义编写的 UIElements 类中的特殊构造函数,但我不知道它到底需要什么。

【问题讨论】:

    标签: c# .net wpf canvas


    【解决方案1】:

    让我们先看看您的错误,以便您更好地了解代码的作用:

    MyCanvas = NewCanvas;
    

    这不符合您的要求,因为您只是替换了引用。 MyCanvas 是您实例中的一个变量,它首先指向一个 Canvas 实例(表单中显示的空实例)。当您执行上述语句时,MyCanvas 变量被修改为引用另一个 Canvas 实例 - 一个可能包含一些图形元素的 Canvas 实例,但绝不会添加到您的窗口中。您的窗口仍然只包含原始的空 Canvas 实例。

    System.ArgumentException 的出现正是因为它所说的。您不能一次将视觉元素添加到多个父级。


    更简洁的解决方案不是复制完整的Canvas,而是拥有一个方法(或带有方法的对象),该方法将Canvas 作为参数,然后创建并添加您的 petrinet 包含的所有元素的Canvas。像这样非常简化的形式:

    public class Petrinet
    {
        public void OutputPetrinet(Canvas dest)
        {
            if (dest == null) {
                throw new ArgumentNullException("dest");
            }
    
            Ellipse e = new Ellipse() {
                Width = 50,
                Height = 60,
                Fill = Brushes.Blue
            };
            Canvas.SetLeft(e, 20);
            Canvas.SetTop(e, 30);
            dest.Children.Add(e);
        }
    }
    

    然后,您可以为任意数量的Canvas 实例调用此方法。

    请注意,您还可以使用 Petrinet 类来存储描述您的 petrinet 的数据 - 并且您可以使该类可序列化,以便您可以持久化该数据,因为它是您自己的类: -)

    【讨论】:

      【解决方案2】:

      你不能做你想做的事。正如您的Exception 告诉您的那样,在 WPF 中,每个 UI 元素只能在可视树中托管一次。基本上,这意味着如果 UI 元素显示在 UI 中,那么您不能将相同的 UI 元素添加到 UI 中的另一个容器中。您可以从当前位置移除一个 UI 元素,然后将其添加到 UI 中的不同容器中,但这不是您想要的。

      为了清楚起见,我说的是 UI 元素,而不是数据元素。一个数据元素可以在 UI 中多次表示,尽管其呈现的表单看起来可能完全相同,但它们实际上都是不同的 UI 元素。

      【讨论】:

        【解决方案3】:
        *`I get an System.ArgumentException: “Must disconnect specified child from current parent Visual before attaching to new parent Visual.”`*
        

        在 Windows Presentation Foundation (WPF) 中,一个元素只能有一个可视父级和一个逻辑父级。这实际上非常重要,因为它可以确保视觉和逻辑树的格式正确。

        But that does not show anything at all. I can, however replace the last line with 这很正常,您正在创建一个新对象,您应该将其添加到您的逻辑树中,以便让 wpf 进行所有必需的计算 例如你可以做这样的事情

                this.yourGrid.Children.Add(NewCanvas);
        

        I looked into cloning the canvas-object, but the canvas-object is not serializable

        你不能像你正在做的那样序列化你的控件,你可以使用这样的东西

                string saved = XamlWriter.Save(canvas1); 
                StringReader sReader = new StringReader(saved); 
                XmlReader xReader = XmlReader.Create(sReader); 
                Canvas newCanvas = (Canvas)XamlReader.Load(xReader); 
                yourGrid.Children.Add(newCanvas);  
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-03
          • 2014-11-12
          • 2014-01-02
          • 2021-10-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多