将ObservableCollections彼此分开

时间:2016-03-28 18:58:33

标签: c# wpf observablecollection

信息

我有两个ObservableCollectionScreenshot个对象。一个称为Screenshots,其中包含用户可用的所有可能屏幕,一个称为SelectedScreenshots,这是用户想要组合的所选屏幕。每次用户通过点击在WPF中绑定到SelectedScreenshots

列表的列表来选择屏幕时,我都会在Screenshots中添加一个新屏幕

问题

问题在于,当我刷新屏幕并清除Screenshots以便我看到屏幕上已更新的内容时,即使它是另一个对象,它也会清除SelectedScreenshots。我确实理解他们指向内存中的相同位置,但我该如何解决呢?深度复制是一种解决方案。还有其他选择吗?

代码

属性:

public ObservableCollection<Screenshot> Screenshots { get; set; } = new ObservableCollection<Screenshot>();

public ObservableCollection<Screenshot> SelectedScreenshots { get; set; } = new ObservableCollection<Screenshot>();

在此处初始化和刷新屏幕:

private void InitScreens()
{
    var screenshots = Screen.AllScreens.OrderBy(scrn => scrn.Bounds.Location.X).ThenBy(scrn => scrn.Bounds.Location.Y).Select(screen => new Screenshot(GetScreenImage(screen.Bounds), screen.DeviceName));

    App.Current.Dispatcher.InvokeAsync(delegate
    {    
        Screenshots.Clear();

        for (int i = 0; i < 3; i++)
        {
            Screenshots.Add(screenshots.ElementAt(i));
        }
    });
}

添加和删除SelectedScreenshots

private void lst_ScreenShots_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    foreach (Screenshot item in e.RemovedItems)
    {
        vm.SelectedScreenshots.Remove(item);
    }

    foreach (Screenshot item in e.AddedItems)
    {
        vm.SelectedScreenshots.Add(item);
    }
}

屏幕截图类:

public class Screenshot : ViewModelBase
{
    private BitmapImage _screenImage;
    public BitmapImage ScreenImage
    {
        get { return _screenImage; }
        set
        {
            _screenImage = value;
            OnPropertyChanged();
        }
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public Screenshot(BitmapImage screenImage, string name)
    {
        this.ScreenImage = screenImage;
        this.Name = name;
    }
}

2 个答案:

答案 0 :(得分:2)

正在清除SelectedScreenshots的UI,因为您绑定的控件只能包含项目源中存在的所选项目,并且每次刷新时都会创建一组全新的屏幕截图。

解决方案是在刷新之前将所选项目保存到另一个集合,然后重新选择或仅更新现有的屏幕截图集。

我希望您能够在UI中保留所选的屏幕截图。为了实现这一点,您需要以某种方式将旧屏幕截图实例链接到新屏幕截图实例。希望你能用DeviceName做到这一点?还是按顺序?将这一点留给你。

private void InitScreens()
{
    var screenshots = Screen.AllScreens.OrderBy(scrn => scrn.Bounds.Location.X).ThenBy(scrn => scrn.Bounds.Location.Y).Select(screen => new Screenshot(GetScreenImage(screen.Bounds), screen.DeviceName));

    App.Current.Dispatcher.InvokeAsync(delegate
    {
        // store current selections
        var currentSelections = SelectedScreenshots.ToArray();

        Screenshots.Clear();

        for (int i = 0; i < 3; i++)
        {
            Screenshots.Add(screenshots.ElementAt(i));
        }

        // select what was previously selected
        SelectedScreenshots = new ObservableCollection<Screenshot>(Screenshots
           .Where(s => currentSelections.Any(c => c.DeviceName == s.DeviceName)));
    });
}

答案 1 :(得分:1)

如果您的屏幕截图对象具有引用类型的属性,那么我认为您需要按照您的说法进行操作;执行深层复制以确保Screenshots属性中的Screenshot对象和SelectedScreenshots属性中的Screenshot对象不指向相同的内存位置。在过去,我在对象中创建了一个PerformDeepCopy()方法,在您的情况下是Screenshot对象。在此方法中,您可以执行MemberwiseClone(),这是一个浅表副本。这将复制Screenshot类中的Name属性。然后,您将手动为引用类型分配新的内存地址。这是BitmapImage属性“newed up”的地方。返回值是一个新的Screenshot对象。例如:

public class Screenshot : ViewModelBase
{
    private BitmapImage _screenImage;
    public BitmapImage ScreenImage
    {
        get { return _screenImage; }
        set
        {
            _screenImage = value;
            OnPropertyChanged();
        }
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public Screenshot(BitmapImage screenImage, string name)
    {
        this.ScreenImage = screenImage;
        this.Name = name;
    }

    public Screenshot PerformDeepCopy()
    {
        Screenshot deepCopy = (Screenshot)this.MemberwiseClone();
        deepCopy.ScreenImage = new BitmapImage(this.ScreenImage.UriSource);
        return deepCopy;
    }
}