c# - 我的 ListView 两次显示相同的项目。我如何解决它?
问题描述
我有一个ComboBox
允许用户选择一个类别和一个ListView
绑定到ObservableCollection
所选类别中的项目的一个。当用户选择不同的类别时,集合中的项目会更新。有时这会按预期工作,但有时项目列表会被破坏。当应该有两个单独的项目时,它会显示一个重复的项目。
结果似乎取决于我从哪个类别切换。例如,如果我从没有项目的类别切换到有两个项目的类别,相同的项目会显示两次。但是,如果我从具有四个项目的类别切换到具有两个项目的同一类别,它们就会正确显示。
这是一个复制品:
主页.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();
}
}
主视图模型.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();
}
}
}
}
}
数据源.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>();
}
}
}
}
小部件.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); }
}
}
}
解决方案
这似乎仅在项目包含使用绑定标记的自定义控件时发生。
在上面的示例中,如果MyControl
从 中删除MainPage.xaml
,它会按预期工作。
同样,如果<local:MyControl Text="{Binding ViewModel.SelectedWidget.Id}" />
更改为<local:MyControl Text="{x:Bind ViewModel.SelectedWidget.Id}" />
,则示例按预期工作
这似乎是ListView
控件中的一个错误,但您可以通过使用{x:Bind} 编译的绑定来解决它。
编辑:经过进一步调查,自定义控件可能是一个红鲱鱼。将自定义控件更改为标准 TextBox 并不能解决我之前认为的问题。该问题可以在没有自定义控件的情况下重现。尽管如此,使用 {x:Bind} 或完全删除控件确实可以解决这种情况下的问题。
推荐阅读
- scala - Kubernetes kafka - lagom - 消费者在超时后因 WakeupException 而中断。消息:空
- ios - view.addSubview(: ) Not working in swift playgrounds
- asp.net-core - Automapper v8 不忽略 EF(实体框架)类的导航属性
- arrays - 在 VBA 中存储字符串并输出到多个单元格
- sql - 将商或更长的表达式分配给局部(实)变量 T SQL
- json - 快速解析 JSON。解析json文件的最佳结构是什么
- javascript - 单击按钮添加到 field_tag rails jQuery
- facebook-graph-api - 评论计数 API Facebook Restfb Java
- firebase - 使用 GCP VPN 保护 Firebase 托管网站
- c# - 使用 PrintDialog 打印功能区