GridViewColumn not subscribing to PropertyChanged event in a ListView - wpf

I have a ListView with a GridView that's bound to the properties of a class that implements INotifyPropertyChanged, like this:
<ListView Name="SubscriptionView" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" ItemsSource="{Binding Path=Subscriptions}">
<ListView.View>
<GridView>
<GridViewColumn Width="24" CellTemplate="{StaticResource IncludeSubscriptionTemplate}"/>
<GridViewColumn Width="150" DisplayMemberBinding="{Binding Path=Name}" Header="Subscription"/>
<GridViewColumn Width="75" DisplayMemberBinding="{Binding Path=RecordsWritten}" Header="Records"/>
<GridViewColumn Width="Auto" CellTemplate="{StaticResource FilenameTemplate}"/>
</GridView>
</ListView.View>
</ListView>
The class looks like this:
public class Subscription : INotifyPropertyChanged
{
public int RecordsWritten
{
get
{
return _records;
}
set
{
_records = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("RecordsWritten"));
}
}
private int _records;
...
}
So I fire up a BackgroundWorker and start writing records, updating the RecordsWritten property and expecting the value to change in the UI, but it doesn't. In fact, the value of PropertyChanged on the Subscription objects is null. This is a puzzler, because I thought WPF is supposed to subscribe to the PropertyChanged event of data objects that implement INotifyPropertyChanged. Am I doing something wrong here?

Figured out the problem. The "Subscriptions" list was coming from a LINQ query. When I added .ToList() to the end of that query, the UI started updating properly.

Related

Filtering a Collection in a ListView in WPF

I'm creating an WPF application that allows a user to enter some details about their Employee, using Entity Framework, CRUD operations and MVVM.
So far, I have two ListViews. One contains a list of employees names (listview1), while the other (listview2) lists their details such as Date of Birth, address etc. The Image below will give you a better picture of what I'm creating;
I am using a CollectionViewSoruce to enable me to filter the results on listview2 when you select a specific name from listbox1. So far I am able to achieve this, but When I add an employee or delete, it throws an exception;
An unhandled exception of type 'System.StackOverflowException' occurred in *.UI.exe
Here are the code snippets that might help
ViewModel:
private EmployeeListViewModel()
: base("")
{
EmployeeList = new ObservableCollection<EmployeeViewModel>(GetEmployees());
this._employeeCol = new ListCollectionView(this.employeeList);
}
private ListCollectionView _employeeCol;
public ICollectionView EmployeeCollection
{
get { return this._employeeCol; }
}
private ObservableCollection<EmployeeViewModel> employeeList;
public ObservableCollection<EmployeeViewModel> EmployeeList
{
get { return employeeList; }
set
{
employeeList = value;
OnPropertyChanged("EmployeeList");
}
}
private EmployeeViewModel selectedEmployee = null;
public EmployeeViewModel SelectedEmployee
{
get
{
return selectedEmployee;
}
set
{
selectedEmployee = value;
OnPropertyChanged("SelectedEmployee");
EmployeeCollection.Filter = new Predicate<object>(o => SelectedEmployee != null && o != null && ((EmployeeViewModel)o).EmployeeID == SelectedEmployee.EmployeeID);
}
}
internal ObservableCollection<EmployeeViewModel> GetEmployees()
{
if (employeeList == null)
employeeList = new ObservableCollection<EmployeeViewModel>();
employeeList.Clear();
foreach (DataObjects.Employee i in new EmployeeRepository().GetAllEmployees())
{
EmployeeViewModel c = new EmployeeViewModel(i);
employeeList.Add(c);
}
return employeeList;
}
ListView2 - EmployeeListView;
<ListView Name="lsvEmpoyeeList" Height="170" Width="700"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
HorizontalAlignment="Center"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding EmployeeCollection}"
SelectedItem="{Binding SelectedEmployee}">
<ListView.View>
<GridView>
<GridViewColumn Header="Position" DisplayMemberBinding="{Binding Position}" Width="100" />
<GridViewColumn Header="DateOfBirth" DisplayMemberBinding="{Binding DateOfBirth, StringFormat={}\{0:dd/MM/yyyy\}}" Width="100" />
</GridView>
</ListView.View>
</ListView>
ListView1 - EmployeeSetUpView;
<ListView Height="380" HorizontalAlignment="Left" Name="lsNames" VerticalAlignment="Top" Width="170"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
SelectedItem="{Binding SelectedEmployee}"
ItemsSource="{Binding EmployeeList}" Grid.RowSpan="2" Grid.Row="1">
<ListView.View>
<GridView>
<GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" Width="80" />
<GridViewColumn Header="Surname" DisplayMemberBinding="{Binding Surname}" Width="80" />
</GridView>
</ListView.View>
</ListView>
<ContentControl Grid.Column="1" Grid.Row="1" HorizontalAlignment="Center"
Content="{Binding}" ContentTemplate="{StaticResource EmployeeListView}" />
As you can see, I have put the filter within the setaccessor. I placed it within the constructor but what seems to happen is that none of the details appeared on the ListView2.
Furthermore, if I select a row from listview2 rather then from listview1, it also produces the StackOverFlowException which I am unsure why.
Any help would be appreciated or advice. Also, sorry for the large question!
I don't think the UI knows that EmployeeCollection has changed
Try adding a PropertyChanged event for EmployeeCollection in the SelectedEmployee setter after the filter is applied.
public EmployeeViewModel SelectedEmployee
{
get { return selectedEmployee;}
set
{
selectedEmployee = value;
OnPropertyChanged("SelectedEmployee");
EmployeeCollection.Filter = new Predicate<object>(o => SelectedEmployee != null && o != null && ((EmployeeViewModel)o).EmployeeID == SelectedEmployee.EmployeeID);
// EmployeeCollection view has changed, Notify UI
OnPropertyChanged("EmployeeCollection");
}
}
And as for the StackOverflowException I think this is caused by the fact both ListView have a TwoWay binding on SelectedEmployee, so when one ListView1 changes SelectedItem it causes ListView2 to update its selected item which updates ListView1 and so on, and so on.
Try setting the binding to OneWay for SelectedEmployee on ListView2
SelectedItem="{Binding SelectedEmployee, Mode=OneWay}">

WPF ListView Key Search Issue

I am using MVVM to show a list data in ListView. The ListView is very simple as follow:
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Customers}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding FirstName}">
<GridViewColumnHeader Width="100" Content="First Name" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding LastName}">
<GridViewColumnHeader Width="100" Content="Last Name" />
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I bind a list of customer objects into ListView and the issue comes when I highlight a item in the ListView and start typing T. The ListView will highlight the next item each time you type a T character.
By spending some time to find out the issue, I track down it is because that my customer object has namespace TestMVVMProject so I think the ToString() function for customer object is always return TestMVVMProject.Customer
Can anyone give me some idea how to stop this behaviour?
This is because by default the ListView implements text searching ability. You can stop this behavious byusing the IsTextSearchEnabled property on the list box. Try
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Customers}"
IsTextSearchEnabled="False">
This should disable the text search when you do not want it.
Edit:
After some investigation I found next solution:
<ListView
TextSearch.TextPath="FirstName"
...>
Hope this will help.
Original answer:
Just override ToString() on View Model and you will get proper behavior.
public class Customer
{
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}

WPF - How to bind generic method in XAML?

I have a class with a static method that returns a Dictionary. The signature of the method is as follows:
public static Dictionary<int, string> CodeLookup<T>() where T : EntityCodeBase
At the moment, I'm using this method to bind to my comboboxes in the code behind, like so:
this.cboState.ItemsSource = CodeCache.CodeLookup<StateCode>();
Would someone please be able to point me in the correct direction for doing this in XAML so that I can remove this kind of stuff from my codebehind?
Thanks,
Sonny
Not directly binding to a particular method. You should create a property and bound that to it.
public Dictionary<int, string> Code {
get { return CodeCache.CodeLookup<StateCode>(); }
}
looks like you cannot do it for generic methods
more info
Here is a property in my viewModel:
public ObservableCollection<Contact> AllContacts
{
get { return _applicationViewModel.CurrentContacts; }
}
And here is my XAML:
<ListView Margin="5" ItemsSource="{Binding Path=AllContacts}">
<ListView.View>
<GridView>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}" />
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}" />
<GridViewColumn Header="Work Phone" DisplayMemberBinding="{Binding Path=OfficePhone, Converter={StaticResource phoneConverter}}" />
<GridViewColumn Header="Cell Phone" DisplayMemberBinding="{Binding Path=CellPhone, Converter={StaticResource phoneConverter}}" />
<GridViewColumn Header="Email Address" DisplayMemberBinding="{Binding Path=PrimaryEmail}" />
</GridView>
</ListView.View>
</ListView>
Just set the DataContext to the ViewModel and you get everything you need. Check out MVVM pattern for more info.
There are ways to bind to a static method but if all you are doing is basic databinding then it's a bit of overkill. Take a look at Actions if you are interested.

Listview of items from a object selected in another listview

Ok the title maybe a little confusing. I have a database with the table Companies wich has one-to-many relotionship with another table Divisions ( so each company can have many divisions ) and division will have many employees.
I have a ListView of the companies. What I wan't is that when I choose a company from the ListView another ListView of divisions within that company appears below it. Then I pick a division and another listview of employees within that division appaers below that. You get the picture.
Is there anyway to do this mostly inside the XAML code declaritively (sp?). I'm using linq so the Company entity objects have a property named Division wich if I understand linq correctly should include Division objects of the divisions connected to the company. So after getting all the companies and putting them as a itemsource to CompanyListView this is where I currently am.
<ListView x:Name="CompanyListView"
DisplayMemberPath="CompanyName"
Grid.Row="0" Grid.Column="0" />
<ListView DataContext="{Binding ElementName=CompanyListView, Path=SelectedItem}"
DisplayMemberPath="Division.DivisionName"
Grid.Row="1" Grid.Column="0" />
I know I'm way off but I was hoping by putting something specific in the DataContext and DisplayMemberPath I could get this to work. If not then I have to capture the Id of the company I guess and capture a select event or something.
Another issue but related is the in the seconde column besides the lisview I wan't to have a details/edit view for the selected item. So when only a company is selected details about that will appear then when a division under the company is picked It will go there instead, any ideas?
You can use the MVVM pattern to bind your XAML to a class that contains information for your ListViews and reset the content for the Division collection based on the selected Comany item.
Here is a basic sample to get you started.
Here are two ListView controls in XAML:
<Window x:Class="MultiListView.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="800">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemsSource="{Binding Companies}"
SelectedItem="{Binding Company, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding CompanyName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
<ListView Grid.Row="1"
ItemsSource="{Binding Divisions}"
SelectedItem="{Binding Division, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding DivisionName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</DockPanel>
</Window>
In the code-behind set the DataContext for the Window to a class the contains the binding references used in the XAML.
public partial class MainView : Window
{
MainViewModel _mvm = new MainViewModel();
public MainView()
{
InitializeComponent();
DataContext = _mvm;
}
}
The following class uses the MVVM pattern, which you can find lots of information
on in StackOverFlow. This class contains the data that the XAML binds with. Here
is where you can use LINQ to load/reload the collections.
using System.Collections.ObjectModel;
using MultiListView.Models;
namespace MultiListView.ViewModels
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
_companies = new ObservableCollection<Company>();
_companies.Add(new Company("Stackoverflow", "QA web site"));
_companies.Add(new Company("Fog Creek", "Agile Bug Tracking"));
_companies.Add(new Company("Second Beach", "Not sure yet"));
_divisions = new ObservableCollection<Division>();
}
private ObservableCollection<Company> _companies;
public ObservableCollection<Company> Companies
{
get { return _companies; }
set
{
_companies = value;
OnPropertyChanged("Companies");
}
}
private Company _company;
public Company Company
{
get { return _company; }
set
{
_company = value;
// load/reload divisions for the selected company here
LoadDivisions();
OnPropertyChanged("Company");
}
}
// hack to keep the example simpe...
private void LoadDivisions()
{
_divisions.Clear();
// use db or linq here to filiter property
if ( _company != null )
{
if ( _company.CompanyName.Equals("Stackoverflow") )
{
_divisions.Add( new Division("QA", "Test all day"));
_divisions.Add( new Division("Write", "Doc all day"));
_divisions.Add( new Division("Code", "Code all day"));
}
else if (_company.CompanyName.Equals("Fog Creek"))
{
_divisions.Add(new Division("Test", "Test all day"));
_divisions.Add(new Division("Doc", "Doc all day"));
_divisions.Add(new Division("Develop", "Code all day"));
}
else if (_company.CompanyName.Equals("Second Beach"))
{
_divisions.Add(new Division("Engineering", "Code all day"));
}
}
}
private ObservableCollection<Division> _divisions;
public ObservableCollection<Division> Divisions
{
get { return _divisions; }
set
{
_divisions = value;
OnPropertyChanged("Divisions");
}
}
private Division _division;
public Division Division
{
get { return _division; }
set
{
_division = value;
OnPropertyChanged("Division");
}
}
}
}
OnPropertyChanged implements INotifyPropertyChanged.
When the properties of a ViewModel change, the Views bound to the ViewModel receive a notification when the ViewModel raises its PropertyChanged event.
You can find examples in most MVVM libraries, or look to MSDN for an example.
If Divisions is a property of Company, you could probably do something like this:
<Window x:Class="MultiListView.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="800">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView Grid.Row="0"
x:Name="lvCompanies"
ItemsSource="{Binding Companies}"
SelectedItem="{Binding Company, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding CompanyName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
<ListView Grid.Row="1"
ItemsSource="{Binding ElementName='lvCompanies', Path=SelectedItem.Divisions}"
SelectedItem="{Binding Division, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding DivisionName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</DockPanel>
</Window>

Binding a wpf listview to a Dataset....Possible..?

I was struggling moving to Wpf,I am just stuck while trying out databinding to a lsitview.I want to databind a listview to a dataset(dataset because the data i want to display in columns belongs to different tables).I am attaching a sample code that i am trying with.It works alright but the listliew only shows one row.What could be wrong.Can anyone guide me through.All the samples available are using datatables.None specifies about binding to a dataset.Pls help..any input will be highly appreciated...thanks in advance
My Xaml
<Grid>
<TextBox Text="" Height="20" Width="100" HorizontalAlignment="Left" Margin="15,13,0,0" VerticalAlignment="Top"></TextBox>
<TextBox Text="" Height="20" Width="100" HorizontalAlignment="Left" Margin="15,42,0,0" VerticalAlignment="Top"></TextBox>
<ListView Margin="15,89,63,73" Name="lst" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=T1/Name}"></GridViewColumn>
<GridViewColumn Header="Place" DisplayMemberBinding="{Binding Path=T2/Name}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<!--<Button Height="19" HorizontalAlignment="Right" Name="button2" VerticalAlignment="Top" Width="46" Margin="0,42,63,0" Click="button2_Click">Add</Button>-->
<Button Height="19" HorizontalAlignment="Right" Name="button1" VerticalAlignment="Top" Width="46" Click="button1_Click" Margin="0,43,63,0">Add</Button>
My Code
Dt1 = new DataTable("T1");
Dt1.Columns.Add("Name");
Dt1.Rows.Add("abc1");
Dt1.Rows.Add("abc2");
Dt2 = new DataTable("T2");
Dt2.Columns.Add("Name");
Dt2.Rows.Add("xyz1");
Dt2.Rows.Add("xyz1");
Ds = new DataSet();
Ds.Tables.Add(Dt1);
Ds.Tables.Add(Dt2);
lst.DataContext = Ds;
Hi am in full accord with Andy and Thomas. They both have explained the concept elegantly.
I am only showing the steps of doing the same only with dataset.
The MVVM (ModelView ViewModel) I am not discussing here.
The Xaml looks like this
<Grid Name="myGrid" ShowGridLines="False">
<Label Height="28" Margin="12,5,0,0" Name="lblName" VerticalAlignment="Top" HorizontalAlignment="Left" Width="55">Name</Label>
<TextBox Height="23" Margin="73,8,85,0" Name="txtName" VerticalAlignment="Top" />
<Label Height="28" Margin="12,39,0,0" Name="lblPlace" VerticalAlignment="Top" HorizontalAlignment="Left" Width="55">Place</Label>
<TextBox Height="23" Margin="73,44,85,0" Name="txtPlace" VerticalAlignment="Top" />
<Button Height="23" HorizontalAlignment="Left" Margin="20,82,0,0" Name="btnAddRecord" VerticalAlignment="Top" Width="75" Click="btnAddRecord_Click">Add Record</Button>
<ListView Margin="31,119,27,45" Name="listView" *ItemsSource="{Binding}"*>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Place" DisplayMemberBinding="{Binding Place}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
In the .CS file create a dataset
private DataSet MyDataSet()
{
DataTable dtInformation1 = new DataTable();
dtInformation1.Columns.Add("Name");
dtInformation1.Columns.Add("Place");
dtInformation1.Rows.Add(txtName.Text, txtPlace.Text);
DataTable dtInformation2 = new DataTable();
dtInformation2.Columns.Add("Name");
dtInformation2.Columns.Add("Place");
dtInformation2.Rows.Add(txtName.Text + "2", txtPlace.Text + "2");
DataSet Ds = new DataSet();
Ds.Tables.Add(dtInformation1);
Ds.Tables.Add(dtInformation2);
return Ds;
}
Next in the Button's click event write the following
private void btnAddRecord_Click(object sender, RoutedEventArgs e)
{
**listView.ItemsSource = MyDataSet().Tables[0].DefaultView;
- OR -
listView.ItemsSource = MyDataSet().Tables[1].DefaultView;**
}
N.B.~ You cannot assign the source of the ListView a dataset.
Why ? You may ask?
A dataset, in simple terms , is a collection of data tables.
Suppose you have 5 different datatables. And say none of their column names as well as column numbers are same.
Now you have assigned all those to your dataset. How will the controls source know that which source it has to bind?
Inorder to overcome such a situation, either make a custom datatable that will have all the columns of those discreet datatables and assign the values to this custom one and then bind to the source.
Or you need to explicitly specify the datatable in the datasource
But I always prefer to use MVVM pattern for this kind of operations.
I'm not sure what you're trying to do here... Is that the result you expect ?
abc1 xyz1
abc2 xyz2
There is no relation between your tables, so the binding system can't guess which row of T1 you want to associate with which row of T2... You should either put all data in the same table, or use a DataRelation between the two tables (but that would require extra fields for the join). You would then set the DataTable as the ItemsSource, not the DataSet.
Alternatively, you could create a dedicated class to hold the data, as suggested by Andy
WPF Binding works off of properties. For example, consider the following Person object:
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
You could then modify the XAML for your ListView to display a collection of Person objects by doing the following:
<ListView Margin="15,89,63,73" Name="lst" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name"
DisplayMemberBinding="{Binding Path=FirstName}" />
<GridViewColumn Header="Last Name"
DisplayMemberBinding="{Binding Path=LastName}" />
</GridView>
</ListView.View>
</ListView>
This will display the object's first name in the first column, and their last name in the second column (assuming that the DataContext of the ListView is a collection of Person objects).
In theory, to bind the values in a DataTable to a ListView, you could set the ItemsSource to the DataTable's Rows property. The problem becomes that the DataRow class doesn't expose properties for its columns - there is only the Item property that takes an argument specifying the row. To my knowledge, XAML does not support properties that take arguments, so I don't think that it is possible to use a DataTable as the ItemsSource for a ListView.
You do have some other options, however. You could create a strongly typed DataSet, which would expose a DataTable with a property for each column. You can then bind each GridViewColumn to the correct property.
Another approach would be to not use a DataTable at all. Your data layer would still load the data from your source into the DataTable, but it would then convert that data into normal objects. You could create an instance of ObservableCollection, add each of the objects into it, and then bind the ListView to that. Each GridViewColumn would just bind to the corresponding property of the objects.
Updated:
In answer to OP's further question:
Can I use the xsd for the purpose?.It
holds a property for each datatable..
You need more than just a property for a DataTable. You'd also need a property for each value in each row of the DataTable. Otherwise there is no property for the ListView's columns to bind to.
This worked for me.
public partial class MainWindow : Window
{
public ObservableCollection<DataTable> _observableCollection =
new ObservableCollection<DataTable>();
public MainWindow()
{
InitializeComponent();
DataTable dt1 = new DataTable();
dt1.Columns.Add("OrderID");
dt1.Columns.Add("CustomerID");
dt1.Columns.Add("ProductID");
dt1.Rows.Add("test1", "test2", "test3");
DataTable dt2 = new DataTable();
dt2.Columns.Add("OrderID");
dt2.Columns.Add("CustomerID");
dt2.Columns.Add("ProductID");
dt2.Rows.Add("test4", "test5", "test6");
_observableCollection.Add(dtInformation1);
_observableCollection.Add(dtInformation2);
}
public ObservableCollection<DataTable> _Collection
{ get { return _observableCollection; } }
ListView XAML
<ListView Height="Auto"
HorizontalAlignment="Stretch"
Name="somename"
ItemsSource="{Binding _Collection}" VerticalAlignment="Stretch" Width="Auto" >
<GridViewColumn Header="OrderID" Width="auto" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Tag="{Binding OrderID}" Content="{Binding OrderID}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="auto" DisplayMemberBinding="{Binding CustomerID}" Header="CustomerID" />
<GridViewColumn Width="auto" DisplayMemberBinding="{Binding ProductID}" Header="ProductID" />

Resources