11 votes

Comment ajouter plusieurs fois à un formulaire WPF, au moment de l'exécution, un chemin défini dans le XAML ResourceDictionary ?

J'ai un chemin défini dans 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>

Je veux l'ajouter à un Gird WPF et le faire une fois comme ceci fonctionne :

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

Cependant, si j'ajoute ce code à l'événement de clic d'un bouton et que je clique ensuite deux fois sur le bouton, une erreur se produit, avec le texte suivant

"Le visuel spécifié est déjà un enfant d'un autre visuel ou la racine d'une CompositionTarget".

J'ai ensuite essayé de créer deux instances de la ressource mais j'ai continué à recevoir la même erreur. Voici le code que j'ai utilisé pour ce test :

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

En tant que tel, comment puis-je ajouter un chemin XAML, qui a été défini dans le ResourceDictionary, plusieurs fois à un formulaire WPF au moment de l'exécution ?

15voto

skypecakes Points 890

Il existe un moyen plus simple et intégré de le faire. Définissez x:Shared="False" sur la ressource. Cela lui permettra d'être réutilisée. Utilisez-la ensuite autant de fois que vous le souhaitez.

<UserControl.Resources>
    <ResourceDictionary>
        <Path x:Shared="False" x:Key="N44" Width="20" Height="80" Stretch="Fill" Fill="#FF000000" Data="..."/>
    </ResourceDictionary>
</UserControl.Resources>

12voto

Sebastian Gray Points 1137

J'ai découvert depuis que j'avais manqué une partie importante de l'histoire de l'entreprise. la documentation de MSDN :

Types partageables et types UIElement :

Un dictionnaire de ressources est une technique pour définir des types et des valeurs partageables de ces types dans XAML. Tous les types ou ne conviennent pas tous à l'utilisation d'un ResourceDictionary. Pour plus d'informations informations sur les types qui sont considérés comme partageables dans Silverlight, voir Dictionnaires de ressources.

En particulier, tous les éléments dérivés de UIElement ne sont pas partageables, sauf s'ils proviennent de modèles et l'application d'un d'un modèle sur un contrôle spécifique spécifique. En excluant le cas des modèles, un UIElement est censé n'exister que qu'à un seul endroit dans un arbre d'objets instancié, et le fait qu'un UIElement soit partageable soit partageable violerait potentiellement ce principe.

Ce que je résumerai en disant, ce n'est pas comme ça que ça marche parce que il ne crée pas de nouvelle instance à chaque fois que j'exécute ce code, il ne fait que créer une référence à l'objet, ce qui explique pourquoi il fonctionne une fois mais pas plusieurs. Après avoir lu un peu plus, j'ai trouvé trois solutions possibles pour résoudre mon problème.

1) Utilisez une technique pour créer une copie profonde vers un nouvel objet. Exemple à partir d'une autre question StackOverflow - Clonage profond d'objets

2) Stockez le XAML dans des chaînes au sein de l'application, puis utilisez le lecteur XAML pour créer des instances des chemins :

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) Ne stockez que les données relatives aux chemins dans le dictionnaire des ressources. Créer une nouvelle instance d'un chemin dans le code, appliquer les données du chemin au nouveau chemin, puis ajouter manuellement les autres propriétés qui m'intéressent.

Le XAML - Les données du chemin sont stockées en tant que 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>

Le code C# pour ensuite créer une instance et appliquer les autres valeurs :

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

3voto

MrH Points 1

Il suffit de créer un style pour Path, et de l'appliquer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X