INPC没有从“驱动”ObservableCollection的setter中触发多个属性

时间:2013-08-09 19:14:26

标签: c# wpf mvvm observablecollection inotifypropertychanged

我的Senario:我有两个ListBoxes绑定到两个不同的ObervableCollections - 一组形状和一组颜色,用户可以从中选择以查找具有匹配属性条件的零件号。 (我的应用程序中存在更多属性集合,但为了清楚起见,我省略了它们。)

从包含两个属性值的列表框中的任何一个中选择后,我会在名为ResultingPNsIntersect的集合中收集拥有所选属性属性的结果部件号。 (结果部件号的集合显示在第三个ListBox中。)

应该发生什么:在两个列表框中的任何一个中进行选择后,所得到的部分编号与“SelectedAttribute”的交集应该更新,以便仅相关部件号仍然存在。如果已选择形状,则必须更新包含ColorsCollection的列表框,以便仅在第二个列表框中显示与具有ColorsCollection的PartNumbers相关的颜色属性(在SelectedShape中)。 / p>

我的问题:选择形状后,ResultingPNsIntersect ObservableCollection会更新,但ColorsCollection的PropertyChanged通知永远不会被触发,因此第二个列表框永远不会更新以提供给用户更新的颜色属性可供选择。

我之前在其他应用中做过这一点没有任何问题。我认为没有必要订阅CollectionChanged因为我没有在ResultingPNsIntersect中编辑属性值 - 我正在用新值替换集合。请帮助我看看我的代码失败的地方以及为什么以便我能更好地理解INPC需要解雇的条件。

xaml绑定:

<ListBox x:Name="SelectFromAvailableShapesLB" DockPanel.Dock="Top"  
         ItemsSource="{Binding AvailableShapesCollection}" 
         DisplayMemberPath="AttVal" 
         SelectedItem="{Binding SelectedShape, Mode=TwoWay}"/>


<ListBox x:Name="SelectFromAvailableColorsLB" DockPanel.Dock="Top"  
         ItemsSource="{Binding AvailableColorsCollection}" 
         DisplayMemberPath="AttVal" 
         SelectedItem="{Binding SelectedColor, Mode=TwoWay}"/>


<ListBox x:Name="PnsResultingFromAttributeSelectionsLB" DockPanel.Dock="Top"  
         ItemsSource="{Binding ResultingPNsIntersect}" 
         DisplayMemberPath="PartNum" 
         SelectedItem="{Binding SelectedPartNum, Mode=TwoWay}"/>

我的ViewModel:

    public ObservableCollection<AttributeValuesLibrary> AvailableShapesCollection
    {
        get
        {
            if (_resultingPNsIntersect != null)
            {
                foreach (PartNumber shape in _resultingPNsIntersect.Where(x => x.ShapeID != null))
                {
                    if (!_availableShapesCollection.Contains(shape.AttributeValuesLibrary_Shape))
                    {
                        this._availableShapesCollection.Add(shape.AttributeValuesLibrary_Shape);
                    }
                }

            }
            return _availableShapesCollection;
        }
        set
        {
            if (_availableShapesCollection != value)
            {
                this._availableShapesCollection = value;
                RaisePropertyChanged("AvailableShapesCollection");
            }
        }
    }


    public ObservableCollection<AttributeValuesLibrary> AvailableColorsCollection
    {
        get
        {
            if (_resultingPNsIntersect != null)
            {
                foreach (PartNumber color in _resultingPNsIntersect.Where(x => x.ColorID != null))
                {
                    if (!_availableColorsCollection.Contains(color.AttributeValuesLibrary_Color))
                    {
                        _availableColorsCollection.Add(color.AttributeValuesLibrary_Color);
                    }
                }

            }
            return _availableColorsCollection;
        }
        set
        {
            if (_availableColorsCollection != value)
            {
                _availableColorsCollection = value;
                RaisePropertyChanged("AvailableColorsCollection");
            }
        }
    }

    public AttributeValuesLibrary SelectedShape
    {
        get
        {
            return _selectedShape;
        }
        set
        {
            if (_selectedShape != value)
            {
                _selectedShape = value;
                RaisePropertyChanged("SelectedShape");
                RaisePropertyChanged("ResultingPNsIntersect");
            }

        }
    }


    public ObservableCollection<ConnectorPartNumber> ConnAttPNResults
    {
        get
        {
            // If a shape has been selected, we need to navigate to it's related PartNumbers and add those to the intersection
            // contained by ResultingPNsIntersection. 
            if (_selectedShape != null)
            {
                var shapeResults = _context.PartNumbers.Where(x => x.AttributeValuesLibrary_Shape.AttValID == _selectedShape.AttValID);

                if (_resultingPNsIntersect != null)
                {
                    var resultsFromPreviousSelection = _resultingPNsIntersect;
                    _resultingPNsIntersect = new ObservableCollection<PartNumber>(resultsFromPreviousSelection.Intersect(shapeResults));
                }
                else if (_resultingPNsIntersect == null)
                {
                    _resultingPNsIntersect = new ObservableCollection<PartNumber>(shapeResults);
                }
              }
            return _resultingPNsIntersect;
        }
        set
        {
            if (_resultingPNsIntersect != value)
            {
                this._resultingPNsIntersect = value;
                RaisePropertyChanged("ResultingPNsIntersect");
                RaisePropertyChanged("AvailableColorsCollection");  <--Not firing!!!!!

            }


        }
    }

提前致谢!

:: UPDATE ::如果我将RaisePropertyChanged("AvailableColorsCollection")放入SelectedShape的setter中,我可以强制执行此操作。但是当然,它没那么有意义,因为AvailableColorsCollection依赖于ResultingPNsIntersect Collection,它根据属性列表框中的选择而变化。

1 个答案:

答案 0 :(得分:1)

我认为一般来说,你的方法会给你带来问题。您应该考虑使您的属性更加“笨拙”,而不是在属性的getter中实现逻辑。我会将这些逻辑从属性中移到一个修改可用选项的辅助方法中:

private readonly ObservableCollection<AttributeValuesLibrary> _availableShapesCollection =
    new ObservableCollection<AttributeValuesLibrary>();
private readonly ObservableCollection<AttributeValuesLibrary> _availableColorsCollection =
    new ObservableCollection<AttributeValuesLibrary>();

public ObservableCollection<AttributeValuesLibrary> AvailableShapesCollection
{
    get { return _availableShapesCollection; }
}

public ObservableCollection<AttributeValuesLibrary> AvailableColorsCollection
{
    get { return _availableColorsCollection; }
}

public AttributeValuesLibrary SelectedShape
{
    get { return _selectedShape; }
    set
    {
        if (_selectedShape != value)
        {
            _selectedShape = value;
            RaisePropertyChanged("SelectedShape");
            SelectedShapeChanged();
        }
    }
}

public ObservableCollection<ConnectorPartNumber> ConnAttPNResults
{
    get { return _resultingPNsIntersect; }
    set
    {
        if (_resultingPNsIntersect != value)
        {
            this._resultingPNsIntersect = value;
            RaisePropertyChanged("ResultingPNsIntersect");
            UpdateAvailableOptions();
        }
    }
}

private void SelectedShapeChanged()
{
    // If a shape has been selected, we need to navigate to it's related 
    // PartNumbers and add those to the intersection contained by ResultingPNsIntersection. 
    if (_selectedShape != null)
    {
        var shapeResults = _context.PartNumbers.Where(x => x.AttributeValuesLibrary_Shape.AttValID == _selectedShape.AttValID);

        if (_resultingPNsIntersect != null)
        {
            var resultsFromPreviousSelection = _resultingPNsIntersect;
            ConnAttPNResults = new ObservableCollection<PartNumber>(resultsFromPreviousSelection.Intersect(shapeResults));
        }
        else
        {
            ConnAttPNResults = new ObservableCollection<PartNumber>(shapeResults);
        }
    }
}

private void UpdateAvailableOptions()
{
    if (_resultingPNsIntersect != null)
    {
        _availableColorsCollection.Clear();
        _availableShapesCollection.Clear();

        foreach (PartNumber color in _resultingPNsIntersect.Where(x => x.ColorID != null).Distinct())
        {
            _availableShapesCollection.Add(color.AttributeValuesLibrary_Color);
        }

        foreach (PartNumber shape in _resultingPNsIntersect.Where(x => x.ShapeID != null).Distinct())
        {
            shapes.Add(shape.AttributeValuesLibrary_Shape);
        }
    }
}

如果您希望拥有可设置的属性,UpdateAvailableOptions可以创建新的集合并设置AvailableColorsCollection&amp;新实例的AvailableShapesCollection属性(但不需要ObservableCollection)。

我甚至会更进一步。由于您不希望任何人更改您的可用集合,因此我会让它们返回ReadonlyObersvableCollection个实例,并使其返回类型为IEnumerable<T>