在XAML中实例化和重用对象实例

4
我希望在XAML中实例化对象,并重复使用这些实例。我认为应该很简单,但我卡住了,可能是我漏掉了一些显而易见的东西。
比如我想将猫添加到不同的房间(Room有一个ObservableCollection,其中包含Cat类型的对象)。在UserControl.Resources中,我创建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>

在我的用户控件中,我想将猫添加到房间中:
<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>

如何将Cat实例添加到ObservableCollection Room.Cats中?例如,我想将Cat1和Cat2添加到Room1,将Cat2和Cat3添加到Room2。我完全错了吗?


可能可以通过将 <StaticResource ResourceKey="Cat1" /> 添加到您的 Cat 列表中来实现此操作。由于我目前没有测试的可能性,因此我只将其添加为注释... - Heinzi
@Heinzi:我尝试了一下,它编译通过了,但在运行时它会抛出一个XamlParseException异常:类型为“System.Windows.Data.ObjectDataProvider”的对象无法转换为类型“System.Collections.ObjectModel.ObservableCollection`1 [Cat]”。 - eriksmith200
我明白了。我接下来会尝试的是在XAML中创建一个ObservableCollection,放在<local:Room.Cats>里面(请参阅http://msdn.microsoft.com/en-us/library/ms668604.aspx中的“关于XAML用法的注释”),然后将`ObjectDataProvider`替换为`Cat`(即`<local:Cat Name="Garfield" x:Key="Cat1">),并将StaticResource添加到指向ObservableCollection中的Cat。但是,这需要你的Cat`类有一个无参数的构造函数。 - Heinzi
3个回答

7

尝试以您所尝试的方式重复使用单个实例是棘手的。这是因为通常在XAML中引用单个对象的方式是使用StaticResource标记扩展,而您只能使用该标记扩展来设置属性值。

因此,您可以轻松地将类型为Cat的属性设置为Cat的实例:

<Room Cat="{StaticResource Cat1}"/>

但是你不能通过设置属性来填充集合。

令人惊讶的是,答案是在XAML中直接实例化对象,而不是将它们包装在ObjectDataProvider中。 你仍然使用ObjectDataProvider,但是方式不同:

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

3

根据Heinzi和Robert Rossney的反馈,我想出了以下解决方案,可以使用ObservableCollection在XAML和代码后台中访问:

在代码中,我扩展了ObservableCollection,以便可以在XAML中使用它(在XAML 2009中将不再需要此操作):

public class CatObservableCollection : ObservableCollection<Cat> { }

在UserControl.Resources中的XAML中,我实例化了Cats:
<local:Cat x:Key="Tom" Name="Tom"/>
<local:Cat x:Key="Garfield" Name="Garfield"/>
<local:Cat x:Key="Furball" Name="Furball"/>

集合:

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

房间现在被定义为以下内容:
<local:Room x:Name="Room1" Cats="{StaticResource Room1Collection}"/>
<local:Room x:Name="Room2" Cats="{StaticResource Room2Collection}"/>

Room.Cats is an ObservableCollection<Cat>


0

针对您非常特定的需求,您需要使用泛型。为了声明性地使用泛型,您必须使用x:TypeArguments Directive。TypeArguments指令只能与根元素一起使用。因此,您现在必须选择松散的XAML文件。可以使用System.Windows.Markup.XamlReader.Load(Stream Stream)方法读取松散的XAML文件。

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

窗口类:

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

因此,包含XAML代码的.txt文件将是:

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

请确保将roomcats.txt的Build Action设置为Content,将Copy到输出目录属性设置为Always。 - AnjumSKhan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接