У меня есть ComboBox
, который позволяет пользователю выбрать категорию, и ListView
, который связан с ObservableCollection
элементов в выбранной категории. Когда пользователь выбирает другую категорию, элементы в коллекции обновляются. Иногда это работает, как и ожидалось, но иногда список пунктов искажен. Показывает дубликат элемента, когда должно быть два отдельных элемента.
Результаты зависят от категории, из которой я переключаюсь. Например, если я переключаюсь с категории без элементов на категорию с двумя элементами, один и тот же элемент отображается дважды. Но если я переключаюсь с категории с четырьмя предметами на ту же категорию с двумя предметами, они отображаются правильно.
Вот ответ:
MainPage.xaml
<Page
x:Class="ListViewDuplicateItem_Binding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ListViewDuplicateItem_Binding">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox
Grid.Row="0"
Grid.Column="0"
ItemsSource="{Binding ViewModel.Groups}"
SelectedItem="{Binding ViewModel.SelectedGroup, Mode=TwoWay}" />
<ListView
Grid.Row="1"
Grid.Column="0"
ItemsSource="{Binding ViewModel.Widgets}"
SelectedItem="{Binding ViewModel.SelectedWidget, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Widget">
<TextBlock Text="{Binding Id}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<local:MyControl
Grid.Row="1"
Grid.Column="1"
Text="{Binding ViewModel.SelectedWidget.Id, Mode=OneWay}" />
</Grid>
</Page>
MainPage.xaml.cs
using Windows.UI.Xaml.Controls;
namespace ListViewDuplicateItem_Binding
{
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
DataContext = this;
}
public MainViewModel ViewModel { get; } = new MainViewModel();
}
}
MainViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
namespace ListViewDuplicateItem_Binding
{
public class MainViewModel : INotifyPropertyChanged
{
private string _selectedGroup;
private Widget _selectedWidget;
public MainViewModel()
{
PropertyChanged += HomeViewModel_PropertyChanged;
SelectedGroup = Groups.First();
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> Groups { get; } = new ObservableCollection<string>(DataSource.AllGroups);
public string SelectedGroup
{
get => _selectedGroup;
set
{
if (_selectedGroup != value)
{
_selectedGroup = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedGroup)));
}
}
}
public Widget SelectedWidget
{
get => _selectedWidget;
set
{
if (_selectedWidget != value)
{
_selectedWidget = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedWidget)));
}
}
}
public ObservableCollection<Widget> Widgets { get; } = new ObservableCollection<Widget>();
private void HomeViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SelectedGroup))
{
var widgetsToLoad = DataSource.GetWidgetsForGroup(SelectedGroup);
// Add widgets in this group
widgetsToLoad.Except(Widgets).ToList().ForEach(w => Widgets.Add(w));
// Remove widgets not in this group
Widgets.Except(widgetsToLoad).ToList().ForEach(w => Widgets.Remove(w));
// Select the first widget
if (SelectedWidget == null && Widgets.Any())
{
SelectedWidget = Widgets.First();
}
}
}
}
}
DataSource .cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace ListViewDuplicateItem_Binding
{
public static class DataSource
{
public static ObservableCollection<string> AllGroups { get; } = new ObservableCollection<string>
{
"First Widget",
"First Two Widgets",
"Last Two Widgets",
"All Widgets",
"None"
};
public static List<Widget> AllWidgets { get; } = new List<Widget>
{
new Widget()
{
Id = 1,
},
new Widget()
{
Id = 2,
},
new Widget()
{
Id = 3,
},
new Widget()
{
Id = 4,
}
};
public static List<Widget> GetWidgetsForGroup(string group)
{
switch (group)
{
case "First Widget":
return new List<Widget> { AllWidgets[0] };
case "First Two Widgets":
return new List<Widget> { AllWidgets[0], AllWidgets[1] };
case "Last Two Widgets":
return new List<Widget> { AllWidgets[2], AllWidgets[3] };
case "All Widgets":
return new List<Widget>(AllWidgets);
default:
return new List<Widget>();
}
}
}
}
Widget.cs
namespace ListViewDuplicateItem_Binding
{
public class Widget
{
public int Id { get; set; }
}
}
MyControl.xaml
<UserControl
x:Class="ListViewDuplicateItem_Binding.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Text="{x:Bind Text, Mode=TwoWay}" />
</UserControl>
MyControl.xaml.cs
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ListViewDuplicateItem_Binding
{
public sealed partial class MyControl : UserControl
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(MyControl), new PropertyMetadata(null));
public MyControl()
{
InitializeComponent();
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
}