Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/docker/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 可观察收集。清除留下的幻影元素_C#_Wpf_Observablecollection - Fatal编程技术网

C# 可观察收集。清除留下的幻影元素

C# 可观察收集。清除留下的幻影元素,c#,wpf,observablecollection,C#,Wpf,Observablecollection,我有一组转换器,用于在画布上动态定位正方形。正方形存储在可观察的集合中CanvasPositionScaleConverter将范围内的值转换为0到1之间的值。如果要转换的值超出指定范围,它将引发参数异常 我的问题是,当我清除我的方块集合时,屏幕变空,“幻影”元素似乎被留在后面,转换器继续作用。因此,在调整画布大小时,即使已清除squares集合,仍会引发异常 为什么转换器仍在通过Squares.Clear()删除的这些元素上运行 注 使用Squares=newobservedcollecti

我有一组转换器,用于在画布上动态定位正方形。正方形存储在可观察的集合中<代码>CanvasPositionScaleConverter将范围内的值转换为0到1之间的值。如果要转换的值超出指定范围,它将引发参数异常

我的问题是,当我清除我的方块集合时,屏幕变空,“幻影”元素似乎被留在后面,转换器继续作用。因此,在调整画布大小时,即使已清除squares集合,仍会引发异常

为什么转换器仍在通过
Squares.Clear()
删除的这些元素上运行


使用
Squares=newobservedcollection()
而不是
Squares.Clear()
仍然会导致此问题


更新

如果从未添加过
Sqaures
集合,并且按下了
Resize
,则不会发生此问题。仅当元素已从集合中删除(显然,如果元素尚未从集合中删除,但超出了有效范围),才会发生这种情况

例外情况详细信息如下所示

Exception thrown: 'System.ArgumentException' in LateExceptionMcve.dll
An unhandled exception of type 'System.ArgumentException' occurred in LateExceptionMcve.dll
Value cannot be greater than max

MCVE

MainWindow.xaml


MainWindow.xaml.cs

使用系统;
使用System.Collections.ObjectModel;
使用系统组件模型;
利用制度全球化;
使用System.Runtime.CompilerServices;
使用System.Windows;
使用System.Windows.Data;
命名空间LateExceptionMcve
{
公共部分类主窗口:INotifyPropertyChanged
{
公共主窗口()
{
初始化组件();
DataContext=this;
最小值=0;
最大值=10;
尺寸=最大值-最小值;
正方形=新的可观测集合
{
新广场
{
X=8,
Y=7,
},
};
}
公共事件属性更改事件处理程序属性更改;
私有void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
私有void Resize(对象发送器,RoutedEventArgs e)
{
最小值=0;
最大值=5;
尺寸=最大值-最小值;
}
私有无效清除(对象发送方,路由目标)
{
正方形。清除();
}
私人可观测集合广场;
公众可观测集合广场
{
get=>\u平方;
设置
{
_平方=值;
OnPropertyChanged();
}
}
私人双倍大小;
公共双码
{
获取=>\u大小;
设置
{
_大小=值;
OnPropertyChanged();
}
}
私人双(u min),;
公共双分钟
{
get=>\u min;
设置
{
_最小值=数值;
OnPropertyChanged();
}
}
私人双卡最大;
公共双最大值
{
get=>\u max;
设置
{
_最大值=最大值;
OnPropertyChanged();
}
}
}
公共密封类广场:INOTIFYPROPERTYCHANGE
{
公共事件属性更改事件处理程序属性更改;
私有void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
私人双x;
公共双X
{
get=>\ux;
设置
{
_x=值;
OnPropertyChanged();
}
}
私人双人房;
公共双Y
{
get=>\u y;
设置
{
_y=值;
OnPropertyChanged();
}
}
}
公共密封类CanvasScaleConverter:IValueConverter
{
私人常数双刻度=100;
公共对象转换(
对象值,
类型targetType,
对象参数,
文化信息(文化)
{
如果(值为双值缩放)
{
返回值
<Window x:Class="LateExceptionMcve.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LateExceptionMcve"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance Type=local:MainWindow, IsDesignTimeCreatable=True}"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Viewbox Grid.Column="0"
                 Grid.ColumnSpan="2"
                 Grid.Row="0"
                 Stretch="Uniform">
            <Border BorderThickness="1"
                    BorderBrush="Black">
                <Border.Resources>
                    <local:CanvasScaleConverter x:Key="CanvasScaleConverter" />
                    <local:CanvasPositionScaleConverter x:Key="CanvasPositionScaleConverter" />
                </Border.Resources>
                <ItemsControl ItemsSource="{Binding Squares}"
                                  Width="{Binding Size, Converter={StaticResource CanvasScaleConverter}}"
                                  Height="{Binding Size, Converter={StaticResource CanvasScaleConverter}}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Canvas />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemContainerStyle>
                        <Style TargetType="ContentPresenter">
                            <Setter Property="Canvas.Left">
                                <Setter.Value>
                                    <MultiBinding Converter="{StaticResource CanvasPositionScaleConverter}">
                                        <Binding Path="X" />
                                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" 
                                                     Path="DataContext.Min" />
                                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" 
                                                     Path="DataContext.Max" />
                                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" 
                                                     Path="DataContext.Size"
                                                     Converter="{StaticResource CanvasScaleConverter}" />
                                    </MultiBinding>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="Canvas.Top">
                                <Setter.Value>
                                    <MultiBinding Converter="{StaticResource CanvasPositionScaleConverter}">
                                        <Binding Path="Y" />
                                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" 
                                                     Path="DataContext.Min" />
                                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" 
                                                     Path="DataContext.Max" />
                                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" 
                                                     Path="DataContext.Size"
                                                     Converter="{StaticResource CanvasScaleConverter}" />
                                    </MultiBinding>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="Width" Value="50" />
                            <Setter Property="Height" Value="50" />
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid Background="Red" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Border>
        </Viewbox>

        <Button Grid.Column="0"
                Grid.Row="1"
                Content="Clear"
                Click="Clear" />

        <Button Grid.Column="1"
                Grid.Row="1"
                Content="Resize"
                Click="Resize" />
    </Grid>
</Window>
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;

namespace LateExceptionMcve
{
    public partial class MainWindow : INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            Min = 0;
            Max = 10;
            Size = Max - Min;
            Squares = new ObservableCollection<Square>
            {
                new Square
                {
                    X = 8,
                    Y = 7,
                },
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void Resize(object sender, RoutedEventArgs e)
        {
            Min = 0;
            Max = 5;
            Size = Max - Min;
        }

        private void Clear(object sender, RoutedEventArgs e)
        {
            Squares.Clear();
        }

        private ObservableCollection<Square> _squares;

        public ObservableCollection<Square> Squares
        {
            get => _squares;
            set
            {
                _squares = value;
                OnPropertyChanged();
            }
        }

        private double _size;

        public double Size
        {
            get => _size;
            set
            {
                _size = value;
                OnPropertyChanged();
            }
        }

        private double _min;

        public double Min
        {
            get => _min;
            set
            {
                _min = value;
                OnPropertyChanged();
            }
        }

        private double _max;

        public double Max
        {
            get => _max;
            set
            {
                _max = value;
                OnPropertyChanged();
            }
        }
    }

    public sealed class Square : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private double _x;

        public double X
        {
            get => _x;
            set
            {
                _x = value;
                OnPropertyChanged();
            }
        }

        private double _y;

        public double Y
        {
            get => _y;
            set
            {
                _y = value;
                OnPropertyChanged();
            }
        }
    }

    public sealed class CanvasScaleConverter : IValueConverter
    {
        private const double Scale = 100;

        public object Convert(
            object value,
            Type targetType,
            object parameter,
            CultureInfo culture)
        {
            if (value is double valueToScale)
            {
                return valueToScale * Scale;
            }

            return value;
        }

        public object ConvertBack(
            object value,
            Type targetType,
            object parameter,
            CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

    public sealed class CanvasPositionScaleConverter : IMultiValueConverter
    {
        public object Convert(
            object[] values,
            Type targetType,
            object parameter,
            CultureInfo culture)
        {
            if (values.Length == 4 &&
                values[0] is double value &&
                values[1] is double min &&
                values[2] is double max &&
                values[3] is double canvasWidthHeight)
            {
                return canvasWidthHeight * RangeToNormalizedValue(min, max, value);
            }

            return values;
        }

        private static double RangeToNormalizedValue(
            double min,
            double max,
            double value)
        {
            if (min > max)
            {
                throw new ArgumentException("Min cannot be less than max");
            }

            if (value < min)
            {
                throw new ArgumentException("Value cannot be less than min");
            }

            if (value > max)
            {
                throw new ArgumentException("Value cannot be greater than max");
            }

            return (value - min) / (max - min);
        }

        public object[] ConvertBack(
            object value,
            Type[] targetTypes,
            object parameter,
            CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}
private void Clear(object sender, RoutedEventArgs e)
{
    Squares.Clear();
    Dispatcher.BeginInvoke(new Action(GC.Collect), DispatcherPriority.ContextIdle);
}
public sealed class CanvasPositionScaleConverter : IMultiValueConverter
{
    public object Convert(
        object[] values,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        if (values.Length != 4 ||
            !(values[0] is double value) ||
            !(values[1] is double min) ||
            !(values[2] is double max) ||
            !(values[3] is double canvasWidthHeight))
        {
            return null;
        }

        try
        {
            return canvasWidthHeight * RangeToNormalizedValue(min, max, value);
        }
        catch (ArgumentException e) when (e.Message == ValueLessThanMin)
        {
            return 0;
        }
        catch (ArgumentException e) when (e.Message == ValueGreaterThanMax)
        {
            return canvasWidthHeight;
        }
    }

    // This is in a utility class normally
    private const string ValueLessThanMin = "Value cannot be less than min";
    private const string ValueGreaterThanMax = "Value cannot be greater than max";

    private static double RangeToNormalizedValue(
        double min,
        double max,
        double value)
    {
        if (min > max)
        {
            throw new ArgumentException("Min cannot be less than max");
        }

        if (value < min)
        {
            throw new ArgumentException(ValueLessThanMin);
        }

        if (value > max)
        {
            throw new ArgumentException(ValueGreaterThanMax);
        }

        return (value - min) / (max - min);
    }

    public object[] ConvertBack(
        object value,
        Type[] targetTypes,
        object parameter,
        CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
        <Setter Property="Width" Value="50" />
        <Setter Property="Height" Value="50" />
        <EventSetter Event="Unloaded" Handler="ContentPresenter_Unloaded"/>
    </Style>
</ItemsControl.ItemContainerStyle>
    public double Max
    {
        get => _max;
        set
        {
            _max = value;
            OnPropertyChanged();
        }
    }

    private void ContentPresenter_Unloaded(object sender, RoutedEventArgs e)
    {

    }
}