Instantiate y casos de reutilización de los objetos en XAML
-
27-09-2019 - |
Pregunta
Quiero crear instancias de objetos en XAML, y reutilizar estos casos. Creo que debería ser simple, pero estoy atascado, probablemente estoy perdiendo algo obvio.
Say Quiero añadir gatos a diferentes habitaciones (habitación tiene una ObservableCollection que contiene objetos de tipo gato). En los UserControl.Resources creo ObjectDataProviders:
<ObjectDataProvider x:Key="Cat1" ObjectType="{x:Type local:Cat}">
<ObjectDataProvider.ConstructorParameters>
<System:String>Tom</System:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="Cat2" ObjectType="{x:Type local:Cat}">
<ObjectDataProvider.ConstructorParameters>
<System:String>Garfield</System:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="Cat3" ObjectType="{x:Type local:Cat}">
<ObjectDataProvider.ConstructorParameters>
<System:String>Furball</System:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
En mi UserControl Quiero añadir los gatos de las habitaciones:
<local:Room x:Name="Room1">
<local:Room.Cats>
</local:Room.Cats>
<local:Room>
<local:Room x:Name="Room2">
<local:Room.Cats>
</local:Room.Cats>
<local:Room>
¿Cuál es la sintaxis para agregar las instancias de Cat a los Room.Cats ObservableCollection? Por ejemplo, quiero añadir Cat1 y Cat2 a Room1, y Cat2 y Cat3 a Room2 ¿Te. ¿Estoy completamente en el camino equivocado?
Solución 2
Con base en la retroalimentación de Heinzi y Robert Rossney me ocurrió la siguiente solución que funciona con un ObservableCollection que pueden acceder en XAML y código detrás:
En el código que se extendía ObservableCollection para que pueda usarlo en XAML (esto ya no será necesario en XAML 2009):
public class CatObservableCollection : ObservableCollection<Cat> { }
En XAML en los UserControl.Resources que instanciar los gatos:
<local:Cat x:Key="Tom" Name="Tom"/>
<local:Cat x:Key="Garfield" Name="Garfield"/>
<local:Cat x:Key="Furball" Name="Furball"/>
Las colecciones:
<local:CatObservableCollection x:Key="Room1Collection">
<StaticResourceExtension ResourceKey="Tom"/>
<StaticResourceExtension ResourceKey="Garfield"/>
</local:CatObservableCollection>
<local:CatObservableCollection x:Key="Room2Collection">
<StaticResourceExtension ResourceKey="Garfield"/>
<StaticResourceExtension ResourceKey="Furball"/>
</local:CatObservableCollection>
Las habitaciones están definidos de la siguiente manera:
<local:Room x:Name="Room1" Cats="{StaticResource Room1Collection}"/>
<local:Room x:Name="Room2" Cats="{StaticResource Room2Collection}"/>
Room.Cats es un ObservableCollection
Otros consejos
reutilización de sesiones individuales de la manera que estamos tratando de hacer es complicado. Esto se debe a la forma en que generalmente se comunica objetos individuales en XAML es con la extensión StaticResource
marcado, y sólo se puede utilizar esa extensión de marcado para establecer un valor de propiedad.
Así se pueden crear fácilmente una propiedad de tipo Cat
a una instancia de un Cat
:
<Room Cat="{StaticResource Cat1}"/>
pero no se puede rellenar una colección mediante el establecimiento de una propiedad.
La respuesta, sorprendentemente, es crear instancias de los objetos directamente en XAML en lugar de envolverlos en ObjectDataProvider
s. Todavía se utiliza el ObjectDataProvider
, pero de manera diferente:
<Window x:Class="ObjectDataProviderDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ObjectDataProviderDemo"
xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<local:Cat x:Key="Tom" Name="Tom"/>
<local:Cat x:Key="Garfield" Name="Garfield"/>
<local:Cat x:Key="Furball" Name="Furball"/>
<Collections:ArrayList x:Key="CatList1">
<ObjectDataProvider ObjectInstance="{StaticResource Tom}" />
<ObjectDataProvider ObjectInstance="{StaticResource Garfield}" />
<ObjectDataProvider ObjectInstance="{StaticResource Furball}" />
</Collections:ArrayList>
<Collections:ArrayList x:Key="CatList2">
<ObjectDataProvider ObjectInstance="{StaticResource Tom}" />
<ObjectDataProvider ObjectInstance="{StaticResource Furball}" />
</Collections:ArrayList>
<DataTemplate x:Key="CatTemplate">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{StaticResource CatList1}"
ItemTemplate="{StaticResource CatTemplate}"/>
<ListBox ItemsSource="{StaticResource CatList2}"
ItemTemplate="{StaticResource CatTemplate}" />
</StackPanel>
</Window>
Para sus necesidades muy específicas, que no vaya para los genéricos. Y utilizar los genéricos de forma declarativa, usted tiene que utilizar x: Directiva TypeArguments. TypeArguments Directiva sólo se puede utilizar con el elemento raíz. Por lo tanto, usted tiene que ir ahora para archivo XAML suelto. Un archivo XAML suelto puede ser leído utilizando System.Windows.Markup.XamlReader.Load(Stream Stream) method
Cat.cs:
using System;
namespace WpfCollection._3840371
{
public class Cat
{
public Cat() { }
public Cat(String name, String color) { Name = name; Color = color; }
public String Name { get; set; }
public String Color { get; set; }
}
}
Room.cs:
using System;
using System.Collections.ObjectModel;
namespace WpfCollection._3840371
{
public class Room<T> where T : System.Windows.Data.ObjectDataProvider
{
public Room()
{
Cats = new ObservableCollection<T>();
}
public ObservableCollection<T> Cats { get; set; }
}
}
clase Window:
namespace WpfCollection._3840371
{
/// <summary>
/// Interaction logic for Win3840371.xaml
/// </summary>
public partial class Win3840371 : Window
{
public Win3840371()
{
InitializeComponent();
Room<ObjectDataProvider> kitchenRoom;
using (FileStream fs = new FileStream(@"3840371/roomcats.txt", FileMode.Open))
{
kitchenRoom = (Room<ObjectDataProvider>)XamlReader.Load(fs);
}
foreach (ObjectDataProvider o in kitchenRoom.Cats)
Debug.WriteLine(((Cat)o.Data).Name + " : " + ((Cat)o.Data).Color);
}
}
}
Por lo tanto, el archivo .txt que contiene el código XAML sería:
<local:Room
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfCollection._3840371;assembly=WpfCollection"
x:Key="UpperRoom" x:TypeArguments="ObjectDataProvider">
<local:Room.Cats>
<ObjectDataProvider x:Key="Cat1" ObjectType="{x:Type local:Cat}">
<ObjectDataProvider.ConstructorParameters>
<System:String>Tom</System:String>
<System:String>Red</System:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="Cat2" ObjectType="{x:Type local:Cat}">
<ObjectDataProvider.ConstructorParameters>
<System:String>Rubia</System:String>
<System:String>Brown</System:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</local:Room.Cats>
</local:Room>