This is a migrated thread and some comments may be shown as answers.

Gridview - Binding to dictionary(of string,object)

4 Answers 609 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Joshua
Top achievements
Rank 1
Joshua asked on 13 May 2011, 03:32 PM
I'm trying to bind a RadGridView to a list of x, where x has a property called Attributes which is a Dictionary(of String, Object).  The binding works correctly for display and the items in the grid are sortable.  However, the filtering is not working.  When a column is populated with numeric values, the unique values filter box is displayed but nothing happens when you click on the checkbox beside a unique value.  When a column is populated with string values, the unique values filter is displayed but the filter only shows the first matching record after checking a unique value, even though there are more records that contain that value. 

If I changed my code to bind to a Dictionary(of String, String), the filtering works perfectly.  Is there something I can do to get filtering to work with a Dictionary(of String, Object)?

Here is my sample code:


    
Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
  
        Dim lyrData As List(Of LayerData) = GetLayerData()
        Dim ga As GraphicAttributeColumn
  
        For i As Integer = 0 To lyrData(0).Attributes.Count - 1
            Dim key As String = lyrData(0).Attributes.Keys.ElementAt(i)
            ga = New GraphicAttributeColumn
            ga.AttributeName = key
  
            Me.RadGridView1.Columns.Add(ga)
        Next
  
        RadGridView1.ItemsSource = lyrData
  
    End Sub
  
    Private Function GetLayerData() As List(Of LayerData)
        Dim data As New List(Of LayerData)
  
        For j As Integer = 1 To 10
            Dim attributes As New Dictionary(Of String, Object)
  
            attributes.Add("ID", j)
            attributes.Add("Mod", (j Mod 2).ToString)
            attributes.Add("Description", String.Format("Description: {0}", j Mod 2))
            attributes.Add("Quantity", New Random().[Next](500))
            attributes.Add("Address", String.Format("Address: {0}", j))
  
            data.Add(New LayerData(True, attributes))
        Next
  
        Return data
  
    End Function
  
Public Class GraphicAttributeColumn
    Inherits Telerik.Windows.Controls.GridViewDataColumn
  
    Private _attributeName As String
  
    Public Sub New()
    End Sub
  
    Public Property AttributeName() As String
        Get
            Return _attributeName
        End Get
        Set(ByVal value As String)
  
            Dim b As System.Windows.Data.Binding = New System.Windows.Data.Binding("Attributes[" & value & "]")
            Me.DataMemberBinding = b
            _attributeName = value
  
        End Set
    End Property
  
    Public Overrides Function CanSort() As Boolean
        Return True
    End Function
    Public Overrides Function CanFilter() As Boolean
        Return True
    End Function
End Class
  
  
Public Class LayerData
    Private m_IsSelected As Boolean
    Private m_Attributes As Dictionary(Of String, Object)
  
    Public Property IsSelected() As Boolean
        Get
            Return m_IsSelected
        End Get
        Set(ByVal value As Boolean)
            m_IsSelected = value
        End Set
    End Property
  
  
    Public Property Attributes() As Dictionary(Of String, Object)
        Get
            Return m_Attributes
        End Get
        Set(ByVal value As Dictionary(Of String, Object))
            m_Attributes = value
        End Set
    End Property
  
  
    Public Sub New()
    End Sub
  
    Public Sub New(ByVal isSelectecd As Boolean, ByVal attributes As Dictionary(Of String, Object))
        Me.IsSelected = isSelectecd
        Me.Attributes = attributes
    End Sub
End Class

4 Answers, 1 is accepted

Sort by
0
Ivan Ivanov
Telerik team
answered on 18 May 2011, 04:11 PM
Hello Joshua,

Would you please send us your xaml to demonstrate the way you are binding to this dictionary property, in order to help me provide an adequate solution to your issue. Excuse me for the inconvenience.

Best wishes,
Ivan Ivanov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Scott
Top achievements
Rank 1
answered on 19 May 2011, 10:24 PM
I'm having the same type of issue.
0
Joshua
Top achievements
Rank 1
answered on 20 May 2011, 03:36 AM
I have found a solution and I can now filter my RadGridView that is bound to a list of items that have a Dictionary(of String, Object) property.  I started with the sample found at a blog post "Custom Filtering with RadGridView" found here: http://blogs.telerik.com/blogs/posts/09-11-20/custom_filtering_with_radgridview_for_silverlight.aspx, I modified it to mirror the functionality of the out-of-the-box filtering.

I created a user control that inherits from the Telerik IFilteringControl interface.  I then set the FilteringControl property of the GridviewDataColumn so my filter control displays when a column filter icon is clicked in the column header.  My filter user control creates an instance of a CustomFilteringViewModel class that I created to handle retrieving unique values and creating filter descriptors when unique values are checked in the UI.  The key is, when the filter descriptor is created, you must set the type of the descriptor to String or Int32 (depending on the data type of the values in that column), as opposed to using the data type of the column itself (which will always be Object). 

Just a note, I'm using the ESRI API for Silverlight and I am binding to a list of Graphics that are returned from a query. 

Ivan - there is not really any XAML other than the Gridview itself with AutoGenerateColumns set to False.  All columns are created programatically and the binding is set in code, which I included in my first post. 

It looks like I can't attach a zip file with my sample project.  I will reply again and try to post all of the code for anyone else that is trying to do this.
0
Joshua
Top achievements
Rank 1
answered on 20 May 2011, 03:46 AM

<!--

 

 

MainPage.xaml-->
<

UserControl x:Class="Telerik_Test.MainPage"

    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation">
  
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Format:" />
                <telerik:RadComboBox Margin="0,2" x:Name="ComboBox1" SelectedIndex="0">
                    <telerik:RadComboBoxItem Content="Excel"  />
                    <telerik:RadComboBoxItem Content="ExcelML" />
                    <telerik:RadComboBoxItem Content="Word" />
                    <telerik:RadComboBoxItem Content="Csv"  />
                </telerik:RadComboBox >
  
                <Button Content="Export" Click="Button_Click" Width="100" Height="40" />                
            </StackPanel>
              
            <telerik:RadGridView HorizontalAlignment="Left" Margin="10,10,0,0" Name="RadGridView1" VerticalAlignment="Top" ShowGroupPanel="False" 
                                 AutoGenerateColumns="False" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="600" />
            <telerik:RadDataPager Height="28" Name="RadDataPager1" PageSize="50" DisplayMode="FirstLastPreviousNextNumeric, Text" Source="{Binding Items, ElementName=RadGridView1}" />
        </StackPanel>
  
    </Grid>
</UserControl>

MainPage.xaml.vb
Imports Telerik.Windows.Controls
Imports System.Windows.Data
Imports System.IO
Imports Telerik.Windows.Data
  
  
Partial Public Class MainPage
    Inherits UserControl
  
    Private _list As List(Of LayerData)
    Private _filters As List(Of GridviewFilter)
  
    Public Sub New()
        InitializeComponent()
    End Sub
  
    Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
  
        Dim ga As GraphicAttributeColumn
        Dim cgf As CustomGridFilter
  
        _list = GetLayerData()
  
        For i As Integer = 0 To _list(0).Attributes.Count - 1
            Dim key As String = _list(0).Attributes.Keys.ElementAt(i)
            ga = New GraphicAttributeColumn
            ga.AttributeName = key
            cgf = New CustomGridFilter(key)
            ga.FilteringControl = cgf
  
            Me.RadGridView1.Columns.Add(ga)
        Next
  
        RadGridView1.ItemsSource = _list
  
        _filters = New List(Of GridviewFilter)
    End Sub
  
    Private Function GetLayerData() As List(Of LayerData)
        Dim data As New List(Of LayerData)
  
        For j As Integer = 1 To 1000
            Dim attributes As New Dictionary(Of String, Object)
  
            attributes.Add("ID", j)
            attributes.Add("Mod", (j Mod 2).ToString)
            attributes.Add("Description", String.Format("Description: {0}", j Mod 2))
            attributes.Add("Quantity", New Random().[Next](50))
            attributes.Add("Address", String.Format("Address: {0}", CInt(j / 5)))
  
            data.Add(New LayerData(True, attributes))
        Next
  
        Return data
  
    End Function
  
  
End Class

GraphicAttributeColumn.vb
Public Class GraphicAttributeColumn
    Inherits Telerik.Windows.Controls.GridViewDataColumn
  
    Private _attributeName As String
  
    Public Sub New()
    End Sub
  
    Public Property AttributeName() As String
        Get
            Return _attributeName
        End Get
        Set(ByVal value As String)
  
            Dim b As System.Windows.Data.Binding = New System.Windows.Data.Binding("Attributes[" & value & "]")
            Me.DataMemberBinding = b
            _attributeName = value
  
        End Set
    End Property
  
    Public Overrides Function CanSort() As Boolean
        Return True
    End Function
    Public Overrides Function CanFilter() As Boolean
        Return True
    End Function
End Class
  
  
Public Class LayerData
    Private m_IsSelected As Boolean
    Private m_Attributes As Dictionary(Of String, Object)
  
    Public Property IsSelected() As Boolean
        Get
            Return m_IsSelected
        End Get
        Set(ByVal value As Boolean)
            m_IsSelected = value
        End Set
    End Property
  
  
    Public Property Attributes() As Dictionary(Of String, Object)
        Get
            Return m_Attributes
        End Get
        Set(ByVal value As Dictionary(Of String, Object))
            m_Attributes = value
        End Set
    End Property
  
  
    Public Sub New()
    End Sub
  
    Public Sub New(ByVal isSelectecd As Boolean, ByVal attributes As Dictionary(Of String, Object))
        Me.IsSelected = isSelectecd
        Me.Attributes = attributes
    End Sub
End Class

CustomGridFilter.xaml
<UserControl x:Class="Telerik_Test.CustomGridFilter"
    mc:Ignorable="d">
  
    <Grid x:Name="LayoutRoot" MinWidth="100">
        <Border Background="#DAE4F1" CornerRadius="8">
            <Border.BorderBrush>
                <SolidColorBrush Color="DarkGray"/>
            </Border.BorderBrush>
              
                <StackPanel Margin="10,10,10,5">
                <CheckBox x:Name="chkSelectAll" Content="Select All" />
                <ListBox x:Name="lstUniqueValues" ItemsSource="{Binding DistinctValues}"  ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="350">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked"  Content="{Binding Display}" IsChecked="{Binding IsSelected, Mode=TwoWay}" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
            </ListBox>
            <Button x:Name="btnClear" Content="Clear" HorizontalAlignment="Stretch" Height="20" VerticalAlignment="Bottom" />
            </StackPanel>
        </Border>
    </Grid>
</UserControl>

CustomGridFilter.xaml.vb
Imports Telerik.Windows.Controls.GridView
Imports System.Windows.Data
Imports Telerik.Windows.Data
  
  
Partial Public Class CustomGridFilter
    Inherits UserControl
    Implements IFilteringControl
  
    Public Shared ReadOnly IsActiveProperty As DependencyProperty = DependencyProperty.Register("IsActive", GetType(Boolean), GetType(CustomGridFilter), New PropertyMetadata(False))
    Private _filterColumn As Telerik.Windows.Controls.GridViewBoundColumnBase
    Private _columnName As String
  
    Public Sub New(ByVal columnName As String)
        InitializeComponent()
        DataContext = Nothing
  
        _columnName = columnName
    End Sub
  
    Public Property IsActive As Boolean Implements Telerik.Windows.Controls.GridView.IFilteringControl.IsActive
        Get
            Return GetValue(IsActiveProperty)
        End Get
        Set(ByVal value As Boolean)
            SetValue(IsActiveProperty, value)
        End Set
    End Property
  
    Public Sub Prepare(ByVal column As Telerik.Windows.Controls.GridViewBoundColumnBase) Implements Telerik.Windows.Controls.GridView.IFilteringControl.Prepare
  
        If Me.DataContext Is Nothing Then
            ' Create the view mode the very first time Prepare is called.
            Dim viewModel = New CustomFilteringViewModel(column, column.DataControl.FilterDescriptors, AddressOf column.DataControl.GetDistinctValues)
  
            ' Let my view model tell me whether the filtering is active.
            Me.SetBinding(IsActiveProperty, New Binding("IsActive") With { _
              .Source = viewModel, _
              .Mode = BindingMode.OneWay})
  
            Me.DataContext = viewModel
        End If
  
        DirectCast(Me.DataContext, CustomFilteringViewModel).Prepare()
  
    End Sub
  
    Private Sub CheckBox_Checked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        CType(Me.DataContext, CustomFilteringViewModel).ChangeFilter(CType(sender, CheckBox).Content.ToString)
    End Sub
  
    Private Sub CheckBox_Unchecked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        CType(Me.DataContext, CustomFilteringViewModel).ChangeFilter(CType(sender, CheckBox).Content.ToString)
    End Sub
  
    Private Sub btnClear_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnClear.Click
        For Each lstItem As UniqueValues In lstUniqueValues.Items
            If TypeOf lstItem Is UniqueValues Then
                lstItem.IsSelected = False
            End If
        Next
  
        chkSelectAll.IsChecked = False
  
    End Sub
  
    Private Sub chkSelectAll_Checked(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles chkSelectAll.Checked
        For Each lstItem As UniqueValues In lstUniqueValues.Items
            If TypeOf lstItem Is UniqueValues Then
                If lstItem.IsSelected = False Then lstItem.IsSelected = True
            End If
        Next
  
    End Sub
  
    Private Sub chkSelectAll_Unchecked(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles chkSelectAll.Unchecked
        For Each lstItem As UniqueValues In lstUniqueValues.Items
            If TypeOf lstItem Is UniqueValues Then
                If lstItem.IsSelected = True Then lstItem.IsSelected = False
            End If
        Next
    End Sub
End Class

CustomFilteringViewModel.vb
Imports Telerik.Windows.Data
Imports System.Collections.ObjectModel
Imports System.ComponentModel
  
Public Class CustomFilteringViewModel
  
    Private ReadOnly dataFieldDescriptor As IDataFieldDescriptor
    Private ReadOnly dataMemberName As String
    Private ReadOnly targetFilters As FilterDescriptorCollection
    Private ReadOnly getDistinctValuesCallback As Func(Of IDataFieldDescriptor, Boolean, IEnumerable(Of Object))
    Private ReadOnly convertedAndFormattedValueFunc As Func(Of Object, Object)
    Private ReadOnly _distinctValues As New ObservableCollection(Of UniqueValues)
  
    Private _compositeFilter As CompositeFilterDescriptor
    Private _filterType As Type = GetType(System.String)
    Private _isFilterTypeSet As Boolean = False
    Private _isSelectAll As Boolean = False
  
  
    Public Sub New(ByVal dataFieldDescriptor As IDataFieldDescriptor, ByVal targetFilters As FilterDescriptorCollection, _
                   ByVal getDistinctValuesCallback As Func(Of IDataFieldDescriptor, Boolean, IEnumerable(Of Object)))
  
        Me.dataFieldDescriptor = dataFieldDescriptor
        Me.dataMemberName = Me.dataFieldDescriptor.GetDataMemberName()
        Me.targetFilters = targetFilters
        Me.getDistinctValuesCallback = getDistinctValuesCallback
  
        Me.CreateFilterDescriptors()
  
        Try
            Me.convertedAndFormattedValueFunc = dataFieldDescriptor.CreateConvertedAndFormattedValueFunc()
  
        Catch generatedExceptionName As ArgumentException
        End Try
    End Sub
  
    Public Sub Prepare()
        If _distinctValues IsNot Nothing AndAlso _distinctValues.Count = 0 Then PopulateUniqueValues()
    End Sub
  
    Public Sub Clear()
  
    End Sub
  
    Private Sub PopulateUniqueValues()
  
        _distinctValues.Clear()
  
        For Each rawDistinctValue As Object In Me.getDistinctValuesCallback(Me.dataFieldDescriptor, True)
  
            If TypeOf rawDistinctValue Is Integer And _isFilterTypeSet = False Then
                _filterType = GetType(System.Int32)
                _isFilterTypeSet = True
            End If
  
            If Me.convertedAndFormattedValueFunc IsNot Nothing Then
                ' Convert the distinct value to take into account the
                ' IValueConverter on the column's DataMemberBinding (if any)
                ' and the column's DataFormatString (if any). If none of them
                ' exists, then this would be the identity function, i.e. it will
                ' return the same thing that was passed in.
                _distinctValues.Add(New UniqueValues(rawDistinctValue, Me.convertedAndFormattedValueFunc(rawDistinctValue)))
            Else
                ' Return the "raw" unformatted distinct value.
                _distinctValues.Add(New UniqueValues(rawDistinctValue, rawDistinctValue))
            End If
        Next
    End Sub
  
    Private Sub CreateFilterDescriptors()
  
        Me._compositeFilter = New CompositeFilterDescriptor With {.LogicalOperator = FilterCompositionLogicalOperator.Or}
        AddHandler Me._compositeFilter.FilterDescriptors.CollectionChanged, AddressOf OnCompositeFilterDescriptorsCollectionChanged
    End Sub
  
    Private Sub OnCompositeFilterDescriptorsCollectionChanged(ByVal sender As Object, ByVal e As Collections.Specialized.NotifyCollectionChangedEventArgs)
        If Me._compositeFilter.FilterDescriptors.Count > 0 Then
            If Not Me.targetFilters.Contains(Me._compositeFilter) Then
                Me.targetFilters.Add(Me._compositeFilter)
            End If
        Else
            Me.targetFilters.Remove(Me._compositeFilter)
        End If
    End Sub
  
    Public Sub ChangeFilter(ByVal value As Object)
  
        For Each f As FilterDescriptor In _compositeFilter.FilterDescriptors
            If f.Value = value Then
                _compositeFilter.FilterDescriptors.Remove(f)
                Return
            End If
        Next
  
        _compositeFilter.FilterDescriptors.Add(New FilterDescriptor(Me.dataMemberName, FilterOperator.IsEqualTo, value) With {.MemberType = _filterType})
    End Sub
  
#Region "Properties"
    Public ReadOnly Property DistinctValues As ObservableCollection(Of UniqueValues)
        Get
            Return _distinctValues
        End Get
    End Property
  
    Public ReadOnly Property IsActive
        Get
            Return True
        End Get
    End Property
  
#End Region
  
End Class

UniqueValues.vb
Imports System.ComponentModel
  
  
Public Class UniqueValues
    Implements INotifyPropertyChanged
  
    Private _value As Object
    Private _display As Object
    Private _isSelected As Boolean = False
  
    Public Sub New(ByVal value As Object, ByVal display As Object)
        _value = value
        _display = display
    End Sub
  
    Public Property Value As Object
        Get
            Return _value
        End Get
        Set(ByVal value As Object)
            _value = value
        End Set
    End Property
  
    Public Property Display As Object
        Get
            Return _display
        End Get
        Set(ByVal value As Object)
            _display = value
        End Set
    End Property
  
    Public Property IsSelected As Boolean
        Get
            Return _isSelected
        End Get
        Set(ByVal value As Boolean)
            If _isSelected = value Then Return
  
            _isSelected = value
  
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("IsSelected"))
        End Set
    End Property
  
    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Tags
GridView
Asked by
Joshua
Top achievements
Rank 1
Answers by
Ivan Ivanov
Telerik team
Scott
Top achievements
Rank 1
Joshua
Top achievements
Rank 1
Share this question
or