11

我在 XAML 中有一个定义的路径:

<UserControl.Resources>
    <ResourceDictionary>
        <Path x:Key="N44" Width="20" Height="80" Stretch="Fill" Fill="#FF000000" Data="M 20,25.2941L 20,29.4118L 15.9091,29.4118L 15.9091,40L 12.2727,40L 12.2727,29.4118L 2.54313e-006,29.4118L 2.54313e-006,25.6985L 13.4872,7.62939e-006L 15.9091,7.62939e-006L 15.9091,25.2941L 20,25.2941 Z M 12.2727,25.2941L 12.2727,5.28493L 2.09517,25.2941L 12.2727,25.2941 Z M 20,65.2941L 20,69.4118L 15.9091,69.4118L 15.9091,80L 12.2727,80L 12.2727,69.4118L -5.08626e-006,69.4118L -5.08626e-006,65.6985L 13.4872,40L 15.9091,40L 15.9091,65.2941L 20,65.2941 Z M 12.2727,65.2941L 12.2727,45.2849L 2.09517,65.2941L 12.2727,65.2941 Z "/>
    </ResourceDictionary>
</UserControl.Resources>

我想将它添加到 WPF 网格中并像这样工作一次:

System.Windows.Shapes.Path aPath = new System.Windows.Shapes.Path();
aPath = (System.Windows.Shapes.Path)this.Resources["N44"];
LayoutRoot.Children.Add(aPath); 

但是,如果我在按钮单击事件上添加此代码,然后单击按钮两次,则会引发错误说明

“指定的视觉对象已经是另一个视觉对象的子对象或 CompositionTarget 的根。”

然后我尝试创建资源的两个实例,但我继续收到相同的错误。以下是我用于此测试的代码:

private void cmbTest_Click(object sender, System.Windows.RoutedEventArgs e)
  {
   System.Windows.Shapes.Path aPath = new System.Windows.Shapes.Path();
   aPath = (System.Windows.Shapes.Path)this.Resources["N44"];

   if (LayoutRoot.Children.Contains(aPath) == true){
    System.Windows.Shapes.Path bPath = (System.Windows.Shapes.Path)this.Resources["N44"];
    LayoutRoot.Children.Add(bPath); 
   }else{
    aPath.Name = "a";
    aPath.Tag = "a";
    LayoutRoot.Children.Add(aPath);
   }
  }

因此,如何在运行时将已在 ResourceDictionary 中定义的 XAML 路径多次添加到 WPF 表单?

4

3 回答 3

15

有一种更简单的内置方法可以做到这一点。在资源上设置 x:Shared="False"。这将允许它被重复使用。然后根据需要多次使用它。

<UserControl.Resources>
    <ResourceDictionary>
        <Path x:Shared="False" x:Key="N44" Width="20" Height="80" Stretch="Fill" Fill="#FF000000" Data="..."/>
    </ResourceDictionary>
</UserControl.Resources>
于 2010-05-12T17:15:45.517 回答
12

从那以后,我发现我错过了MSDN 文档的重要部分:

可共享类型和 UIElement 类型:

资源字典是一种在 XAML 中定义可共享类型和这些类型的值的技术。并非所有类型或值都适合从 ResourceDictionary 使用。有关在 Silverlight 中哪些类型被视为可共享的详细信息,请参阅资源字典。

特别是,所有 UIElement 派生类型都是不可共享的,除非它们来自模板和特定控件实例上的模板应用程序。排除模板的情况,一旦实例化,UIElement 应该只存在于对象树中的一个位置,并且使 UIElement 可共享可能会违反此原则。

我将总结为,这不是它的工作方式,因为它不会在我每次执行该代码时创建一个新实例——它只是创建一个对对象的引用——这就是它工作一次但不能多次工作的原因。因此,在阅读了更多内容后,我想出了 3 种可能的方法来解决我的问题。

1)使用一种技术为新对象创建深层副本。来自其他 StackOverflow 问题的示例 -深度克隆对象

2)将 XAML 存储在应用程序中的字符串中,然后使用 XAML 阅读器创建路径的实例:

System.Windows.Shapes.Path newPath = (System.Windows.Shapes.Path)System.Windows.Markup.XamlReader.Parse("<Path xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'  Width='20' Height='80' Stretch='Fill' Fill='#FF000000' Data='M 20,25.2941L 20,29.4118L 15.9091,29.4118L 15.9091,40L 12.2727,40L 12.2727,29.4118L 2.54313e-006,29.4118L 2.54313e-006,25.6985L 13.4872,7.62939e-006L 15.9091,7.62939e-006L 15.9091,25.2941L 20,25.2941 Z M 12.2727,25.2941L 12.2727,5.28493L 2.09517,25.2941L 12.2727,25.2941 Z M 20,65.2941L 20,69.4118L 15.9091,69.4118L 15.9091,80L 12.2727,80L 12.2727,69.4118L -5.08626e-006,69.4118L -5.08626e-006,65.6985L 13.4872,40L 15.9091,40L 15.9091,65.2941L 20,65.2941 Z M 12.2727,65.2941L 12.2727,45.2849L 2.09517,65.2941L 12.2727,65.2941 Z ' HorizontalAlignment='Left' VerticalAlignment='Top' Margin='140,60,0,0'/>");
LayoutRoot.Children.Add(newPath);

3)仅将路径数据存储在资源字典中。在代码中创建 Path 的新实例,将 Path 数据应用到新 Path,然后手动添加我感兴趣的其他属性。

XAML - 路径数据存储为 StreamGeometry:

<UserControl.Resources>
    <ResourceDictionary>
        <StreamGeometry x:Key="N44">M 20,25.2941L 20,29.4118L 15.9091,29.4118L 15.9091,40L 12.2727,40L 12.2727,29.4118L 2.54313e-006,29.4118L 2.54313e-006,25.6985L 13.4872,7.62939e-006L 15.9091,7.62939e-006L 15.9091,25.2941L 20,25.2941 Z M 12.2727,25.2941L 12.2727,5.28493L 2.09517,25.2941L 12.2727,25.2941 Z M 20,65.2941L 20,69.4118L 15.9091,69.4118L 15.9091,80L 12.2727,80L 12.2727,69.4118L -5.08626e-006,69.4118L -5.08626e-006,65.6985L 13.4872,40L 15.9091,40L 15.9091,65.2941L 20,65.2941 Z M 12.2727,65.2941L 12.2727,45.2849L 2.09517,65.2941L 12.2727,65.2941 Z</StreamGeometry>
    </ResourceDictionary>
</UserControl.Resources>

然后 C# 代码创建一个实例并应用其他值:

System.Windows.Shapes.Path bPath = new System.Windows.Shapes.Path();
bPath.Data = (System.Windows.Media.Geometry)this.FindResource("N44");

bPath.Width = 20;
bPath.Height = 80;

bPath.VerticalAlignment = System.Windows.VerticalAlignment.Top;
bPath.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

left = left + 40;

System.Windows.Thickness thickness = new System.Windows.Thickness(left,100,0,0);
bPath.Margin = thickness;

bPath.Fill = System.Windows.Media.Brushes.Black;
LayoutRoot.Children.Add(bPath);
于 2009-09-05T00:15:16.603 回答
3

只需为 Path 创建样式并应用它。

于 2009-09-04T07:55:16.270 回答