代码之家  ›  专栏  ›  技术社区  ›  Chris Holmes

如何防止WPF数据报在项目更新时取消选择SelectedItem?

  •  8
  • Chris Holmes  · 技术社区  · 14 年前

    我的场景:我有一个后台线程,用于轮询更改并定期更新WPF数据报的ObservableCollection(MVVM样式)。用户可以单击数据报中的一行,并在同一主视图的相邻用户控件中显示该行的“详细信息”。

    当后台线程有更新时,它会循环访问ObservableCollection中的对象,并在个别对象发生更改时替换它们(换句话说,我不会将整个新的ObservableCollection重新绑定到DataGrid,而是替换集合中的个别项;这允许DataGrid在ING更新。

    问题是,当用户选择了一个特定的行,并且详细信息显示在相邻的用户控件中之后,当后台线程更新数据报时,数据报将丢失SelectedItem(它将被重置回-1的索引)。

    如何在更新ObservableCollection之间保留SelectedItem?

    3 回复  |  直到 14 年前
        1
  •  6
  •   ASanch    14 年前

    如果您的网格是单个选择,我建议您使用CollectionView作为项源,而不是实际的ObservableCollection。然后,确保DataGrid.IsSynchronizedWithCurrentItem设置为true。最后,在“替换项逻辑”的末尾,只需将CollectionView的当前项移动到相应的新项。

    下面是一个示例,演示了这一点。(不过我在用列表框。希望它能和你的数据报一起工作。

    编辑-使用MVVM新建示例:

    XAML

    <Window x:Class="ContextTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            x:Name="window"
            Title="MainWindow" Height="350" Width="525">
        <DockPanel>
            <ListBox x:Name="lb" DockPanel.Dock="Left" Width="200" 
                     ItemsSource="{Binding ModelCollectionView}"
                     SelectionMode="Single" IsSynchronizedWithCurrentItem="True">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=Name}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
            <TextBlock Text="{Binding ElementName=lb, Path=SelectedItem.Description}"/>
    
        </DockPanel>
    </Window>
    

    代码落后:

    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.Collections.ObjectModel;
    using System.Windows.Threading;
    
    namespace ContextTest
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = new ViewModel();
            }
        }
    
        public class ViewModel
        {
            private DataGenerator dataGenerator;
            private ObservableCollection<Model> modelCollection;
            public ListCollectionView ModelCollectionView { get; private set; }
    
            public ViewModel()
            {
                modelCollection = new ObservableCollection<Model>();
                ModelCollectionView = new ListCollectionView(modelCollection);
    
                //Create models
                for (int i = 0; i < 20; i++)
                    modelCollection.Add(new Model() { Name = "Model" + i.ToString(), 
                        Description = "Description for Model" + i.ToString() });
    
                this.dataGenerator = new DataGenerator(this);
            }
    
            public void Replace(Model oldModel, Model newModel)
            {
                int curIndex = ModelCollectionView.CurrentPosition;
                int n = modelCollection.IndexOf(oldModel);
                this.modelCollection[n] = newModel;
                ModelCollectionView.MoveCurrentToPosition(curIndex);
            }
        }
    
        public class Model
        {
            public string Name { get; set; }
            public string Description { get; set; }
        }
    
        public class DataGenerator
        {
            private ViewModel vm;
            private DispatcherTimer timer;
            int ctr = 0;
    
            public DataGenerator(ViewModel vm)
            {
                this.vm = vm;
                timer = new DispatcherTimer(TimeSpan.FromSeconds(5), 
                    DispatcherPriority.Normal, OnTimerTick, Dispatcher.CurrentDispatcher);
            }
    
            public void OnTimerTick(object sender, EventArgs e)
            {
                Random r = new Random();
    
                //Update several Model items in the ViewModel
                int times = r.Next(vm.ModelCollectionView.Count - 1);
                for (int i = 0; i < times; i++)
                {   
                    Model newModel = new Model() 
                        { 
                            Name = "NewModel" + ctr.ToString(),
                            Description = "Description for NewModel" + ctr.ToString()
                        };
                    ctr++;
    
                    //Replace a random item in VM with a new one.
                    int n = r.Next(times);
                    vm.Replace(vm.ModelCollectionView.GetItemAt(n) as Model, newModel);
                }
            }
        }
    }
    

    旧样品:

    XAML:

    <Window x:Class="ContextTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <StackPanel>
            <ListBox x:Name="lb" SelectionMode="Single" IsSynchronizedWithCurrentItem="True" SelectionMode="Multiple">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=Name}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
            <TextBlock Text="{Binding ElementName=lb, Path=SelectedItem.Name}"/>
            <Button Click="Button_Click">Replace</Button>
    
    
        </StackPanel>
    </Window>
    

    代码落后:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    
    namespace ContextTest
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            ObservableCollection<MyClass> items;
            ListCollectionView lcv;
    
            public MainWindow()
            {
                InitializeComponent();
    
                items = new ObservableCollection<MyClass>();
                lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(items);
                this.lb.ItemsSource = lcv;
                items.Add(new MyClass() { Name = "A" });
                items.Add(new MyClass() { Name = "B" });
                items.Add(new MyClass() { Name = "C" });
                items.Add(new MyClass() { Name = "D" });
                items.Add(new MyClass() { Name = "E" });
    
            }
    
            public class MyClass
            {
                public string Name { get; set; }
            }
    
            int ctr = 0;
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                MyClass selectedItem = this.lb.SelectedItem as MyClass;
                int index = this.items.IndexOf(selectedItem);
                this.items[index] = new MyClass() { Name = "NewItem" + ctr++.ToString() };
                lcv.MoveCurrentToPosition(index);
            }
    
        }
    }
    
        2
  •  3
  •   Jay    14 年前

    我还没有使用WPF数据报,但我会尝试以下方法:

    向视图模型添加一个属性,该属性将保存当前选定项的值。

    束缚 SelectedItem 使用 TwoWay .

    这样,当用户选择一行时,它将更新视图模型,并且当 ObservableCollection 得到更新它不会影响到 选择项目 是绑定的。被束缚了,我不希望它能像你看到的那样被重置。

        3
  •  1
  •   GWLlosa    14 年前

    您可以在更新集合的逻辑中保存CollectionView.Current项对另一个变量的引用。然后,在完成更新后,调用CollectionView.MoveCurrentTo(variable)重置所选项目。