Есть ли способ предотвратить выбор пользователем другой строки, когда DataGrid находится в режиме редактирования?

Я хочу, чтобы если ячейка/строка перешла в режим редактирования, то, если пользователь пытается выбрать другую строку, он должен попытаться зафиксировать эту строку, если строка не была успешно зафиксирована, он должен отклонить запрос на выбор и строка редактирования должна оставаться выделенной и находиться в режиме редактирования.

У вас есть опыт работы с хорошим помощником для этого? любой хороший обходной путь?

ПРИМЕЧАНИЕ. Я долго боролся с этой проблемой и уже получил некоторый опыт, поэтому, пожалуйста, публикуйте только рабочие примеры, а не случайные мысли.


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


Ответы (1)


Приведенный ниже код включает расширение из здесь (код нормализован, чтобы соответствовать размеру экрана StackOverflow, извините).

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