Search code examples
wpfwpfdatagrid

select a row on datagrid that has grouped data


I've searched online and have tried myself and am unable to find a way to accurately select a record programming by providing a search value on a datagrid that has grouped data. The code below that I have works on ungrouped data, but when the data is grouped, the index seems to no longer be valid and the grouping seems to have it's on index on the data.

Here is a screen shot of the form. The data is grouped. The user types in a sales order number and clicks the find button. I'd like to record that is found for that row to be highlighted.

enter image description here

This is the data ungrouped when a record is found, you can see the record is highlighted:

enter image description here

Below is my code. The OnSelectionButtonClick event is fired when the Find button is clicked. Data is grouped/ungrouped in the cmdAddRemoveGroup_Click procedure. Data is bound to the grid in the RebindData procedure. A sales order is passed into the SelectGridRow procedure to select a row if found.

Any help is appreciated! Thanks, Jimmy

'Module level scope
Private da As wpfProductionDashboard.flexDataSetTableAdapters.vwProductionScheduleWPFMainTableAdapter = New wpfProductionDashboard.flexDataSetTableAdapters.vwProductionScheduleWPFMainTableAdapter()

Private Sub OnSelectionButtonClick(sender As Object, e As RoutedEventArgs)
        Try
            Dim so As Integer = Convert.ToInt32(Me.txtFind.Text)

            Dim index As Integer = FlexDataSet.vwProductionScheduleWPFMain.Rows.IndexOf(FlexDataSet.vwProductionScheduleWPFMain.Rows.Find(so))

            'Dim index2 As Integer = 1

            'For Each c In FlexDataSet.vwProductionScheduleWPFMain.Rows
            '    If c(0) = so Then
            '        SelectGridRow(dataGrid1, index2)
            '        Exit Sub
            '    End If
            '    Console.WriteLine(c(0))
            '    index2 += 1
            'Next
            If index > 0 Then
                Console.WriteLine(index.ToString())
                SelectGridRow(dataGrid1, index)
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Find button click error", MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
    End Sub

    Private Sub SelectGridRow(ByVal DataGrid As DataGrid, ByVal rowIndex As Integer)
        If Not DataGrid.SelectionUnit.Equals(DataGridSelectionUnit.FullRow) Then
            Throw New ArgumentException("The SelectionUnit of the DataGrid must be set to FullRow.")
        End If

        If rowIndex < 0 OrElse rowIndex > (DataGrid.Items.Count - 1) Then
            Throw New ArgumentException(String.Format("{0} is an invalid row index.", rowIndex))
        End If

        DataGrid.SelectedItems.Clear()

        Dim item As Object = DataGrid.Items(rowIndex)

        DataGrid.SelectedItem = item

        Dim row As DataGridRow = TryCast(DataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex), DataGridRow)
        If row Is Nothing Then
            ' bring the data item into view
            DataGrid.ScrollIntoView(item)
            row = TryCast(DataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex), DataGridRow)
        End If
    End Sub

    Private Sub cmdAddRemoveGroup_Click(sender As Object, e As RoutedEventArgs) Handles cmdAddRemoveGroup.Click
        Try
            If cmdAddRemoveGroup.IsChecked = False Then
                FlexDataSet.vwProductionScheduleWPFMain.DefaultView.RowFilter = "SalesOrderStatus <> 'Hold' and SalesOrderStatus <> 'Approval Done'"
                vw.GroupDescriptions.Add(New PropertyGroupDescription("SalesOrderStatusGroup", New OpenStatusConverter))
            Else
                vw.GroupDescriptions.Clear()
            End If
            SortDataGrid()
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Grouping Event", MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
    End Sub

    Private Sub RebindData()
        Try
            Dim taStatuses As wpfProductionDashboard.flexDataSetTableAdapters.spGetProductionDashboardStatusesTableAdapter = New wpfProductionDashboard.flexDataSetTableAdapters.spGetProductionDashboardStatusesTableAdapter()

            da.Fill(FlexDataSet.vwProductionScheduleWPFMain)
            taStatuses.Fill(FlexDataSet.spGetProductionDashboardStatuses)

            vw.View.MoveCurrentToFirst()
            cvOrderStatuses.View.MoveCurrentToFirst()

            lstOrderStatus.ItemsSource = cvOrderStatuses.View
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Rebind Data Event", MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
    End Sub

Thanks Konstantin. I was afraid you were going to say that. I haven't figured out how to implement an ObservableCollection as this app doesn't use MVVM. If you don't mind, and I understand if you do and don't want to, would you mind try to help me see if I can bind to an ObservableCollection via my current implementation? Below is my xaml and explanation.

<Window.Resources>
<local:flex2kSQLDataSet x:Key="Flex2kSQLDataSet" />
<CollectionViewSource x:Key="cvOrderStatuses" Source="{Binding spGetProductionDashboardStatuses, Source={StaticResource Flex2kSQLDataSet}}" />
<CollectionViewSource x:Key="VwProductionScheduleWPFMainViewSource" Source="{Binding vwProductionScheduleWPFMain, Source={StaticResource Flex2kSQLDataSet}}" />

<Grid DataContext="{StaticResource VwProductionScheduleWPFMainViewSource}">

VwProductionScheduleWPFMainViewSource uses a SQL Server view and a table adapter. Is it possible to use this view and create an ObservableCollection and bind it to the datagrid? If so, if you could help with code suggestion or point me in the right direction, I'd greatly appreciate it!

UPDATE #3: Konstantin, I think I'm close but need a little more help.

Module level: Private Item As Object

Private Property Items() As ObservableCollection(Of vwProductionScheduleWPFMain)
    Get
        Return m_Items
    End Get
    Set(value As ObservableCollection(Of vwProductionScheduleWPFMain))
        m_Items = value
    End Set
End Property

Private m_Items As ObservableCollection(Of vwProductionScheduleWPFMain)

Form load: Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) Handles MyBase.Loaded Try

        dataGrid1.DataContext = Me

Bind data procedure: Private Sub BindData() Try Dim db As TFCDataContext

        db = New TFCDataContext

        Items = New ObservableCollection(Of vwProductionScheduleWPFMain)(db.vwProductionScheduleWPFMains)
        dataGrid1.ItemsSource = Items

Find procedure: Private Sub OnSelectionButtonClick(sender As Object, e As RoutedEventArgs) Try Dim so As Integer = Convert.ToInt32(Me.txtFind.Text)

        Item = Items.Where(Function(x) x.SalesOrder = so)
        dataGrid1.SelectedItem = Item

    Catch ex As Exception
        MessageBox.Show(ex.Message, "Find button click error", MessageBoxButton.OK, MessageBoxImage.Error)
    End Try
End Sub

XAML for grid:

I'm able to see that my Items observable collection is getting the correct data and that the find method successfully find the record. However, the found record does not select the record in the grid. What am I missing to make it find the record in the grid?

Also, I noticed that if I remove setting the DataContext on the load event, the data is still loaded. Is setting the DataContext to (me/this) needed or am I doing it incorrectly? Thanks again for your help!

UPDATE #4: I've successfully found the item searched for in the observable Items collection and assigned it to an object Item, but when I set the SelectedItem property of the datagrid, the row is not selected, which is what I really need. If you could help or provide sample code, it would be much appreciated. Below is my code showing the item has been found (but the row isn't selected).

enter image description here

XAML for datagrid with ItemSource and SelectedItem set:

<DataGrid x:Name="dataGrid1"
AutoGenerateColumns="False"
CanUserReorderColumns="True"
CanUserAddRows="False"
IsReadOnly="False"
CanUserResizeColumns="True"
EnableRowVirtualization="True"
ItemsSource="{Binding Items}"
SelectedItem="{Binding Item}"
CanUserSortColumns="True"
RowHeight="25"
Margin="10,15,10,10"
Grid.Row="3"
Sorting="DataGrid_Standard_Sorting">

Solution

  • An ObservableCollection is the best implementation. Example code below.

    Public Property MyCollection() As ObservableCollection(Of YourClass)
        Get
            Return m_MyCollection
        End Get
        Set(value As ObservableCollection(Of YourClass))
            m_MyCollection = value
        End Set
    End Property
    Private m_MyCollection As New ObservableCollection(Of YourClass)
    
    Dim findItem As YourClass
    
    findItem = MyCollection.First(Function(x) x.SalesOrder = so)
    
    dataGrid1.SelectedItem = findItem
    
    dataGrid1.ScrollIntoView(findItem)