Как я могу добавить Path, который был определен в XAML ResourceDictionary, несколько раз для формы WPF во время выполнения?

У меня есть определенный путь в 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 Gird и сделать это один раз, как это работает:

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);
   }
  }

Как таковой, как я могу добавить путь XAML, который был определен в ResourceDictionary, несколько раз для формы WPF во время выполнения?

Ответ 1

С тех пор я обнаружил, что я пропустил важную часть документа из MSDN:

Типы и типы UIElement:

Ресурсный словарь - это метод для определение разделяемых типов и значений эти типы в XAML. Не все типы или значения подходят для использования с ResourceDictionary. Для большего информация о том, какие типы считается совместимым в Silverlight, см. Ресурсные словари.

В частности, все полученные UIElement типы не подлежат совместному использованию, если они не исходят из шаблонов и применения шаблон для конкретного элемента управления пример. Исключая шаблон, ожидается, что UIElement будет существовать только в одном месте в дереве объектов один раз экземпляр и имеющий UIElement могут быть потенциально нарушены этот принцип.

Что я буду суммировать, так как это не так, как это работает, потому что не создает новый экземпляр каждый раз, когда я запускаю этот код, - это только создание ссылки на объект - вот почему он работает один раз, но не несколько раз. Поэтому после немного большего количества чтения Ive придумали 3 возможных способа решения моей проблемы.

1) Используйте технику для создания глубокой копии для нового объекта. Пример из другого объекта StackOverflow - Глубокие объекты клонирования

2) Храните XAML в строках в приложении, а затем используйте XAML-ридер для создания экземпляров Paths:

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 к новому пути, а затем добавьте другие свойства, которые меня интересуют вручную.

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>

Код С#, чтобы затем создать экземпляр и применить другие значения:

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);

Ответ 2

Там проще, встроенный способ сделать это. Установите 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>

Ответ 3

Просто создайте стиль для Path и примените его.