2014-11-14 23 views
5

我有一個可觀察的集合在視圖模型中實現了Bindable Base,如下所示請查看MoveUp和MoveDown方法,它們綁定到視圖中的兩個按鈕。當向上按鈕被按下時,我希望數據網格中的選定行在基於數據庫的序列列中向上移動一步,向下移動一步。兩種方法都可以正常工作。問題是隻有刷新整個視圖時纔會在數據網格中顯示更改。我的要求是點擊按鈕時,我希望視圖自動刷新。我對這樣長的代碼表示歉意。請幫忙!!!!。我也有一些cs代碼,以及viewmodel下面指定的向上和向下按鈕。只有代碼中需要強調的指針是ObservableCollection JobEntities,MoveUp和MoveDown命令。Wpf Observable集合和DataGrid沒有更新變化

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 CODE:

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

你能證明你怎麼綁定觀察集合?你也確定每次點擊向上/向下時都執行這些命令嗎? – whoisthis

+0

Yup for datagrid itemsource = JobEntities ..所有的命令都能正常工作。只有datagrid不刷新。 – nikhil

回答

10

ObservableCollection會通知上的變化。沒有理由手動去做,所以你可以刪除所有的OnPropertyChanged("JobEntities");。這將使你得到一個更清潔的解決方案。

MSDN

WPF提供的ObservableCollection類,它是一個內置的 實現數據採集實現了 INotifyCollectionChanged接口。

接下來的部分是ObservableCollection只會通知對集合本身的更改(添加/刪除)。對列表中的元素進行的任何修改都不會發送通知消息。要做到這一點,最簡單的方法是對可觀察集合中使用的元素實現INotifyPropertyChanged

我在示例中使用了PRISM 5,因此它應該與您所做的相當。對代碼有一些主要的設計更改。首先,我爲我的Observable Collection使用直屬性。我們知道該框架將處理對該集合的任何添加/刪除操作。然後通知當我更改可觀察集合中實體中的屬性時,我已使用TestEntity類中的通知屬性。

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; 

    } 
} 

這裏是我的實體,注意BindableBase而事實上我通知上的變化。這允許DataGrid或您使用的任何其他信息被通知屬性已更改。

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); } 
    } 
} 

現在TestEntity真的需要所有已經實現了INotifyPropertyChanged這個工作,但我使用的PRISM BindableBase作爲一個例子。

編輯

我發現SO類似的問題。我認爲你們略有不同,但它們在概念上重疊。它可能有助於查看它。

Observable Collection Notify when property changed in MVVM

編輯

如果DataGrid的排序以前的方法將不會更新電網。爲了處理這個問題,你需要刷新網格視圖,但無法使用MVVM直接訪問它。所以要處理這個問題,你需要使用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(); 
    } 
} 

TestEntity類不會改變,但在這裏再次在班上是:

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); } 
    } 
} 

爲了清楚起見,這裏顯示了綁定到新的CollectionViewSource我的XAML。

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

要進一步閱讀,你可以參考MSDN這篇文章。

這裏還有一個相關的問題/答案 - Re-sort WPF DataGrid after bounded Data has changed

+0

謝謝Nathan我會試試看。非常感謝代碼和解釋。 – nikhil

+0

彌敦道我已經嘗試過,但它仍然沒有刷新DataGrid。它只在刷新整個視圖時才起作用。有沒有辦法刷新按鈕點擊的視圖.. – nikhil

+0

@nikhil:對不起,我一定錯過了你想要做的事情。當您修改可觀察集合中的元素時,我所顯示的內容將會更新屏幕,這似乎是您要做的事情。唯一可以看到的其他部分是,您可能已經對數據網格進行了排序,在這種情況下,網格不會更新並使用這些項目。所以我會更新我的答案來解決這個問題。 – Nathan