在previous问题中,我描述了一个问题,我无法弄清楚如何记住并在SelectedItem
上设置ListView
。问题是,当我从ViewModel设置SelectedItem
时,ListView不会将SelectedItem
显示为突出显示。
这个问题仍然没有答案,所以我试图用一个小例子来重现这个问题。令我惊讶的是,我刚刚了解到,当我创建原始所选项目(我存储在我的ViewModel中)的副本时,问题就解决了,并将其设置为所选项目。如果我设置了已设置的完全相同的对象,则listview将不会显示所选项目。
我有一个ListView
,显示绑定到ItemSource
的数据项。 SelectedValue
也是绑定到ViewModel上的属性的数据。我有两个按钮,上一个和下一个,用于浏览不同的对象集,每个对象集包含一个不同的ObersevableCollection
,它绑定到ListView
s ItemSource
。
想法是存储在ListView
中进行的选择,以便当再次显示相同的对象集时,再次选择先前选择的项目。
下面的(丑陋的测试)代码让我到了现在的位置:
请注意;方法ShowList1CommandOnExecute()
涵盖了令人惊讶的部分。我添加了一些注释,解释了我看起来很奇怪的东西,而它似乎是让它正常工作的唯一方法。
public class MainViewModel : INotifyPropertyChanged
{
private int _index;
private Person _selectedPerson1;
private Person _selectedPerson2;
private Person _selectedPerson3;
private ObservableCollection<Person> _list1;
private ObservableCollection<Person> _list2;
private ObservableCollection<Person> _list3;
public RelayCommand ShowList1Command { get; set; }
public RelayCommand ShowList2Command { get; set; }
private Person _selectedPerson;
public Person SelectedPerson
{
get
{
switch (_index)
{
case 0:
return _selectedPerson1;
case 1:
return _selectedPerson2;
case 2:
return _selectedPerson3;
}
return null;
}
set
{
if (value != null)
{
switch (_index)
{
case 0:
_selectedPerson1 = value;
break;
case 1:
_selectedPerson2 = value;
break;
case 2:
_selectedPerson3 = value;
break;
}
}
_selectedPerson = value;
OnPropertyChanged();
}
}
private ObservableCollection<Person> _persons;
public ObservableCollection<Person> Persons
{
get { return _persons; }
set
{
_persons = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
ShowList1Command = new RelayCommand(ShowList1CommandOnExecute, ShowList1CommandOnCanExecute);
ShowList2Command = new RelayCommand(ShowList2CommandOnExecute, ShowList2CommandOnCanExecute);
_list1 = new ObservableCollection<Person>();
_list1.Add(new Person { Name = "Bas" });
_list1.Add(new Person { Name = "Anke" });
_list1.Add(new Person { Name = "Suus" });
_list2 = new ObservableCollection<Person>();
_list2.Add(new Person { Name = "Freek" });
_list2.Add(new Person { Name = "Ina" });
_list2.Add(new Person { Name = "Liam" });
_list3 = new ObservableCollection<Person>();
_list3.Add(new Person { Name = "aap" });
_list3.Add(new Person { Name = "noot" });
_list3.Add(new Person { Name = "mies" });
Persons = new ObservableCollection<Person>();
}
private void ShowList1CommandOnExecute()
{
if (_index < 3)
{
_index++;
}
else
{
_index = 0;
}
switch (_index)
{
case 0:
Persons = _list1;
if (_selectedPerson1 != null)
{
// This is what surprised me, this DOES work. Why do I need a copy of an object??
SelectedPerson = new Person(_selectedPerson1.Name);
}
break;
case 1:
Persons = _list2;
// This did work, which is why I tried the copied object (see case 0)
SelectedPerson = new Person {Name = "freek"};
break;
case 2:
Persons = _list3;
if (_selectedPerson3 != null)
{
// This will NEVER result in the selected item to be visualized as selected
// However, when you will check while debugging, the ListView DOES contain the correct selected item
SelectedPerson = _selectedPerson3;
}
break;
}
}
private bool ShowList1CommandOnCanExecute()
{
return true;
}
private void ShowList2CommandOnExecute()
{
switch (_index)
{
case 0:
SelectedPerson = new Person {Name = "bas"};
break;
case 1:
SelectedPerson = new Person {Name = "Freek"};
break;
case 2:
SelectedPerson = new Person{Name = "mieS"};
break;
}
}
private bool ShowList2CommandOnCanExecute()
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Person
:public class Person
{
public string Name { get; set; }
public Person()
{
}
public Person(string name)
{
Name = name;
}
public override bool Equals(object obj)
{
var other = obj as Person;
if (other != null)
{
return Name.ToLowerInvariant().Equals(other.Name.ToLowerInvariant());
}
return false;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
ListView
: <Grid.RowDefinitions>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ToolBar Grid.Row="0">
<Button Height="90" Width="90" Command="{Binding ShowList1Command}">Show List1</Button>
<Button Height="90" Width="90" Command="{Binding ShowList2Command}">Show List2</Button>
</ToolBar>
<ListView
x:Name="_matchingTvShowsFromOnlineDatabaseListView"
Grid.Row="1"
Grid.Column="0"
ItemsSource="{Binding Persons}"
SelectedItem="{Binding SelectedPerson, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
同样,问题:
为什么我需要创建以前SelectedItem
的副本并将该副本再次设置为选定项目,以便ListView可视化(并突出显示)ListView中的SelectedItem?
答案 0 :(得分:0)
我已经调查了你的问题,但尚未明确指出问题所在。我能说什么 你就是这样,虽然看起来什么都没有被选中,但ListView上的SelectedItem的值实际上是正确的。我没有其他结论,这是WPF中ListView / ListBox的意外行为。所以看来你确实是对的 - 这是一个错误。
现在,关于解决你的问题,我建议这就是:
视图(为简单起见,我删除了RelayCommand):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Height="90" Width="90" Click="Button_Click">Toggle List</Button>
<ListView
DataContext="{Binding Persons}"
x:Name="_matchingTvShowsFromOnlineDatabaseListView"
Grid.Row="1"
Grid.Column="0"
ItemsSource="{Binding}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Text="{Binding Index}" Grid.Row="2"/>
</Grid>
</Window>
ViewModel:
public class MainViewModel : INotifyPropertyChanged
{
private int _index;
public int Index
{
get { return _index; }
set
{
_index = value;
OnPropertyChanged();
}
}
private SelectionCollection<Person> _list1;
private SelectionCollection<Person> _list2;
private SelectionCollection<Person> _list3;
private SelectionCollection<Person> _persons;
public SelectionCollection<Person> Persons
{
get { return _persons; }
set
{
_persons = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
_list1 = new SelectionCollection<Person>();
_list1.Add(new Person { Name = "Bas" });
_list1.Add(new Person { Name = "Anke" });
_list1.Add(new Person { Name = "Suus" });
_list2 = new SelectionCollection<Person>();
_list2.Add(new Person { Name = "Freek" });
_list2.Add(new Person { Name = "Ina" });
_list2.Add(new Person { Name = "Liam" });
_list3 = new SelectionCollection<Person>();
_list3.Add(new Person { Name = "aap" });
_list3.Add(new Person { Name = "noot" });
_list3.Add(new Person { Name = "mies" });
Persons = new SelectionCollection<Person>();
}
public void ShowList1CommandOnExecute()
{
if (Index < 2)
{
Index++;
}
else
{
Index = 0;
}
switch (Index)
{
case 0:
Persons = _list1;
break;
case 1:
Persons = _list2;
break;
case 2:
Persons = _list3;
break;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
最后,新推出的系列也可以跟踪选择:
public class SelectionCollection<T> : ObservableCollection<T>, INotifyPropertyChanged
{
private T _selectedItem;
public T SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
请注意,这也可以通过使用CollectionViews来完成,但就个人而言,我更喜欢在ViewModel层中没有这些。
另请注意,_index字段已转换为属性,只允许我绑定它,因为我对它感到困惑,范围在0-3之间,而不是0-2(也固定在上面)。
答案 1 :(得分:0)
游戏开始有点晚,但是我一直在跳铁圈以类似的方式解决这个问题。 使用Viewmodel中的绑定属性在ListView中设置SelectedItem或使用绑定的SelectedIndex设置类似项将不起作用。 直到我尝试使其异步:
Task.Factory.StartNew(() =>
{
BoundSelectedIndex = index;
});
似乎可以正常工作-更高级的贡献者可能会回答原因...