With a WPF virtualised DataGrid I need to employ a 'select all' method. Iterating though the grid itself won't work as it will only select the records that at visible in the UI - but of course if they are selected using the mouse they are retained. I'm guessing that something must change in the DataTable, but what? If so can it be manipulated so that all the records are selected in the DataGrid?
Thanks
======================== ADDED===========================
Eran - thanks for your reply but having a job wiring this up
Create DataGrid
Dim DGV As New CustomControl.DGVx
With DGV
.Name = "Invoice_AdHoc_DGV"
.AutoGenerateColumns = False
.SelectionMode = SelectionMode.Multiple
End With
RegisterControl(Invoice_AdHoc_Grid, DGV)
RightGrid.Children.Add(DGV)
Bind to DataTable
DGV.ItemsSource = AdHocDT.DefaultView
Created the class from your answer
Public Class ObjectSelectAll
Implements System.ComponentModel.INotifyPropertyChanged
Public Property Name() As String
Private vIsSelected As Boolean
Public Property IsSelected() As Boolean
Get
Return vIsSelected
End Get
Set(value As Boolean)
vIsSelected = value
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("IsSelected"))
End Set
End Property
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
cs :
private List<MyObject> _items;
public List<MyObject> Items
{
get
{
if(_items == null)
_items = new List<MyObject> { new MyObject { Name = "obj1" }, new MyObject { Name = "obj2" }, new MyObject { Name = "obj3" }, new MyObject { Name = "obj4" } };
return _items;
}
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
foreach (var item in Items)
{
item.IsSelected = true;
}
}
}
public class MyObject : INotifyPropertyChanged
{
public string Name { get; set; }
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
xaml :
<Button Click="Button_Click_1" Content="Select All"/>
<DataGrid Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Items}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
Finally managed to resolve this issue
Added another column to the DataTable
With AdHocDT.Columns
.Add("Selected", GetType(Boolean))
End With
When 'Select All' is clicked set this to True for all rows and in the Grid the value is updated to true as well, then add this to ensure that the Grid shows it as selected when the virtual data is scrolled into view
Private Sub Invoice_AdHoc_DGV_ItemsChanged(sender As Object, e As EventArgs)
Try
Dim DGV As CustomControl.DGVx = Invoice_AdHoc_Grid.FindName("Invoice_AdHoc_DGV")
Dim i As Integer = 0
For Each Row As DataRowView In DGV.Items
Dim vRow As DevComponents.WPF.Controls.AdvGridRow = CType(DGV.ItemContainerManager.ContainerFromItem(DGV.Items(i), False), DevComponents.WPF.Controls.AdvGridRow)
If Not vRow Is Nothing Then
If Row("Selected") = True Then
vRow.IsSelected = True
End If
End If
i += 1
Next
Catch ex As Exception
EmailError(ex)
End Try
End Sub
and handle the event
AddHandler DGV.ItemContainerManager.VisibleContainersChanged, AddressOf Invoice_AdHoc_DGV_ItemsChanged
Now the Grid shows all the rows as selected :-)
Related
I have an editable combobox that I bind the name property from a list of objects to (QBD.Name). What I can't figure out is how to allow editing of those names - I keep getting an object reference error when I try to edit.
I believe I need to implement INotifyPropertyChanged, but I'm not entirely sure how that works.
Here's the binding code:
<ComboBox Name="cmbBxQBDNames" Text="Please Select a QBD" ItemsSource="{Binding Path=QBDs, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" DisplayMemberPath="QBD.Name" SelectedValuePath="QBD.Name" IsEditable="True" VerticalAlignment="Center" HorizontalAlignment="Stretch" Width="auto" MinWidth="25" Margin="45,0,0,0" Foreground="Black"></ComboBox>
Here's the objects I'm binding to:
Public Class QBDs
Private QBDsLocal As New ObservableCollection(Of QBD)
Public Property QBDs As ObservableCollection(Of QBD)
Get
Return QBDsLocal
End Get
Set(value As ObservableCollection(Of QBD))
QBDsLocal = value
End Set
End Property
End Class
Public Class QBD
Private NameLocal As String
Public Property Name As String
Get
Return NameLocal
End Get
Set(value As String)
NameLocal = value
End Set
End Property
End Class
Also, when I select an object from the combobox, how can I have it's name displayed in the combobox? Currently, it remains blank.
i think your problem is with DisplayMemberPath.
try with DisplayMemberPath = "Name"
let me know if it fails.
I couldn't get why this fails,Please see the Code i've written to test your problem.
<ComboBox ItemsSource="{Binding MyCollection}" DisplayMemberPath="FName" SelectedValuePath="SName" Height="40" IsEditable="True" />
//My DataContext Goes here
public class Model
{
private string sName;
public string SName
{
get { return sName; }
set { sName = value; }
}
private string fName;
public string FName
{
get { return fName; }
set { fName = value; }
}
}
public class ViewModel
{
private ObservableCollection<Model> myColl;
public ObservableCollection<Model> MyCollection
{
get { return myColl; }
set { myColl = value; }
}
public ViewModel()
{
MyCollection = new ObservableCollection<Model>();
MyCollection.Add(new Model { FName = "Tony", SName = "Strark" });
MyCollection.Add(new Model { FName = "Bruce", SName = "Wayne" });
MyCollection.Add(new Model { FName = "Miranda", SName = "Frost" });
}
}
//and I ve set ViewModel as DataContext,
This works just fine for me please check it once,and please forgive me for not giving the code in VB .
Regards,
Kumar
I have a datagrid like below in my WPF application.
<Window x:Class="MyApp.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<DataGrid x:Name="dgTest" ItemsSource="{Binding TestSource}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Width="125" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Column1}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="500" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Column2}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Click="SaveButton_Click">Save</Button>
</Grid>
</Window>
I am binding it using the following code. Now my requirement is when the user enters some text into these textboxes inside datagrid and click on save button, it should update the database. How can I achieve this?
namespace MyApp
{
public partial class TestWindow: Window
{
private ObservableCollection<Test> _testSource
public ObservableCollection<Test> TestSource
{
get
{
return _testSource;
}
set
{
_testSource = value;
OnPropertyChanged("TestSource");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public TestWindow()
{
InitializeComponent();
TestSource= new ObservableCollection<Test>();
string strConnString = Application.Current.Properties["connectionStr"].ToString();
SqlConnection con = new SqlConnection(strConnString);
SqlCommand cmd = new SqlCommand("SELECT Column1,Column2 FROM MyTable", con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dtTest = new DataTable();
da.Fill(dtTest);
foreach (DataRow row in dtTest)
{
Test cd = new Test();
cd.Column1 = row["Column1"].ToString();
cd.Column2 = row["Column2"].ToString();
TestSource.Add(cd);
}
this.DataContext = this;
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
// here I need to get the updated ObservableCollection, but now it is showing old data
foreach Test t in TestSource)
{
string a = t.Column1;
string b = t.Column2;
}
}
}
public class Test:
{
public string Column1{ get; set; }
public string Column2{ get; set; }
}
}
Thanks
When creating your own UI in DataGridTemplateColumn (or a custom DataGrid.RowStyle for that matter), the DataGrid changes the UpdateSourceTrigger (i.e. when the underlying data should be updated) on all your bindings to Explicit if you didn't specify them yourself.
That "feature" is described briefly here: 5 Random Gotchas with the WPF DataGrid, and even though the author says this happens whether or not you set the UpdateSourceTrigger yourself, setting it yourself does actually work (at least in .Net 4.0).
Use LostFocus to mimic the default TextBox behavior (and PropertyChanged on CheckBox etc):
...
<TextBox Text="{Binding Column1, UpdateSourceTrigger=LostFocus}"></TextBox>
...
<TextBox Text="{Binding Column2, UpdateSourceTrigger=LostFocus}"></TextBox>
...
There are a couple of side notes I would like to make.
1) You dont need to have a setter on your TestSource property. You set this value once, and before the DataContext is set, so this is pointless.
2) You dont implement INotifyPropertyChanged on the Test class
3) You load the data on the UI thread. This can cause the App to freeze (become unresponsive) while the Data is being loaded.
Try updating the C# code to the below:
namespace MyApp
{
public partial class TestWindow: Window
{
private ObservableCollection<Test> _testSource = new ObservableCollection<Test>();
public TestWindow()
{
InitializeComponent();
//NOTE: this blocks the UI thread. Slow DB/Network will freeze the App while we wait.
// This should be done on a background thread.
string strConnString = Application.Current.Properties["connectionStr"].ToString();
SqlConnection con = new SqlConnection(strConnString);
SqlCommand cmd = new SqlCommand("SELECT Column1,Column2 FROM MyTable", con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dtTest = new DataTable();
da.Fill(dtTest);
foreach (DataRow row in dtTest)
{
Test cd = new Test();
cd.Column1 = row["Column1"].ToString();
cd.Column2 = row["Column2"].ToString();
TestSource.Add(cd);
}
this.DataContext = this;
}
public ObservableCollection<Test> TestSource { get { return _testSource; } }
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
var rowIdx = 0;
foreach(var t in TestSource)
{
string a = t.Column1;
string b = t.Column2;
Console.WriteLine("Row {0}, col1='{1}', col2='{2}'", rowIdx++, a, b);
}
}
}
public sealed class Test : INotifyPropertyChanged
{
private string _column1;
private string _column2;
public string Column1
{
get{return _column1;}
set
{
if(_column1!=value)
{
_column1 = value;
OnPropertyChanged("Column1");
}
}
}
public string Column2
{
get{return _column2;}
set
{
if(_column2!=value)
{
_column2 = value;
OnPropertyChanged("Column2");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
}
}
You may also want to update the Binding to twoway, however I do think this is the default.
<TextBox Text="{Binding Column1, Mode=TwoWay}" />
I am new to MVVM . I am using wpf with MVVM in my project . So I am testing things right now before diving into an app I need to write.
My page (EmpDetailsWindow.xaml) is like this
<Grid>
<DataGrid Name="dgEmployee" Grid.Row="0" AutoGenerateColumns="True" ItemsSource="{Binding EmployeeDataTable}" CanUserAddRows="True" CanUserDeleteRows="True" IsReadOnly="False" />
<Button x:Name="btnSubmit" Content="Submit" Command="{Binding SubmitCommand}" CommandParameter="sample param" HorizontalAlignment="Left" Margin="212,215,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
and my model (EmpDetailsWindowViewModel) is as below
public class EmpDetailsWindowViewModel : INotifyPropertyChanged
{
public ICommand SubmitCommand { get; set; }
public EmpDetailsWindowViewModel()
{
EmployeeDataTable = DataTableCreator.EmployeeDataTable();
GenderDataTable = DataTableCreator.GenderDataTable();
SubmitCommand = new SubmitCommand();
}
DataTable _employeeDataTable;
public DataTable EmployeeDataTable
{
get { return _employeeDataTable;}
set
{
_employeeDataTable = value;
RaisePropertyChanged("EmployeeDataTable");
}
}
DataTable _genderDataTable;
public DataTable GenderDataTable
{
get { return _genderDataTable; }
set
{
_genderDataTable = value;
RaisePropertyChanged("GenderDataTable");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
datagrid is successfully bound to the datatable . Now I have a column "Gender" in datagrid. This should be a combobox and the item source of the cobobox is got from GenderDataTable of the view model . How can I achieve this ?
You can do it like this
<DataGrid AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"/>
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyName == "Gender")
{
var cb = new DataGridComboBoxColumn();
cb.ItemsSource = (DataContext as MyVM).GenderDataTable;
cb.SelectedValueBinding = new Binding("Gender");
e.Column = cb;
}
}
There doesn't seem to quite be a complete answer here so I'll post what I found from this question and from experimentation. I'm sure this breaks many rules but it's simple and it works
public partial class MainWindow : Window
{
// define a dictionary (key vaue pair). This is your drop down code/value
public static Dictionary<string, string>
dCopyType = new Dictionary<string, string>() {
{ "I", "Incr." },
{ "F", "Full" }
};
// If you autogenerate columns, you can use this event
// To selectively override each column
// You need to define this event on the grid in the event tab in order for it to be called
private void Entity_AutoGeneratingColumn(object sender,
DataGridAutoGeneratingColumnEventArgs e)
{
// The name of the database column
if (e.PropertyName == "CopyType")
{
// heavily based on code above
var cb = new DataGridComboBoxColumn();
cb.ItemsSource = dCopyType; // The dictionary defined above
cb.SelectedValuePath = "Key";
cb.DisplayMemberPath = "Value";
cb.Header = "Copy Type";
cb.SelectedValueBinding = new Binding("CopyType");
e.Column = cb;
}
}
} // end public partial class MainWindow
I have the following scenario:
I have one window say MainWindow where I am displaying the list of activities as per the specific user from the database. There is a one button present on the window. By clicking on that button a new window is getting opened having all the list of activities from the master table. Now I want add a chechbox on the second window against each item dynamically so that user can select/deselect the activities. Those selected/deselected values should save in the database and Parent/MainWindow should refreshed after clicking on the done button and changes should reflect in the MianWindow. But I am not getting how to dynamically creating the checkboxes against each list item and binding with the xaml and select/deselect the checkbox.
Kindly suggest with samples or examples.
Thanks
You can customize your listviewitem using the ListView's ItemTemplate. Add a checkbox
and a textblock to a panel which would constitute your datatemplate.
Update
The Model:
public class Activity
{
public Activity(int id, string name)
{
ID = id;
Name = name;
}
public int ID { get; set; }
public string Name { get; set; }
}
The ViewModel for ListViewItem in Second Window:
public class ActivityViewModel
{
Activity _model;
public ActivityViewModel(Activity model, bool isSelected)
{
_model = model;
IsSelected = isSelected;
}
public string Name { get { return _model.Name; } }
/* Since the view has a checkbox and it requires a bool value for binding
we create this property */
public Nullable<bool> IsSelected { get; set; }
}
The DataAccess
public class DaoDailyActivities
{
string activityName = "";
bool IsSelected;
SqlConnection con = new SqlConnection("server=172.16.32.68;database=ParentalHealth;uid=sa;pwd=Emids123");
public IEnumerable<Activity> GetActivities()
{
SqlCommand cmd = new SqlCommand("SP_GetActivities", con);
cmd.CommandType = CommandType.StoredProcedure;
con.Open(); /* It is safe to open connections in a try block */
SqlDataReader readerActivities = cmd.ExecuteReader();
while (readerActivities.Read())
{
yield new Activity(readerActivities["ActivityID"].ToString(), readerActivities["ActivityName"].ToString());
}
}
}
The ViewModel for SecondWindow:
public class SecondWindowViewModel : ViewModelBase
{
DaoDailyActivities _rep = new DaoDailyActivities();
public ObservableCollection<ActivityViewModel> AllActivities { get; set; }
public SecondWindowViewModel()
{
LoadAllActivities();
}
LoadAllActivities()
{
foreach(Activity activity in _rep.GetActivities())
{
AllActivities.Add(new ActivityViewModel(activity, (activity.ID % 2 == 0)));
}
}
}
The XAML:
<ListView ItemsSource="{Binding AllActivities}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
<CheckBox IsChecked="{Binding Path=IsSelected}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListView>
I'm using the WPF Toolkit's DataGrid. I've enabled the property on the DataGrid to allow for multi-selecting of rows. How do I get the SelectedItems out? I'm using an MVVM framework to bind my ViewModel to my View.
Thanks!
Taking Bill's answer, merging options 1 and 2, adding a sprinkling of attached properties as an alternative to writing code-behind, I came up with a Behavior.
Firstly, we have the behavior:
Public Class SelectedItemsBehavior
Public Shared ReadOnly SelectedItemsChangedHandlerProperty As DependencyProperty =
DependencyProperty.RegisterAttached("SelectedItemsChangedHandler",
GetType(mvvm.RelayCommand), GetType(SelectedItemsBehavior),
New FrameworkPropertyMetadata(New PropertyChangedCallback(AddressOf OnSelectedItemsChangedHandlerChanged)))
Public Shared Function GetSelectedItemsChangedHandler(ByVal element As DependencyObject) As mvvm.RelayCommand
If element Is Nothing Then Throw New ArgumentNullException("element")
Return element.GetValue(SelectedItemsChangedHandlerProperty)
End Function
Public Shared Sub SetSelectedItemsChangedHandler(ByVal element As DependencyObject, ByVal value As mvvm.RelayCommand)
If element Is Nothing Then Throw New ArgumentNullException("element")
element.SetValue(SelectedItemsChangedHandlerProperty, value)
End Sub
Private Shared Sub OnSelectedItemsChangedHandlerChanged(d As DependencyObject,
e As DependencyPropertyChangedEventArgs)
Dim dataGrid As DataGrid = DirectCast(d, DataGrid)
If e.OldValue Is Nothing AndAlso e.NewValue IsNot Nothing Then
AddHandler dataGrid.SelectionChanged, AddressOf ItemsControl_SelectionChanged
End If
If e.OldValue IsNot Nothing AndAlso e.NewValue Is Nothing Then
RemoveHandler dataGrid.SelectionChanged, AddressOf ItemsControl_SelectionChanged
End If
End Sub
Public Shared Sub ItemsControl_SelectionChanged(sender As Object,
e As SelectionChangedEventArgs)
Dim dataGrid As DataGrid = DirectCast(sender, DataGrid)
Dim itemsChangedHandler As RelayCommand = GetSelectedItemsChangedHandler(dataGrid)
itemsChangedHandler.Execute(dataGrid.SelectedItems)
End Sub
End Class
C#:
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class SelectedItemsBehavior
{
public static readonly DependencyProperty SelectedItemsChangedHandlerProperty = DependencyProperty.RegisterAttached("SelectedItemsChangedHandler", typeof(mvvm.RelayCommand), typeof(SelectedItemsBehavior), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnSelectedItemsChangedHandlerChanged)));
public static mvvm.RelayCommand GetSelectedItemsChangedHandler(DependencyObject element)
{
if (element == null)
throw new ArgumentNullException("element");
return element.GetValue(SelectedItemsChangedHandlerProperty);
}
public static void SetSelectedItemsChangedHandler(DependencyObject element, mvvm.RelayCommand value)
{
if (element == null)
throw new ArgumentNullException("element");
element.SetValue(SelectedItemsChangedHandlerProperty, value);
}
private static void OnSelectedItemsChangedHandlerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = (DataGrid)d;
if (e.OldValue == null && e.NewValue != null) {
dataGrid.SelectionChanged += ItemsControl_SelectionChanged;
}
if (e.OldValue != null && e.NewValue == null) {
dataGrid.SelectionChanged -= ItemsControl_SelectionChanged;
}
}
public static void ItemsControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid dataGrid = (DataGrid)sender;
RelayCommand itemsChangedHandler = GetSelectedItemsChangedHandler(dataGrid);
itemsChangedHandler.Execute(dataGrid.SelectedItems);
}
}
Then we add it to the datagrid in XAML:
<DataGrid AutoGenerateColumns="False" FontFamily="Tahoma" FontSize="9"
ItemsSource="{Binding Path=ResultsVM}"
mvvm:SelectedItemsBehavior.SelectedItemsChangedHandler="{Binding Path=ResultsSelectionChangedCommand}" />
Then we code the RelayCommand in the ViewModel:
Public ReadOnly Property ResultsSelectionChangedCommand() As mvvm.RelayCommand
Get
If _resultsSelectionChangedCommand Is Nothing Then
_resultsSelectionChangedCommand = New mvvm.RelayCommand(AddressOf ResultsSelectionChanged)
End If
Return _resultsSelectionChangedCommand
End Get
End Property
Public Sub ResultsSelectionChanged(ByVal selectedItems As Object)
_resultsSelectedItems.Clear()
For Each item In selectedItems
_resultsSelectedItems.Add(item)
Next
End Sub
C#:
public mvvm.RelayCommand ResultsSelectionChangedCommand {
get {
if (_resultsSelectionChangedCommand == null) {
_resultsSelectionChangedCommand = new mvvm.RelayCommand(ResultsSelectionChanged);
}
return _resultsSelectionChangedCommand;
}
}
public void ResultsSelectionChanged(object selectedItems)
{
_resultsSelectedItems.Clear();
foreach (item in selectedItems) {
_resultsSelectedItems.Add(item);
}
}
The _resultsSelectedItems is simply a list of items displayed in the DataGrid:
Private _resultsSelectedItems As New List(Of WorkOrderMatchViewModel)
C#:
private List<WorkOrderMatchViewModel> _resultsSelectedItems = new List<WorkOrderMatchViewModel>();
Hope this helps, kinda uses both of Bill's methods without having to reference MVVM-Light.
I've been looking for an answer to this question as well. The answers that I have found is either to
1) in the codebehind delegate the work to a method in the ViewModel passing the SelectedItems list from the datagrid. This collection will contain all of the items that are selected.
Or
2) use the MVVM toolkit light that allows you to use Event to Command and pass an object as a parameter directly to the ViewModel.
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
IList lst = this.myDataGrid.SelectedItems;
ViewModel.RowsSelected(lst);
}
In this case you will need to bind your SelectionChanged in your xaml to your selectionchanged in the code behind. Then in your code-behind you can save this list and use it for deleting selected rows, etc.
If there is a better way to do this, I'd love to know is well.
HTH
Bill
C# version SelectedItemsBehavior class. May be help someone.
public static class SelectedItemsBehavior
{
public static readonly DependencyProperty SelectedItemsChangedHandlerProperty =
DependencyProperty.RegisterAttached("SelectedItemsChangedHandler",
typeof(RelayCommand),
typeof(SelectedItemsBehavior),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnSelectedItemsChangedHandlerChanged)));
public static RelayCommand GetSelectedItemsChangedHandler(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return element.GetValue(SelectedItemsChangedHandlerProperty) as RelayCommand;
}
public static void SetSelectedItemsChangedHandler(DependencyObject element, RelayCommand value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(SelectedItemsChangedHandlerProperty, value);
}
public static void OnSelectedItemsChangedHandlerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = (DataGrid)d;
if (e.OldValue == null && e.NewValue != null)
{
dataGrid.SelectionChanged += new SelectionChangedEventHandler(ItemsControl_SelectionChanged);
}
if (e.OldValue != null && e.NewValue == null)
{
dataGrid.SelectionChanged -= new SelectionChangedEventHandler(ItemsControl_SelectionChanged);
}
}
public static void ItemsControl_SelectionChanged(Object sender, SelectionChangedEventArgs e)
{
DataGrid dataGrid = (DataGrid)sender;
RelayCommand itemsChangedHandler = GetSelectedItemsChangedHandler(dataGrid);
itemsChangedHandler.Execute(dataGrid.SelectedItems);
}
}
I managed to get around this using Relay Commands as Bill mentioned. It is a little dirty in parts, but I avoided putting any code in the behind file.
Firstly, in your XAML - Bind your command onto a button, or whatever triggers your RelayCommand.
<Button Content="Select"
cmd:ButtonBaseExtensions.Command="{Binding CommandSelect}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding ElementName=Results, Path=SelectedItems}" />
You'll notice the command parameter Binds to another UI element - the DataGrid or ListView you wish to get the selected items of. This syntax will work in Silverlight 3 as well as WPF, as it now supports element to element binding.
In your view model your Command will look something like this
Private _CommandSelect As RelayCommand(Of IEnumerable)
Public ReadOnly Property CommandSelect() As RelayCommand(Of IEnumerable)
Get
If _CommandSelect Is Nothing Then
_CommandSelect = New RelayCommand(Of IEnumerable)(AddressOf CommandSelectExecuted, AddressOf CommandSelectCanExecute)
End If
Return _CommandSelect
End Get
End Property
Private Function CommandSelectExecuted(ByVal parameter As IEnumerable) As Boolean
For Each Item As IElectoralAreaNode In parameter
Next
Return True
End Function
Private Function CommandSelectCanExecute() As Boolean
Return True
End Function
The Selected Items will be returned as a SelectedItemCollection, but you probably don't want this dependancy in your View Model. So typing it as IEnumerable and doing a little casting is your only option, hense the 'dirtyness'. But it keeps your code behind clean and MVVM pattern in tact!