2014-11-14 16 views
5

Ho una collezione osservabile nel modello di vista che implementa Base Bindable come segue Si prega di dare un'occhiata ai metodi MoveUp e MoveDown dove sono associati a due pulsanti nella vista. Quando si preme il pulsante su, voglio che la riga selezionata nel datagrid si muova di un gradino verso l'alto in base alla colonna della sequenza nel database e per un gradino in basso. Entrambi i metodi funzionano PERFETTAMENTE. Il problema è che le modifiche vengono visualizzate nel datagrid solo quando l'intera vista viene aggiornata. Il mio requisito è quando si fa clic sul pulsante Voglio che la vista venga aggiornata automaticamente. Mi scuso per un codice così lungo. Per favore aiuto!!!!. Ho anche un codice cs per entrambi i pulsanti su e giù specificati sotto viewmodel. Solo i puntatori nel codice che devono essere enfatizzati sono i comandi ObservableCollection JobEntities, MoveUp e MoveDown.Raccolta Wpf e DataGrid non aggiornano le modifiche

ViewModel.cs:

public class JobConfigurationViewModel : BindableBase 
{ 


    public JobConfigurationLogic JobConfigurationLogic = 
     new JobConfigurationLogic(new JobConfigurationResultsRepository()); 

    public SrcDestConfigurationLogic SrcDestConfigurationLogic = 
     new SrcDestConfigurationLogic(new SrcDestCofigurationRepository()); 

    private string _enterprise; 

    public string Enterprise 
    { 
     get { return _enterprise; } 
     set { SetProperty(ref _enterprise, value); } 
    } 

    private int currentJobID; 
    private int currentSequence; 
    private int previousJobID; 
    private int previousSequence; 
    private string _site; 

    public string Site 
    { 
     get { return _site; } 
     set { SetProperty(ref _site, value); } 
    } 

    private int _siteID; 

    public int SiteID 
    { 
     get { return _siteID; } 
     set { SetProperty(ref _siteID, value); } 
    } 

    private ObservableCollection<JobConfigurationResults> _jobEntities; 

    public ObservableCollection<JobConfigurationResults> JobEntities 
    { 
     get { return _jobEntities; } 
     set 
     { 
      SetProperty(ref _jobEntities, value); 
      this.OnPropertyChanged("JobEntities"); 
     } 
    } 

    //Source System List for Job 
    private List<SourceSiteSystem> _lstJobSrcSystems; 

    public List<SourceSiteSystem> LstJobSrcSystems 
    { 
     get { return _lstJobSrcSystems; } 
     set 
     { 
      //Using bindable base setproperty method instead of older inotify prop changed method 
      SetProperty(ref _lstJobSrcSystems, value); 
     } 
    } 

    //Deestination System List for Job 
    private List<DestinationSiteSystem> _lstJobDestSystems; 

    public List<DestinationSiteSystem> LstJobDestSystems 
    { 
     get { return _lstJobDestSystems; } 
     set 
     { 
      //Using bindable base setproperty method instead of older inotify prop changed method 
      SetProperty(ref _lstJobDestSystems, value); 
     } 
    } 

    //the Selected Source Site system ID 
    private int _selectedSrcSiteSystemId = 0; 

    public int SelectedSrcSiteSystemId 
    { 
     get { return _selectedSrcSiteSystemId; } 
     set 
     { 
      //Using bindable base setproperty method instead of older inotify prop changed method 
      SetProperty(ref _selectedSrcSiteSystemId, value); 
     } 
    } 

    //the Selected Source Site system from the dropdown 
    private SourceSiteSystem _selectedSrcSiteSystem; 

    public SourceSiteSystem SelectedSrcSiteSystem 
    { 
     get { return _selectedSrcSiteSystem; } 
     set 
     { 
      //Using bindable base setproperty method instead of older inotify prop changed method 
      if (value != null) 
      { 
       SetProperty(ref _selectedSrcSiteSystem, value); 
       SelectedSrcSiteSystemId = SelectedSrcSiteSystem.SiteSystemId; 
      } 
     } 
    } 

    //the Selected Destination Site system ID 
    private int _selectedDestSiteSystemId = 0; 

    public int SelectedDestSiteSystemId 
    { 
     get { return _selectedDestSiteSystemId; } 
     set 
     { 
      //Using bindable base setproperty method instead of older inotify prop changed method 
      SetProperty(ref _selectedDestSiteSystemId, value); 
     } 
    } 

    //the Selected Destination Site system from the dropdown 
    private DestinationSiteSystem _selectedDestSiteSystem; 

    public DestinationSiteSystem SelectedDestSiteSystem 
    { 
     get { return _selectedDestSiteSystem; } 
     set 
     { 
      //Using bindable base setproperty method instead of older inotify prop changed method 
      if (value != null) 
      { 
       SetProperty(ref _selectedDestSiteSystem, value); 
       SelectedDestSiteSystemId = SelectedDestSiteSystem.SiteSystemId; 
      } 
     } 
    } 

    private JobConfigurationResults _jeJobConfigurationResults; 

    public JobConfigurationResults JEJobConfigurationResults 
    { 
     get { return _jeJobConfigurationResults; } 
     set { _jeJobConfigurationResults = value; } 
    } 

    private List<JobTaskConfiguration> _taskSelectionList = new List<JobTaskConfiguration>(); 

    private CancellationTokenSource _source; 

    private RelayCommand<object> _commandSaveInstance; 
    private RelayCommand<object> _hyperlinkInstance; 
    private RelayCommand<object> _commandRunJob; 
    private RelayCommand<object> _upCommand; 
    private RelayCommand<object> _downCommand; 
    private IEventAggregator _aggregator; 


    /// <summary> 
    /// This is a Subscriber to the Event published by EnterpriseViewModel 
    /// </summary> 
    /// <param name="agg"></param> 
    public JobConfigurationViewModel(IEventAggregator agg) 
    { 
     _aggregator = agg; 
     PubSubEvent<Message> evt = _aggregator.GetEvent<PubSubEvent<Message>>(); 
     evt.Subscribe(message => Enterprise = message.Enterprise.ToString(), ThreadOption.BackgroundThread); 
     evt.Subscribe(message => Site = message.Site.ToString(), ThreadOption.BackgroundThread); 
     evt.Subscribe(message => SiteID = message.SiteID, ThreadOption.BackgroundThread); 
     //evt.Unsubscribe(); 
     StartPopulate(); 
    } 

    private async void StartPopulate() 
    { 
     await TaskPopulate(); 
    } 

    //This is to ensure that the publisher has published the data that is needed for display in this workspace 
    private bool TaskProc() 
    { 
     Thread.Sleep(500); 
     PubSubEvent<Message> evt = _aggregator.GetEvent<PubSubEvent<Message>>(); 
     evt.Subscribe(message => Enterprise = message.Enterprise.ToString(), ThreadOption.BackgroundThread); 
     evt.Subscribe(message => Site = message.Site.ToString(), ThreadOption.BackgroundThread); 
     evt.Subscribe(message => SiteID = message.SiteID, ThreadOption.BackgroundThread); 
     return DoPopulate(); 
    } 

    private Task<bool> TaskPopulate() 
    { 
     _source = new CancellationTokenSource(); 
     return Task.Factory.StartNew<bool>(TaskProc, _source.Token); 
    } 

    /// <summary> 
    /// This method handles the populating of the Source and Destination Dropdowns and the Job entity and Task Datagrid 
    /// This is mainly driven by the Site selected in the previous workspace 
    /// </summary> 
    /// <returns></returns> 

    private bool DoPopulate() 
    { 
     PopulateSourceDestinations(this.SiteID); 

     return true; 
    } 

    /// <summary> 
    /// this method displays all entities and tasks for the site. 
    /// This is done async so that the Publisher thread is not held up 
    /// </summary> 
    public void GetJobConfigurationResults() 
    { 
     if (SelectedSrcSiteSystem == null) 
     { 
      SelectedSrcSiteSystem = LstJobSrcSystems[0]; 
     } 
     if (SelectedDestSiteSystem == null) 
     { 
      SelectedDestSiteSystem = LstJobDestSystems[0]; 
     } 
     SelectedSrcSiteSystemId = SelectedSrcSiteSystem.SiteSystemId; 
     SelectedDestSiteSystemId = SelectedDestSiteSystem.SiteSystemId; 
     var jobConfigurationResults = new JobConfigurationResults 
     { 
      SourceId = SelectedSrcSiteSystemId, 
      DestinationId = SelectedDestSiteSystemId 
     }; 
     JobEntities = new ObservableCollection<JobConfigurationResults>(); 
     JobEntities = JobConfigurationLogic.GetResults(jobConfigurationResults.SourceId, 
      jobConfigurationResults.DestinationId); 
     _taskSelectionList = new List<JobTaskConfiguration>(JobEntities.Count * 3); 
    } 

    /// <summary> 
    /// //Adding a method to pupulate the Source and Destination dropdown lists. 
    /// This is done async so that the Publisher thread is not held up 
    /// </summary> 
    /// 
    /// 
    public async void PopulateSourceDestinations(int siteId) 
    { 
     this.LstJobSrcSystems = SrcDestConfigurationLogic.LoadSourceSiteSystems(siteId); 
     this.LstJobDestSystems = SrcDestConfigurationLogic.LoadDestinationSystems(siteId); 
     GetJobConfigurationResults(); 
    } 

    public ICommand HyperlinkCommand 
    { 
     get 
     { 
      if (_hyperlinkInstance == null) 
       _hyperlinkInstance = new RelayCommand<object>(openDialog); 
      return _hyperlinkInstance; 
     } 
    } 

    private void openDialog(object obj) 
    { 
     JobConfigurationResults results = obj as JobConfigurationResults; 
     JEJobConfigurationResults = JobEntities.SingleOrDefault(x => x.JobEntityId == results.JobEntityId); 

    } 

    public ICommand CommandSave 
    { 
     get 
     { 
      if (_commandSaveInstance == null) 
       _commandSaveInstance = new RelayCommand<object>(saveJobConfigurationChanges); 
      return _commandSaveInstance; 
     } 
    } 

    public ICommand CommandRunJob 
    { 
     get { return _commandRunJob ?? (_commandRunJob = new RelayCommand<object>(RunJob)); } 
    } 

    /// <summary> 
    /// this saves all the changes in the selection made by the user 
    /// </summary> 
    /// <param name="ob"></param> 
    public void saveJobConfigurationChanges(object ob) 
    { 
     foreach (var job in JobEntities) 
     { 
      int jobEntityId = job.JobEntityId; 
      foreach (var task in job.TaskDetails) 
      { 
       int id = task.JobTask_ID; 
       bool isSelected = task.IsSelected; 
       _taskSelectionList.Add(task); 
      } 
     } 
     JobConfigurationLogic.UpdateTaskSelection(_taskSelectionList); 
    } 


    public ICommand UpCommand 
    { 
     get 
     { 
      if (_upCommand == null) 
       _upCommand = new RelayCommand<object>(MoveUp); 
      return _upCommand; 
     } 
    } 

    private void MoveUp(object obj) 
    { 

     if (obj != null) 
     { 
      JobConfigurationResults results = obj as JobConfigurationResults; 
      currentJobID = results.JobEntityId; 
      currentSequence = results.SequenceOrder - 1; 
      try 
      { 
       JobConfigurationResults res = _jobEntities.SingleOrDefault(n => n.SequenceOrder == currentSequence); 
       previousJobID = res.JobEntityId; 
       previousSequence = res.SequenceOrder + 1; 
       // JobConfigurationLogic.UpdateSequence(currentJobID, previousSequence, previousJobID, currentSequence); 
       JobConfigurationLogic.UpdateSequence(currentSequence, currentJobID, previousSequence, previousJobID); 
       OnPropertyChanged("JobEntities"); 
      } 
      catch (NullReferenceException) 
      { 
       MessageBox.Show("Can't move the top record"); 
      } 

     } 
     else 
     { 
      MessageBox.Show("Please Select a row that you want to sort"); 
     } 
    } 

    public ICommand DownCommand 
    { 
     get 
     { 
      if (_downCommand == null) 
       _downCommand = new RelayCommand<object>(MoveDown); 
      return _downCommand; 
     } 
    } 

    private void MoveDown(object obj) 
    { 
     if (obj != null) 
     { 
      JobConfigurationResults results = obj as JobConfigurationResults; 
      currentJobID = results.JobEntityId; 
      currentSequence = results.SequenceOrder + 1; 
      try 
      { 
       JobConfigurationResults res = _jobEntities.SingleOrDefault(a => a.SequenceOrder == currentSequence); 
       previousJobID = res.JobEntityId; 
       previousSequence = res.SequenceOrder - 1; 
       JobConfigurationLogic.UpdateSequence(currentSequence, currentJobID, previousSequence, previousJobID); 
       OnPropertyChanged("JobEntities"); 
      } 
      catch (NullReferenceException) 
      { 

       MessageBox.Show("You have reached the end"); 
      } 

     } 
     else 
     { 
      MessageBox.Show("Please Select a row that you want to sort"); 
     } 
    } 

    /// <summary> 
    /// Execute an etl job using the current job id 
    /// </summary> 
    private void RunJob(object obj) 
    { 
     JobEngine jobEngine = new JobEngine(); 
     var jobId = JobEntities[0].JobId; 
     jobEngine.ProcessJob(jobId); 
    } 
} 

CS CODICE:

private void btnup_Click(object sender, RoutedEventArgs e) 
{ 

    dgEntities.Items.Refresh(); 
    //dgEntities.GetBindingExpression(DataGrid.ItemsSourceProperty).UpdateTarget(); 
} 

private void btndown_Click(object sender, RoutedEventArgs e) 
{ 
    dgEntities.GetBindingExpression(DataGrid.ItemsSourceProperty).UpdateTarget(); 
} 
+0

si può mostrare come si fa associare la collezione osservabile? Inoltre sei sicuro che i comandi vengano eseguiti ogni volta che fai clic su Su/Giù? – whoisthis

+0

Yup per datagrid itemsource = JobEntities .. Tutti i comandi funzionano perfettamente. Solo il datagrid non si aggiorna. – nikhil

risposta

10

Un ObservableCollection notificherà sul cambiamento. Non c'è motivo di farlo manualmente, quindi è possibile rimuovere tutti i OnPropertyChanged("JobEntities");. Questo ti porterà a una soluzione più pulita.

MSDN

WPF fornisce la classe ObservableCollection, che è incorporato in attuazione di un insieme di dati che implementa l'interfaccia INotifyCollectionChanged .

La parte successiva è che ObservableCollection notificherà solo le modifiche alla raccolta stessa (aggiungi/rimuovi). Qualsiasi modifica a un elemento all'interno dell'elenco non avrà il messaggio di notifica inviato. Per fare questo, il metodo più semplice è implementare il INotifyPropertyChanged per gli elementi utilizzati nella collezione osservabile

Sto usando PRISM 5 nell'esempio, quindi dovrebbe essere abbastanza uguale a quello che stai facendo. Ci sono un paio di modifiche importanti al tuo codice. Innanzitutto, sto utilizzando una proprietà straight per la mia collezione Observable. Sappiamo che il framework gestirà qualsiasi operazione di aggiunta/rimozione a questa raccolta. Quindi per notificare quando cambio una proprietà all'interno dell'entità in una collezione osservabile, ho usato una proprietà notify all'interno della classe TestEntity stessa.

public class MainWindowViewModel : BindableBase 
{ 
    //Notice no OnPropertyChange, just a property 
    public ObservableCollection<TestEntity> TestEntities { get; set; } 

    public MainWindowViewModel() 
     : base() 
    { 
     this.TestEntities = new ObservableCollection<TestEntity> { 
      new TestEntity { Name = "Test", Count=0}, 
      new TestEntity { Name = "Test1", Count=1}, 
      new TestEntity { Name = "Test2", Count=2}, 
      new TestEntity { Name = "Test3", Count=3} 
     }; 

     this.UpCommand = new DelegateCommand(this.MoveUp); 
    } 

    public ICommand UpCommand { get; private set; } 

    private void MoveUp() 
    { 
     //Here is a dummy edit to show the modification of a element within the observable collection 
     var i = this.TestEntities.FirstOrDefault(); 
     i.Count = 5; 

    } 
} 

Ecco la mia entità, notare la BindableBase e il fatto mi informo sul cambiamento. Ciò consente allo DataGrid oa qualsiasi altra cosa tu stia utilizzando di essere informato che la proprietà è cambiata.

public class TestEntity : BindableBase { 
    private String _name; 
    public String Name 
    { 
     get { return _name; } 
     set { SetProperty(ref _name, value); } 
    } 
    //Notice I've implemented the OnPropertyNotify (Prism uses SetProperty, but it's the same thing) 
    private Int32 _count; 
    public Int32 Count 
    { 
     get { return _count; } 
     set { SetProperty(ref _count, value); } 
    } 
} 

ora davvero tutte le TestEntity ha bisogno di avere implementato il INotifyPropertyChanged per questo al lavoro, ma sto usando il PRISM BindableBase come esempio.

EDIT

Ho trovato una domanda simile su SO. Penso che il tuo sia leggermente diverso, ma si sovrappongono ai concetti. Può aiutare a guardarci sopra.

Observable Collection Notify when property changed in MVVM

EDIT

Se il datagrid viene risolto il metodo precedente non aggiornerà la griglia. Per gestirlo è necessario aggiornare la vista della griglia, ma non è possibile accedervi direttamente tramite MVVM. Quindi per gestire questo ti consigliamo di utilizzare un CollectionViewSource.

public class MainWindowViewModel : BindableBase 
{ 
    //This will bind to the DataGrid instead of the TestEntities 
    public CollectionViewSource ViewSource { get; set; } 
    //Notice no OnPropertyChange, just a property 
    public ObservableCollection<TestEntity> TestEntities { get; set; } 

    public MainWindowViewModel() 
     : base() 
    { 
     this.TestEntities = new ObservableCollection<TestEntity> { 
     new TestEntity { Name = "Test", Count=0}, 
     new TestEntity { Name = "Test1", Count=1}, 
     new TestEntity { Name = "Test2", Count=2}, 
     new TestEntity { Name = "Test3", Count=3} 
    }; 

     this.UpCommand = new DelegateCommand(this.MoveUp); 

     //Initialize the view source and set the source to your observable collection 
     this.ViewSource = new CollectionViewSource(); 
     ViewSource.Source = this.TestEntities; 
    } 

    public ICommand UpCommand { get; private set; } 

    private void MoveUp() 
    { 
     //Here is a dummy edit to show the modification of a element within the observable collection 
     var i = this.TestEntities.FirstOrDefault(); 
     i.Count = 5; 
     //Now anytime you want the datagrid to refresh you can call this. 
     ViewSource.View.Refresh(); 
    } 
} 

La classe TestEntity non cambia, ma qui è ancora una volta la classe:

public class TestEntity : BindableBase 
{ 
    private String _name; 
    public String Name 
    { 
     get { return _name; } 
     set { SetProperty(ref _name, value); } 
    } 
    //Notice I've implemented the OnPropertyNotify (Prism uses SetProperty, but it's the same thing) 
    private Int32 _count; 
    public Int32 Count 
    { 
     get { return _count; } 
     set { SetProperty(ref _count, value); } 
    } 
} 

Per chiarimenti, ecco la mia XAML che mostra il legame con il nuovo CollectionViewSource.

<DataGrid Grid.Row="1" ItemsSource="{Binding ViewSource.View}"></DataGrid> 

Per ulteriori informazioni è possibile consultare l'articolo MSDN.

Ecco un'altra rilevante domanda/risposta - Re-sort WPF DataGrid after bounded Data has changed

+0

Grazie Nathan, ci proverò. Molto apprezzato per il codice e la spiegazione. – nikhil

+0

Nathan Ho provato questo, ma ancora non aggiorna il DataGrid. Funziona solo quando aggiorno l'intera vista. C'è un modo per aggiornare la vista sul clic del pulsante .. – nikhil

+0

@nikhil: Mi dispiace che mi manchi quello che stai cercando di fare. Quello che ho mostrato aggiornerà lo schermo quando modifichi elementi all'interno della collezione osservabile, che sembra essere quello che stai cercando di fare. L'unico altro pezzo che ho potuto vedere è che hai ottenuto il datagrid ordinato, nel qual caso la griglia non si aggiornerà e ricorrere agli oggetti. Quindi aggiornerò la mia risposta per tener conto di ciò. – Nathan