I want, that if a cell/row goes to edit mode, then, if the user attempts to select a different row, it should try to commit th开发者_开发技巧at row, if the row is not committed sucessfully, it should decline the selection request and the editing row should remain selected and in edit mode.
Do you have experience with a good helper for it? any good workaround?
NOTE: I've been struggling with this issue for a long time and already got some experience, so please post only working examples, not random thoughts.
The code bellow is includes extension from here (code normalized to fit StackOverflow screen witdh, sorry).
Imports System.ComponentModel
Imports System.Windows.Threading
Namespace Components
Public NotInheritable Class DataGridSelectionChangingBehavior
Public Shared Function GetEnableSelectionChanging(
ByVal element As DataGrid) As Boolean
If element Is Nothing Then Throw New ArgumentNullException("element")
Return element.GetValue(EnableSelectionChangingProperty)
End Function
Public Shared Sub SetEnableSelectionChanging(
ByVal element As DataGrid, ByVal value As Boolean)
If element Is Nothing Then Throw New ArgumentNullException("element")
element.SetValue(EnableSelectionChangingProperty, value)
End Sub
Public Shared ReadOnly EnableSelectionChangingProperty As _
DependencyProperty =
DependencyProperty.RegisterAttached("EnableSelectionChanging",
GetType(Boolean),
GetType(DataGridSelectionChangingBehavior),
New FrameworkPropertyMetadata(False,
New PropertyChangedCallback(
AddressOf EnableSelectionChanging_PropertyChanged)))
Public Shared Sub AddSelectionChangingHandler(
ByVal element As DataGrid, handler As SelectionChangingEventHandler)
If element IsNot Nothing Then _
element.AddHandler(
DataGridSelectionChangingBehavior.SelectionChangingEvent, handler)
End Sub
Public Shared Sub RemoveSelectionChangingHandler(
ByVal element As DataGrid, handler As SelectionChangingEventHandler)
If element IsNot Nothing Then _
element.RemoveHandler(
DataGridSelectionChangingBehavior.SelectionChangingEvent, handler)
End Sub
Public Shared ReadOnly SelectionChangingEvent As RoutedEvent =
EventManager.RegisterRoutedEvent("SelectionChanging",
RoutingStrategy.Bubble,
GetType(SelectionChangingEventHandler),
GetType(DataGridSelectionChangingBehavior))
Private Shared Sub EnableSelectionChanging_PropertyChanged(
ByVal sender As Object, ByVal e As DependencyPropertyChangedEventArgs)
Dim dataGrid = DirectCast(sender, DataGrid)
If CBool(e.NewValue) Then
AddHandler dataGrid.PreparingCellForEdit,
AddressOf DataGrid_PreparingCellForEdit
AddHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
Else
RemoveHandler dataGrid.PreparingCellForEdit,
AddressOf DataGrid_PreparingCellForEdit
RemoveHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
RecentColumn.Remove(dataGrid)
End If
End Sub
Private Shared Sub DataGrid_SelectionChanged(
ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
If e.RemovedItems.Count = 0 Then Exit Sub
Dim dataGrid = DirectCast(sender, DataGrid)
Dim removed = e.RemovedItems(0)
Dim row = dataGrid.GetContainerFromItem(Of DataGridRow)(removed)
Dim scea As New SelectionChangingEventArgs(row,
DataGridSelectionChangingBehavior.SelectionChangingEvent, dataGrid)
dataGrid.RaiseEvent(scea)
If scea.Cancel Then
RemoveHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
Dim operation = dataGrid.Dispatcher.BeginInvoke(
Sub()
dataGrid.SelectedItem = removed
Dim column As DataGridColumn = Nothing
If RecentColumn.TryGetValue(dataGrid, column) Then
Dim cellsPanel =
row.GetVisualDescendant(Of DataGridCellsPanel)().
Children.Cast(Of DataGridCell)()
Dim recentCell =
If(cellsPanel.SingleOrDefault(
Function(cell) cell.Column Is column),
cellsPanel.FirstOrDefault)
If recentCell IsNot Nothing Then
recentCell.IsEditing = True
Keyboard.Focus(recentCell)
End If
End If
End Sub,
DispatcherPriority.ContextIdle)
AddHandler operation.Completed,
Sub(s, ea) AddHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
End If
End Sub
Private Shared m_RecentColumn As Dictionary(Of DataGrid, DataGridColumn)
Public Shared ReadOnly Property RecentColumn() As Dictionary(Of DataGrid,
DataGridColumn)
Get
If m_RecentColumn Is Nothing Then m_RecentColumn =
New Dictionary(Of DataGrid, DataGridColumn)()
Return m_RecentColumn
End Get
End Property
Private Shared Sub DataGrid_PreparingCellForEdit(
ByVal sender As Object, e As DataGridPreparingCellForEditEventArgs)
Dim dataGrid = DirectCast(sender, DataGrid)
RecentColumn(dataGrid) = e.Column
End Sub
End Class
Public Delegate Sub SelectionChangingEventHandler(
ByVal sender As Object, ByVal e As SelectionChangingEventArgs)
Public Class SelectionChangingEventArgs : Inherits RoutedEventArgs
Public Sub New(ByVal row As DataGridRow, routedEvent As RoutedEvent)
MyBase.New(routedEvent)
m_CurrentRow = row
End Sub
Public Sub New(ByVal row As DataGridRow,
ByVal routedEvent As RoutedEvent,
ByVal source As Object)
MyBase.New(routedEvent, source)
m_CurrentRow = row
End Sub
Private m_CurrentRow As DataGridRow
Public ReadOnly Property CurrentRow() As DataGridRow
Get
Return m_CurrentRow
End Get
End Property
Public ReadOnly Property Item() As Object
Get
If CurrentRow IsNot Nothing Then Return CurrentRow.Item
Return Nothing
End Get
End Property
Public Property Cancel As Boolean
End Class
End Namespace
Usage:
<DataGrid Name="dg"
src:DataGridSelectionChangingBehavior.EnableSelectionChanging="True"
src:DataGridSelectionChangingBehavior.SelectionChanging="dg_SelectionChanging">
Code behind (pseudu):
Private Sub dg_SelectionChanging(ByVal sender As Object,
ByVal e As SelectionChangingEventArgs)
If e.CurrentRow.IsEditing Then
Dim item = TryCast(e.CurrentRow.Item, MyEntityType)
If item IsNot Nothing AndAlso item.IsValid Then
Dim dataGrid = DirectCast(sender, DataGrid)
Dim committed = dataGrid.CommitEdit(DataGridEditingUnit.Row, True)
If committed Then Exit Sub
End If
e.Cancel = True
End If
End Sub
Note: visual studio might not show the event in the intellisense and might even generate a design-time error, but it should compile and work perfect.
FYI: code formatted modified to fit SO screen.
精彩评论