ComboBox绑定到分组集合

时间:2016-10-15 16:45:19

标签: c# binding uwp observablecollection collectionviewsource

我面临一个非常困难的绑定问题。附带的应用程序非常简单,因为你可以看到并且是错误出现的原始代码的简化再现。
要重现问题,启动应用程序并按下 + <的3个按钮/ strong>图标从左到右。这些按钮将在集合中添加3个项目。然后按第4个按钮导航到第二页。在第二页上,从组合框中选择 TEA 元素。返回主页面并按右侧的最后一个按钮,将 CAPPUCCINO 产品添加到列表中。您将获得Value does not fall within expected range例外。我想知道为什么会发生这种情况,而不仅仅是一种解决方案,它不能解决问题。如您所见,在非常具体的情况下会出现问题。
注意:

  • 删除静态变量并将Navigate调用中的集合实例传递给第二页无法解决问题
  • 代替Add调用,按顺序插入了一个名为Insert的集合。我删除了它,Insert不是问题,也不是解决方案
  • 删除第二页上的CollectionViewSource无法解决问题
  • 为组合框创建另一个镜面集合GroupByLetter2无法解决问题(请参阅 test1 分支)

更新:最后我能够在CollectionViewSource.Source = null事件上设置UserControl.Unloaded并修复它。但问题仍然是关于工作理论的问题。


https://github.com/albertorivelli/app1

这是主页:

<Page.Resources>
    <CollectionViewSource x:Name="cvsProductsLetter" IsSourceGrouped="true" />
</Page.Resources>

<ListView x:Name="gwProducts" ItemsSource="{Binding Source={StaticResource cvsProductsLetter}}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}" FontSize="20" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

代码隐藏中填充了CollectionViewSource

public sealed partial class MainPage : Page
{
    public static ProductCollection _productcollection;

    public MainPage()
    {
        this.InitializeComponent();

        NavigationCacheMode = NavigationCacheMode.Enabled;

        _productcollection = new App1.ProductCollection();

        cvsProductsLetter.Source = _productcollection.GroupByLetter;
    }

这是ProductCollection类:

public class ProductCollection
{
    public ObservableCollection<ProductGroup> _groupsletter;

    public ObservableCollection<ProductGroup> GroupByLetter
    {
        get
        {
            if (_groupsletter == null)
            {
                _groupsletter = new ObservableCollection<ProductGroup>();
            }

            return _groupsletter;
        }
    }

    public void Add(Product newitem)
    {
        AddToLetterGroup(newitem);
    }

    private void AddToLetterGroup(Product item)
    {
        int i;
        ProductGroup prodgr = null;

        // get group from letter
        for(i = 0; i < _groupsletter.Count; i++)
        {
            if (String.Equals(_groupsletter[i].Key, item.Name[0].ToString(), StringComparison.CurrentCultureIgnoreCase))
            {
                prodgr = _groupsletter[i];
                break;
            }
        }

        //new letter
        if (prodgr == null)
        {
            prodgr = new ProductGroup();
            prodgr.Key = item.Name[0].ToString();
            prodgr.Add(item);
            _groupsletter.Add(prodgr);
        }
        else
        {
            prodgr.Add(item);
        }
    }
}

public class ProductGroup : ObservableCollection<Product>
{
    public string Key { get; set; }
}

..和Product

public class Product : INotifyPropertyChanged
{
    private string _name = "";

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get { return _name; }
        set
        {
            if (!String.Equals(_name, value))
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    public override string ToString()
    {
        return Name;
    }
}

这是组合框的第二页:

<Page.Resources>
    <CollectionViewSource x:Name="cvsProductsLetter" IsSourceGrouped="true" />
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ComboBox ItemsSource="{Binding Source={StaticResource cvsProductsLetter}}" FontSize="20" Foreground="Black" />
</Grid>

public sealed partial class SecondPage : Page
{
    public SecondPage()
    {
        this.InitializeComponent();
        cvsProductsLetter.Source = App1.MainPage._productcollection.GroupByLetter;
    }

1 个答案:

答案 0 :(得分:1)

  

返回主页面并按右侧的最后一个按钮,将CAPPUCCINO产品添加到列表中。您将获得一个不在预期范围异常范围内的值。

我可以在您的演示中重现此问题。我做了一些测试,发现在第二页上选择 TEA ,然后返回MainPage,只有 T 组可以毫无例外地添加。当您选择 CAPPUCCINO 并重复这些步骤时, C 组不会获得异常,其他人则会。

我想这是因为你在页面之间共享相同的数据模型对象。有关详细的根本原因,我需要在内部进行咨询。

目前最简单的解决方法是清空第二页cvsProductsLetter中的OnNavigatingFrom

SecondPage.xaml.cs:

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    cvsProductsLetter.Source = null;
}

但解决此问题的推荐方法是为第二个页面创建一个单独的新数据模型:

ProductCollection.cs:

public class ProductCollection
{
  ...
    public void CreateNewGroupByLetter(ObservableCollection<ProductGroup> oldGroupByLetter)
    {
        if (oldGroupByLetter != null&&this.GroupByLetter!=null)//add this.GroupByLetter!=null to call the setter of GroupByLetter.
        {
            foreach (var group in oldGroupByLetter)
            {
                foreach (var product in group)
                {
                    Add(new Product {
                         Name=product.Name
                    });
                }
            }
        }
    }
}

和MainPage.xaml.cs:

private void btnProdNavigate_Click(object sender, RoutedEventArgs e)
{
    ProductCollection newColl = new ProductCollection();
    newColl.CreateNewGroupByLetter(_productcollection.GroupByLetter);
    this.Frame.Navigate(typeof(SecondPage), newColl);
}

<强>更新

以下是修改演示的链接:App1