2010-09-02 10 views
7

मेरा परिदृश्य: मेरे पास पृष्ठभूमि थ्रेड है जो परिवर्तनों के लिए चुनाव करता है और समय-समय पर एक WPF डेटाग्रिड के ऑब्जर्जेबल कोलेक्शन (एमवीवीएम-शैली) को अद्यतन करता है। उपयोगकर्ता डेटाग्रिड में एक पंक्ति पर क्लिक कर सकते हैं और उसी पंक्ति के "विवरण" को उसी मुख्य दृश्य पर आसन्न UserControl में ला सकते हैं।आइटम अपडेट किए जाने पर चयनित ITem चुनने से WPF DataGrid को कैसे रोकें?

जब पृष्ठभूमि धागे के अपडेट होते हैं, तो यह ऑब्जेर्जेबल कोलेक्शन में ऑब्जेक्ट्स के माध्यम से चक्र चलाता है और अगर वे बदल जाते हैं तो अलग-अलग ऑब्जेक्ट्स को प्रतिस्थापित करते हैं (दूसरे शब्दों में, मैं डेटाग्रिड में एक नया नया अवलोकन करने योग्य चयन नहीं कर रहा हूं, बल्कि इसके बजाय अलग-अलग आइटमों को बदल रहा हूं संग्रह; यह डेटाग्रिड को अपडेट के दौरान सॉर्टिंग ऑर्डर बनाए रखने की अनुमति देता है)।

समस्या यह है कि उपयोगकर्ता ने एक विशिष्ट पंक्ति का चयन करने के बाद और निकटवर्ती उपयोगकर्ता नियंत्रण में विवरण प्रदर्शित किए हैं, जब पृष्ठभूमि थ्रेड डेटाग्रिड को अपडेट करता है तो डेटाग्रिड चयनित इटैम खो देता है (यह -1 के सूचकांक पर रीसेट हो जाता है)।

मैं ObservableCollection के अपडेट के बीच चयनित इटैम को कैसे बनाए रख सकता हूं?

उत्तर

6

यदि आपका ग्रिड एकल चयन है, तो मेरा सुझाव यह है कि आप संग्रह दृश्य को वास्तविक पर्यवेक्षण चयन के बजाय आइटम्ससोर्स के रूप में उपयोग करते हैं। फिर, सुनिश्चित करें कि Datagrid.IsSynchronizedWithCurrentItem सत्य पर सेट है। अंत में, अपने "आइटम आइटम को प्रतिस्थापित करें" के अंत में, केवल संग्रह दृश्य के CurrentItem को इसी नए आइटम पर ले जाएं।

नीचे एक नमूना है जो इसे प्रदर्शित करता है। (हालांकि मैं यहां एक सूची बॉक्स का उपयोग कर रहा हूं। उम्मीद है कि यह आपके डाटाग्रिड के साथ ठीक काम करता है)।

संपादित करें - नया नमूना का उपयोग 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); 
     } 

    } 
} 
+0

वास्तव में मेरे लिए कर्मिकपपेट काम नहीं करता है। समस्या यह है कि मैं एमवीवीएम कर रहा हूं और इससे थोड़ा अलग समस्या आती है। मैं सिर्फ बटन में हैंडलर क्लिक करके सब कुछ संभाल नहीं सकता। –

+0

मैं यह सुझाव नहीं दे रहा हूं कि आप बटन में हैंडलर पर क्लिक करें। वह सिर्फ एक उदाहरण कोड था। विचार यह है कि आप अपने ViewModel में कुछ करते हैं जो कि मेरे उदाहरण में बटन_Click ईवेंट में से एक जैसा है। मुझे आपको एक एमवीवीएम उदाहरण लिखने की कोशिश करें। मैं तुम्हारे पास वापस मिलने आऊँगा। – ASanch

+0

संपादित देखें। मुझे उम्मीद है कि यह आपको एक बेहतर विचार देता है। इसमें एक सूची बॉक्स है जिसका डेटा हर 5 सेकंड में यादृच्छिक रूप से अपडेट किया जा रहा है। आप देखेंगे कि यह अभी भी अपडेट के बाद चयन रखता है। – ASanch

3

मैं WPF डेटा ग्रिड के साथ काम नहीं किया है, लेकिन मैं इस तरीके का प्रयास करेंगे:

दृश्य-मॉडल है कि वर्तमान में चयनित आइटम के मूल्य का आयोजन करेगा करने के लिए एक संपत्ति जोड़ें।

TwoWay का उपयोग करके इस नई संपत्ति के लिए SelectedItem बांधें।

इस तरह, जब उपयोगकर्ता एक पंक्ति का चयन करता है, तो यह दृश्य-मॉडल अपडेट करेगा, और जब ObservableCollection अपडेट हो जाता है तो यह उस संपत्ति को प्रभावित नहीं करेगा जिस पर SelectedItem बाध्य है। बाध्य होने के नाते, मैं उम्मीद नहीं करूँगा कि आप जिस तरीके से देख रहे हैं उसे रीसेट कर सकते हैं।

+0

यह मेरा पहला विचार था जय, लेकिन अभी तक कोई पासा नहीं था।यह अभी भी इसे रीसेट कर रहा है। –

+0

@Chris क्या चयनित आइटम स्वयं अपडेट किया जा रहा है, या सिर्फ अन्य आइटम? – Jay

+0

सभी आइटम अपडेट किए जा रहे हैं। –

1

आप तर्क संग्रह अपडेट कर देता है, एक और चर के लिए CollectionView.Current आइटम संदर्भ बंद बचा सकता है। फिर, अपडेट करने के बाद, चयनित आइटम को रीसेट करने के लिए CollectionView.MoveCurrentTo (variable) पर कॉल करें।

संबंधित मुद्दे