2016-10-15 3 views
0

У меня возникла очень сложная проблема со связыванием. Приложенное приложение чрезвычайно просто, как вы можете видеть, и это уменьшенное воспроизведение исходного кода, в котором появляется ошибка.
Чтобы воспроизвести проблему, запустите приложение и нажмите 3 кнопки с помощью значка +, начиная слева направо. Эти кнопки добавят 3 элемента в коллекцию. Затем нажмите 4-ю кнопку, чтобы перейти на вторую страницу. На второй странице выберите элемент TEA из выпадающего списка. Вернитесь на главную страницу и нажмите последнюю кнопку справа, которая добавит CAPPUCCINO. Вы получите исключение Value does not fall within expected range. Я хотел бы знать, почему это происходит, а не только обходное решение, которое не может быть реальным решением проблемы. Как видите, проблема возникает при очень специфических обстоятельствах.
Примечания:Связывание ComboBox с группированной коллекцией

  • устраняющие статической переменной и прохождения экземпляра коллекции в Navigate вызове на вторую страницу не решить эту проблему
  • вместо Add вызова была вставка в порядке, который называется Insert на Коллекция. Я удалил его и Insert не проблема, ни решение
  • удаления CollectionViewSource на второй странице не решает проблему
  • создавая еще одну коллекции зеркальной GroupByLetter2 для выпадающего списка не исправляет проблему (см test1 филиал)

UPDATE: в конце концов, я был в состоянии установить 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; 
    } 
+3

Прошу прощения, но, пожалуйста, напишите свой код здесь. Мы (я) не желаем загружать и извлекать zip-файл и проходить через ваше приложение, чтобы помочь решить вашу проблему. Ограничьте его и разместите соответствующие строки. –

+0

переехал на github ... –

ответ

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.

+0

спасибо за ваш ответ, дайте мне немного времени, чтобы проверить его на исходный код. Об обходном пути установки cvs в null, Это не имеет смысла для меня, потому что вторая страница должна быть уничтожена (я даже пытался сделать это явно). Но, возможно, это не gc'ed. Моя гипотеза заключается в том, что выбор элемента из ComboBox нарушает что-то внутри коллекции, и это необратимо, поэтому невозможно сделать это с помощью одной коллекции. –

+0

обновленный вопрос с новым тестом –

+0

Создав новую модель данных, вы можете не только создать новую коллекцию, но и создать новую коллекцию с новыми объектами внутри. Я проверил ваше новое демо в ветке теста 1. Вы добавили один и тот же продукт в две разные коллекции, что приводит к тому, что две коллекции совместно используют одни и те же дочерние элементы внутри. (В C# переменная объекта является ссылочным типом, передавая объектную переменную, вы передаете ссылку). –