Reputation: 403
I am using a technique similar to http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/df77a277-91d4-41f1-a42a-0fa02a443ff4/ I have a DaataGridTemplateColumn built, in code, and I am attempting to address the "select row, THEN click" checkbox issue with data grids in WPF.
In the general sense, this works, however if I want to fire an event when the checkbox is clicked (I.e. CellEditEnding or RowEditEnding), it seems like I cannot (according to http://social.msdn.microsoft.com/Forums/en/wpf/thread/bf080945-0092-43f5-b0eb-42b2edf53dc7)
There MUST be a way to solve this. I thought about using some kind of routed event on my template column in attempt to fire a data grid event when the checkbox is clicked, but this could get ugly. Ultimately I need to be able to have a 1 click checkbox, and be able to fire data grid events.
Please help!
Edit: If I can just get RowEditEnding to fire (somehow: via. bubbling or whatever) then this wouldn't be an issue... I simply cannot get it to fire..
Anybody???
Thank you
Upvotes: 1
Views: 1952
Reputation: 10604
I came across your question and was running into the same thing. Here's my situation and solution and I think it should work for you (and others) as well. I have a datagrid that allows the entry of new items, so the possible values go from 0 to infinity. Users should be able to click the checkbox and have it check or uncheck with one button click. In addition, there are other fields that can be updated as well. I had to create a custom datagrid control to automatically focus and begin editing of the checkbox:
public class CheckboxDataGrid : DataGrid {
public CheckboxDataGrid() {
EventManager.RegisterClassHandler(typeof(DataGridCell),
DataGridCell.PreviewMouseLeftButtonDownEvent,
new RoutedEventHandler(this.OnPreviewMouseLeftButtonDown));
}
private void OnPreviewMouseLeftButtonDown(object sender, RoutedEventArgs e) {
DataGridCell cell = sender as DataGridCell;
if (cell != null && !cell.IsEditing && !cell.IsReadOnly) {
var parentRow = cell.Parent as DataGridRow;
if (parentRow != null) {
SelectedIndex = parentRow.GetIndex();
}
CurrentCell = new DataGridCellInfo(cell);
DependencyObject obj = FindVisualChild<CheckBox>(cell);
if (obj != null) {
BeginEdit(e);
System.Windows.Controls.CheckBox cb = (System.Windows.Controls.CheckBox)obj;
cb.Focus();
}
}
}
public static TChild FindVisualChild<TChild>(DependencyObject obj) where TChild : DependencyObject {
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) {
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is TChild) {
return (TChild)child;
} else {
TChild childOfChild = FindVisualChild<TChild>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
}
I also needed a converter to ignore the new item binding message because of the way that WPF handles new items in a datagrid:
public class IgnoreNewItemPlaceHolderConverter : IValueConverter {
private const string NewItemPlaceholderName = "{NewItemPlaceholder}";
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value != null && value.ToString() == NewItemPlaceholderName)
return DependencyProperty.UnsetValue;
return value;
}
}
Here is the relavent XAML for the datagrid:
<chkDatagrid:CheckboxDataGrid AutoGenerateColumns="False"
ItemsSource="{Binding ElementName=dgPurchaseOrders, Path=SelectedItem.Releases, NotifyOnSourceUpdated=True}" AlternatingRowBackground="#1E000000"
DockPanel.Dock="Bottom" HorizontalAlignment="Left" SelectedItem="{Binding SelectedRelease, Mode=TwoWay, Converter={StaticResource ignoreNewItemPlaceHolderConverter}}"
MinHeight="100">
<b:Interaction.Triggers>
<b:EventTrigger EventName="RowEditEnding">
<b:InvokeCommandAction Command="{Binding ReleaseRowEditEndingCommand}" CommandParameter="{Binding SelectedRelease}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Is Paid" Binding="{Binding IsPaid, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Amount" Binding="{Binding Amount, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Description" Binding="{Binding Description, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Invoice Number" Binding="{Binding InvoiceNumber, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Invoice Receive Date" Binding="{Binding InvoiceRecvDate, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</chkDatagrid:CheckboxDataGrid>
You'll note a few things here. I'm subscribing to the RowEditEnding event for the editing and committing of the columns and the application of the converter to the new row.
Finally, here's the code that updates the attached entity:
private void OnReleaseRowEditEnding(object arg) {
if (arg != null) {
EMService.EMEntities etx = GetEMEntities();
EMService.Release release = (EMService.Release)arg;
if (release.ReleaseID == 0) {
release.EncumbranceID = SelectedPurchaseOrder.EncumbranceID;
release.CreatedOn = DateTime.Now;
release.CreatedBy = CurrentUser.Username;
release.ModifiedOn = DateTime.Now;
release.ModifiedBy = CurrentUser.Username;
etx.AddObject("Releases", release);
} else {
EMService.Release existingRelease = etx.Releases.Where(e => e.ReleaseID == release.ReleaseID).First();
existingRelease.Amount = release.Amount;
existingRelease.Description = release.Description;
existingRelease.InvoiceNumber = release.InvoiceNumber;
existingRelease.InvoiceRecvDate = release.InvoiceRecvDate;
existingRelease.IsPaid = release.IsPaid;
release.ModifiedOn = DateTime.Now;
release.ModifiedBy = CurrentUser.Username;
etx.UpdateObject(existingRelease);
}
etx.SaveChanges();
}
}
It's a lot to digest, but it ended up working in the long run. Who knew the WPF datagrid would be so putzy.
Upvotes: 1
Reputation: 403
The answer here was to not handle any of the checkboxes (or attempt to) in RowEditEnding. Instead, make use of PropertyChange event listening on the checkbox's which you are bound, and act accordingly (i.e. save changes when the checkbox's binding is set)
Upvotes: 0