如何在运行时将多次在XAML ResourceDictionary中定义的Path添加到WPF表单?

Seb*_*ray 11 c# wpf xaml resourcedictionary

我在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>
Run Code Online (Sandbox Code Playgroud)

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

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

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

"指定的Visual已经是另一个Visual的子项或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);
   }
  }
Run Code Online (Sandbox Code Playgroud)

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

sto*_*one 13

有一种更简单的内置方法可以做到这一点.在资源上设置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>
Run Code Online (Sandbox Code Playgroud)


Seb*_*ray 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);
Run Code Online (Sandbox Code Playgroud)

3)仅将Path数据存储在Resource Dictionary中.在代码中创建Path的新实例,将Path数据应用于新Path,然后手动添加我感兴趣的其他属性.

XAML - Path数据存储为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>
Run Code Online (Sandbox Code Playgroud)

然后,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);
Run Code Online (Sandbox Code Playgroud)

  • 将Path.Data存储为资源中的StreamGeometry非常棒! (2认同)