我有一个场景,可以用不同的方式查看相同的项目集合。也就是说,我们对同一数据有多个可视化表示。为了使我们的应用程序在视觉上清洁,您一次只能查看其中一个视图。我遇到的问题是,如果您在查看View#1时更改所选项目,那么当您切换到View#2时,所选项目无法正确更新。
我的再现步骤:
似乎在列表框折叠时,选择更改未被选中。我错过了什么?是否期望PropertyChanged事件不会更新UI元素(如果它们不可见)?
我的代码有一个非常简化的版本。基本上,我有一个共享数组绑定到两个不同的ListBox控件。
XAML:
<Window x:Class="SharedListBindingExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SharedListBindingExample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listBox1" ItemsSource="{Binding List1}">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
<DataTemplate DataType="{x:Type local:SharedListItem}">
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Background="Red" />
<Label Grid.Row="1" Content="{Binding Name}" />
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>
<ListBox Grid.Row="0" x:Name="listBox2" ItemsSource="{Binding List2}" Background="AliceBlue" Visibility="Collapsed">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
<DataTemplate DataType="{x:Type local:SharedListItem}">
<Label Content="{Binding Name}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"/>
</DataTemplate>
</ListBox.Resources>
</ListBox>
<Button Grid.Row="1" Click="Button_Click">Toggle View</Button>
</Grid>
</Window>
代码背后:
using System.Windows;
namespace SharedListBindingExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (listBox1.Visibility == Visibility.Collapsed)
{
listBox1.Visibility = Visibility.Visible;
listBox2.Visibility = Visibility.Collapsed;
}
else
{
listBox2.Visibility = Visibility.Visible;
listBox1.Visibility = Visibility.Collapsed;
}
}
}
}
视图模型:
using System.Collections.Generic;
namespace SharedListBindingExample
{
public class TwoPropertiesForSameListViewModel
{
private readonly List<SharedListItem> _sharedList;
public TwoPropertiesForSameListViewModel()
{
_sharedList = new List<SharedListItem>();
for (int i = 0; i < 300; i++)
{
_sharedList.Add(new SharedListItem($"Item #{i}"));
}
}
public IEnumerable<SharedListItem> List1
{
get
{
return _sharedList;
}
}
public IEnumerable<SharedListItem> List2
{
get
{
return _sharedList;
}
}
}
}
SharedListItem:
using System.ComponentModel;
namespace SharedListBindingExample
{
public class SharedListItem : INotifyPropertyChanged
{
private bool _isSelected;
public SharedListItem(string name)
{
Name = name;
}
public string Name { get; set; }
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
if (value != _isSelected)
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
答案 0 :(得分:2)
我认为您需要在两个不同的视图之间使用CollectionViewSource
对象来保持所选项目的同步。我在自己的应用程序中做了类似的事情。
我有一个控件在Xaml中定义CollectionViewSource
资源:
<UserControl.Resources>
<CollectionViewSource x:Key="DataView" />
</UserControlResources>
该控件还有DependencyProperty
CollectionViewSource
,允许它与其他控件绑定数据:
public static readonly DataViewProperty =
DependencyProperty.Register( "DataView", typeof( CollectionViewSource ), typeof( YourControlType ), new PropertyMetadata( null ) );
public CollectionViewSource DataView {
get { return (CollectionViewSource) GetProperty( DataViewProperty); }
set { SetProperty( DataViewProperty, value );
}
然后在组件构造函数中,在调用InitializeComponent
之后,你必须执行如下代码:
public MyUserControl() {
InitializeComponent();
DataView = FindResource( "DataView" ) as CollectionViewSource;
DataView.Source = YourObservableCollection;
}
在您要共享此对象的其他视图中,您将创建一个新的CollectionViewSource DependencyProperty
。这允许您在具有不同数据视图的窗口中将两个proeprties绑定到彼此。在我的第二个控件中,我有另一个ObservableCollection
对象属性,但它没有在控件的构造函数中初始化。我所做的是在控件的Loaded
事件处理程序中,我将ObservableCollection
属性的值设置为CollectionViewSource
对象的Source属性的值。那就是:
if ( DataCollection == null && DataView != null ) {
DataCollection = (ObservableCollection<DataType>) DataView.Source;
DataGrid.ItemsSource = DataView.View;
}
在此之后,两个控件共享相同的ObservableCollection
和相同的CollectionViewSource
。 CollectionViewSource
使两个控件的所选项保持同步。
显然,您可以在任意数量的视图中共享CollectionViewSource
个对象。一个控件必须声明该对象,其他控件必须共享它。
答案 1 :(得分:0)
使用WPF时,建议您为IEnumerable
或ObservableCollection
删除ICollectionView
。您可以在MSDN上找到有关这些集合的更多详细信息,但我的建议是将一个ListBox的SelectedItem
绑定到XAML中的另一个(SelectedItem = {Binding ElementName='yourList', Path='SelectedItem'}
),这样当一个更改时,另一个将响应(你应该为这两个列表做这个)。我自己从未尝试过这种循环绑定,但我认为在你的情况下它会正常工作。