C#WPF ArgumentException:指定的Visual已经是另一个Visual的子项或CompositionTarget的根

时间:2014-02-19 14:34:59

标签: c# wpf canvas clone argumentexception

我有一个带有图像及其左/上位置的列表,我将其添加到Canvas中。但是,我希望能够将相同的图像(相同的源)添加到Canvas,没有任何问题。

当我使用以下代码时:

Image img = ImagesList[i].Image; // ImagesList is a list of MyClass (containing Image Image; double Left; double Top)
img.Name = "img" + i; // where i is the nr in the list
Canvas.SetLeft(img, ImagesList[i].Left); // Left default = 0
Canvas.SetTop(img, ImagesList[i].Top); // Top default = 0
MyCanvas.Children.Add(img);
OnPropertyChanged("MyCanvas");

当Canvas上已存在相同的Image(-source)时(具有不同的Left / Top位置和Name),我得到以下异常:

ArgumentException: Specified Visual is already a child of another Visual or the root of a CompositionTarget.

所以我知道我不允许将相同的UIElement(在我的情况下为Image)添加到同一个Canvas中。

我将代码修改为:

// If the Image already exists on the Canvas, we need to make a clone of the image
if (MyCanvas.Children.Contains(img)) {
    Image cloneImg = new Image();
    cloneImg.Source = img.Source;
    cloneImg.Name = img.Name;
    Canvas.SetLeft(cloneImg, Left);
    Canvas.SetTop(cloneImg, Top);
    MyCanvas.Children.Add(cloneImg);
}
else
    MyCanvas.Children.Add(img);
OnPropertyChanged("MyCanvas");

这解决了错误,但现在我遇到了一个新问题..我在画布上得到了两个图像,但是已经存在的图像将它的位置(左和上)重置为0,0(与新的相同)添加了Image),当我进行Console.Write测试时,我也注意到Canvas上两个图像的名称现在都是一样的。

克隆的错误是什么?第一个图像的名称,左边,顶部(可能还有其他东西)与第二个图像(“克隆”)相同?

提前感谢您的回复。


编辑:在克莱门斯的建议之后,我改变了我的xaml:

<Canvas Name="MyCanvas" Background="LimeGreen"/>

要:

<ItemsControl ItemsSource="{Binding MyField.ImagesList}"> <!-- MyField is the class where I have my list -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Name="FieldCanvas" Background="LimeGreen" IsItemsHost="True"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Left}"/>
                <Setter Property="Canvas.Top" Value="{Binding Top}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Image Source="{Binding ImageSource}" AllowDrop="True" PreviewMouseLeftButtonDown="Image_PreviewMouseLeftButtonDown" PreviewMouseMove="Image_PreviewMouseMove" PreviewMouseLeftButtonUp="Image_PreviewMouseLeftButtonUp"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

ImagesList现在还包含BitmapImage ImagesSource而不是Image Image。

但是我还没能测试它,因为它给出了一堆错误。我使用Canvas作为ViewModel的构造函数中的参数,但现在不知道如何访问它现在它在ItemsControl中。 我在MouseEvent函数中也遇到了一些错误,我使用img.Parent来获取Canvas(不再使用上面的代码)和canvas.Children来获取所有图像,包括它们的ZIndex(也是不再工作了。


EDIT2 / SOLUTION:

撤消上面的编辑之后因为我的其他糟糕的代码部分导致了很多错误,除了我保存在列表而不是图像本身的ImageSource之外,它现在证明它可以正常工作。

2 个答案:

答案 0 :(得分:3)

您通常使用ItemsControl执行此操作,其中Canvas为ItemsPanelItemContainerStyleCanvas.LeftCanvas.Top属性绑定到适当的属性在您的数据项类中。

如果这是您的数据项类:

public class ImageItem
{
    public string Source { get; set; }
    public double Left { get; set; }
    public double Top { get; set; }
}

public class ViewModel
{
    public ViewModel()
    {
        ImageItems = new ObservableCollection<ImageItem>();
    }

    public ObservableCollection<ImageItem> ImageItems { get; private set; }
}

ItemsControl的XAML如下所示。请注意,其ItemsSource属性绑定到ViewModel类中的ImageItems属性。

<ItemsControl ItemsSource="{Binding ImageItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas IsItemsHost="True"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Path=Left}"/>
                <Setter Property="Canvas.Top" Value="{Binding Path=Top}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding Path=Source}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

MainWindow初始化代码中的某处:

var vm = new ViewModel();
DataContext = vm;

vm.ImageItems.Add(
    new ImageItem
    {
        Source = @"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg",
        Left = 100,
        Top = 50
    });
vm.ImageItems.Add(
    new ImageItem
    {
        Source = @"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg",
        Left = 200,
        Top = 100
    });

答案 1 :(得分:0)

在我用Clemens建议回复所有内容之后,在我的列表中保持BitmapImage ImageSource而不是Image Image的更改时,它现在可以正常工作。

我知道我的代码不是最好的,Clemens代码确实好多了。但是因为我已经拥有了相当多的代码,并且(在我看来)将其更改为Clemens代码同时保持与其他代码一样的工作太麻烦了,除了更改BitmapImage ImageSource之外我还原了它,事实证明它现在有效。

如果我做过类似的项目,我会从一开始就使用你的代码Clemens,因为它在WPF编程方面要好得多。代码的体系结构。谢谢你的回复。

我的问题的解决方案:我没有将图像本身保存在列表中并对其进行“克隆”,而是将我的ImageSource保存在列表中。所以我总是创建一个新的Image(),我可以从中更改Source。