将 ObservableCollection 彼此分开

Separate ObservableCollections from each other

信息

我有两个 ObservableCollection 相同的 Screenshot 对象。一种称为 Screenshots,它包含用户可用的所有可能屏幕,一种称为 SelectedScreenshots,它是用户想要组合的选定屏幕。每次用户通过单击在 WPF 中绑定到 Screenshots

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

问题

问题是,当我刷新屏幕并清除 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);
    }
}

截图class:

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

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

解决方案是在刷新之前将 selected 项目保存到另一个集合并在刷新之后重新select,或者只更新现有的一组屏幕截图。

我预计您会希望在 UI 中保留 selected 的屏幕截图。为了实现这一点,您需要以某种方式 link 旧的屏幕截图实例到新的。希望您可以使用 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)));
    });
}

如果您的 Screenshot 对象具有引用类型的属性,那么我认为您需要按照您说的去做;执行深拷贝以确保 Screenshots 属性 中的 Screenshot 对象和 SelectedScreenshots 属性 中的 Screenshot 对象不指向相同的内存位置。过去,我在对象中创建了一个 PerformDeepCopy() 方法,在您的例子中是 Screenshot 对象。在此方法中,您可以执行 MemberwiseClone(),这是一个浅拷贝。这将在您的屏幕截图 class 中复制名称 属性。然后您将手动为引用类型分配一个新的内存地址。这是 BitmapImage 属性 是 "newed up" 的地方。 return 值是一个新的 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;
    }
}