¿Cómo puedo añadir un camino, que se ha definido en el XAML ResourceDictionary, varias veces a una forma de WPF en tiempo de ejecución?
-
21-09-2019 - |
Pregunta
Tengo una trayectoria definida en 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>
Quiero añadir a un ceñidor WPF y hacerlo una vez como esto funciona:
System.Windows.Shapes.Path aPath = new System.Windows.Shapes.Path();
aPath = (System.Windows.Shapes.Path)this.Resources["N44"];
LayoutRoot.Children.Add(aPath);
Sin embargo, si añado este código en un evento de clic de botón y haga clic en el botón dos veces, se produce un error que indica
"especificado visual ya es un niño de otro visual o la raíz de una CompositionTarget ".
Entonces intentó crear dos instancias del recurso, sino que he seguido recibiendo el mismo error. A continuación se muestra el código que he utilizado para esta prueba:
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);
}
}
Como tal, ¿cómo puedo añadir una ruta de XAML, que ha sido definida en el ResourceDictionary, varias veces a una forma de WPF en tiempo de ejecución?
Solución 2
Desde entonces, he descubierto que me había perdido una parte importante de la documentación de MSDN :
Tipos compartibles y tipos de UIElement:
Un diccionario de recursos es una técnica para la definición de tipos y valores de compartibles estos tipos en XAML. No todos los tipos o los valores son adecuados para el uso de una ResourceDictionary. Para más información sobre qué tipos son considerado compartible en Silverlight, ver diccionarios de recursos.
En particular, todos UIElement deriva tipos no son compartibles a menos que provienen de plantillas y aplicación de una plantilla en un control específico ejemplo. Excluyendo el caso plantilla, Se espera UIElement a sólo existen en un solo lugar en un árbol de objetos vez instanciada, y que tiene un UIElement compartible ser potencialmente violar este principio.
¿Qué voy a resumir como, esa no es la forma en que funciona, porque No es la creación de una nueva instancia cada vez que ejecutar ese código - que sólo está creando una referencia al objeto - que es por qué funciona vez, pero no varias veces. Así que después de un poco más de lectura que he llegado con 3 posibles formas de resolución de mi problema.
1) Utilice una técnica para crear una copia profunda a un nuevo objeto. Ejemplo de otra pregunta StackOverflow - objetos de clonación profundas
2) Almacenar el XAML en cadenas dentro de la aplicación y luego utilizar el lector XAML para crear instancias de los caminos:
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) Sólo almacenar los datos de trayectoria en el diccionario de recursos. Crear una nueva instancia de un camino en el código, aplicar los datos de ruta de la nueva ruta y luego agregar las otras propiedades Estoy interesado en forma manual.
El XAML - los datos de trayectoria se almacena como un 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>
El código C # a continuación, crear una instancia y aplicar los otros valores:
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);
Otros consejos
Hay una manera más fácil, incorporado para realizar esta acción. Set x: Compartido = "false" en el recurso. Esto permitirá que pueda ser reutilizado. Luego usarlo tantas veces como desee.
<UserControl.Resources>
<ResourceDictionary>
<Path x:Shared="False" x:Key="N44" Width="20" Height="80" Stretch="Fill" Fill="#FF000000" Data="..."/>
</ResourceDictionary>
</UserControl.Resources>
Hay que crear el estilo de Ruta, y aplicarlo.