ok, so let's start from the beginning:
To get a brief overview of MVVM and it´s keyplayers, i think
this article [
^]is a good start.
I also recommend this article for a solid base-implementation for
INotifyPropertyChanged
[
^]
if you are (already) familiar with the basic concepts of DataContext, Binding and Commands, i'll show you how you could implement thos concepts for your solution (using the SelectedItem-approach).
see the bottom of this solution for a complete code-sample, which also covers the ICollectionView-approach.
let´s say you have the following DataGrid defined in "MainV.xaml"
<datagrid>
ItemsSource="{Binding Data}"
SelectedItem="{Binding SelectedItem}" />
</datagrid>
WPF would try to resolve a Data-Property and a SelectedItem-Property on the object that is stored in the DataGrid´s DataContextProperty (and also observes for value-changes through INotifyPropertyChanged).
Let's take a look at the constructor of MainV:
public MainV() {
InitializeComponent();
var vm = new MainVM();
DataContext = vm;
vm.Load();
}
and the definition of MainVM:
public class MainVM : INotifyPropertyChanged {
private IEnumerable<dataitem> _Data = null;
public IEnumerable<dataitem> Data {
get {
return _Data;
}
private set {
_Data = value;
NotifyPropertyChanged("Data");
}
}
internal void Load() {
Data = (from i in Enumerable.Range(1, 100)
select new DataItem() {
Id = i,
Name = "Name " + i.ToString()
}).ToArray();
}
private DataItem _SelectedItem;
public DataItem SelectedItem {
get {
return _SelectedItem;
}
set {
_SelectedItem = value;
NotifyPropertyChanged("SelectedItem");
}
}
}
Once Load has finished and the queried data is assigned to the Data-Property, the PropertyChanged-Event is raised, which causes the Binding to it (ItemsSource="{Binding Data}") to re-evaluate.
if a user then clicks on a record, the DataGrid.SelectedItem gets changed. Cause we are binding this property to our ViewModels SelectedItem-Property, this change will automatically be propagated into our ViewModel.
In order to eliminate the Click-eventhandler you would probably use some kind of DelegateCommand (there are lot´s of posts on this topic on the web)...
MainV.xaml:
<button>
Content="Show and switch with slected item"
Command="{Binding ShowAndSwitchWithSelectedItemCommand}" />
</button>
MainVM:
private readonly DelegateCommand _ShowAndSwitchWithSelectedItemCommand;
public ICommand ShowAndSwitchWithSelectedItemCommand {
get {
return _ShowAndSwitchWithSelectedItemCommand;
}
}
public MainVM() {
_ShowAndSwitchWithSelectedItemCommand = new DelegateCommand(
p => {
MessageBox.Show(SelectedItem.Name);
var newItem = (from item in Data
where item.Id == 17
select item).FirstOrDefault();
SelectedItem = newItem;
},
p => {
return SelectedItem != null;
}
);
}
Sure, this sample has much optimization potential, but i hope it helps you to find your way into MVVM a little bit better.
Complete code-sample:
MainV.xaml:
<window>
x:Class="Earloc.MVVMSample.MainV"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main View"
Height="350"
Width="525">
<stackpanel>
<stackpanel>
Orientation="Horizontal">
<stackpanel>
<stackpanel>
Orientation="Horizontal">
<textblock>
Text="selected id:" />
<textblock>
Text="{Binding SelectedItem.Id}" />
<combobox>
ItemsSource="{Binding Data}"
SelectedItem="{Binding SelectedItem}"
DisplayMemberPath="Name" />
<button>
Content="Show and switch with slected item"
Command="{Binding ShowAndSwitchWithSelectedItemCommand}" />
<datagrid>
ItemsSource="{Binding Data}"
SelectedItem="{Binding SelectedItem}" />
<stackpanel>
<stackpanel>
Orientation="Horizontal">
<textblock>
Text="selected id:" />
<textblock>
Text="{Binding Data/Id}" />
<combobox>
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Data}"
DisplayMemberPath="Name" />
<button>
Content="Show and switch with default collection view"
Command="{Binding ShowAndSwitchWithCollectionView}"></button>
<datagrid>
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Data}"
/>
</stackpanel>
</stackpanel>
</window>
MainV.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
namespace Earloc.MVVMSample {
public partial class MainV : Window {
public MainV() {
InitializeComponent();
var vm = new MainVM();
DataContext = vm;
vm.Load();
}
}
}
MainVM.cs (due to lazyness multiple classes in here):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
namespace Earloc.MVVMSample {
public class DataItem {
public string Name { get; set; }
public int Id { get; set; }
}
public class MainVM : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string AIPropertyName) {
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(AIPropertyName));
}
private IEnumerable<dataitem> _Data = null;
public IEnumerable<dataitem> Data {
get {
return _Data;
}
private set {
_Data = value;
NotifyPropertyChanged("Data");
}
}
private ICollectionView _collectionView;
internal void Load() {
Data = (from i in Enumerable.Range(1, 100)
select new DataItem() {
Id = i,
Name = "Name " + i.ToString()
}).ToArray();
_collectionView = CollectionViewSource.GetDefaultView(Data);
_collectionView.CurrentChanged += OnCurrentChanged;
}
private void OnCurrentChanged(object sender, EventArgs e) {
_ShowAndSwitchWithCollectionView.NotifyCanExecuteChanged();
}
private DataItem _SelectedItem;
public DataItem SelectedItem {
get {
return _SelectedItem;
}
set {
_SelectedItem = value;
NotifyPropertyChanged("SelectedItem");
_ShowAndSwitchWithSelectedItemCommand.NotifyCanExecuteChanged();
}
}
private readonly DelegateCommand _ShowAndSwitchWithSelectedItemCommand;
public ICommand ShowAndSwitchWithSelectedItemCommand {
get {
return _ShowAndSwitchWithSelectedItemCommand;
}
}
private readonly DelegateCommand _ShowAndSwitchWithCollectionView;
public ICommand ShowAndSwitchWithCollectionView {
get {
return _ShowAndSwitchWithCollectionView;
}
}
public MainVM() {
_ShowAndSwitchWithSelectedItemCommand = new DelegateCommand(
p => {
MessageBox.Show(SelectedItem.Name);
var newItem = (from item in Data
where item.Id == 17
select item).FirstOrDefault();
SelectedItem = newItem;
},
p => {
return SelectedItem != null;
}
);
_ShowAndSwitchWithCollectionView = new DelegateCommand(
p => {
var selectedItem = _collectionView.CurrentItem as DataItem;
if (selectedItem == null)
return;
MessageBox.Show(selectedItem.Name);
var newItem = (from item in Data
where item.Id == 17
select item).FirstOrDefault();
_collectionView.MoveCurrentTo(newItem);
},
p => {
return _collectionView.CurrentItem != null;
}
);
}
}
public class DelegateCommand : ICommand {
private readonly Action<object> _Execute;
private readonly Func<object,> _CanExecute;
public DelegateCommand(Action<object> AIExecute, Func<object,> AICanExecute) {
if (AIExecute == null)
throw new ArgumentNullException("AIExecute");
_Execute = AIExecute;
_CanExecute = AICanExecute;
}
public DelegateCommand(Action<object> AIExecute)
:this (AIExecute, null) {
}
public bool CanExecute(object AIParameter) {
if (_CanExecute == null)
return true;
return _CanExecute(AIParameter);
}
public event EventHandler CanExecuteChanged;
internal void NotifyCanExecuteChanged() {
var handler = CanExecuteChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
public void Execute(object AIParameter) {
_Execute(AIParameter);
}
}
}