Има ли начин да се попречи на потребител да избере друг ред, когато DataGrid е в режим на редактиране?

Искам, че ако клетка/ред премине в режим на редактиране, тогава, ако потребителят се опита да избере различен ред, той трябва да се опита да ангажира този ред, ако редът не е ангажиран успешно, той трябва да отхвърли заявката за избор и ред за редактиране трябва да остане избран и в режим на редактиране.

Имате ли опит с добър помощник за него? някакво добро решение?

ЗАБЕЛЕЖКА: Боря се с този проблем от дълго време и вече имам известен опит, така че, моля, публикувайте само работещи примери, а не произволни мисли.


person Shimmy Weitzhandler    schedule 21.11.2010    source източник


Отговори (1)


Кодът по-долу включва разширение от тук (код нормализиран, за да пасне на екрана на StackOverflow с dh, съжалявам).

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

Употреба:

<DataGrid Name="dg"
src:DataGridSelectionChangingBehavior.EnableSelectionChanging="True" 
src:DataGridSelectionChangingBehavior.SelectionChanging="dg_SelectionChanging">

Код отзад (псевдо):

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

Забележка: Visual Studio може да не покаже събитието в intellisense и може дори да генерира грешка по време на проектиране, но трябва да се компилира и да работи перфектно.

FYI: форматиран код, модифициран, за да пасне на SO екран.

person Shimmy Weitzhandler    schedule 21.11.2010