Filter a Custom Type

If you want to filter a column that is data-bound to a custom type, you need to make sure that your custom type meets certain criteria. We will use the type Person as an example.

Example 1: The Person class

public class Person 
{ 
    private readonly string name; 
    private int age; 
 
    public string Name 
    { 
        get { return this.name; } 
    } 
 
    public int Age 
    { 
        get { return this.age; } 
        set { this.age = value; } 
    } 
 
    public Person(string name) 
    { 
        this.name = name; 
    } 
} 
Public Class Person 
    Private ReadOnly m_name As String 
    Private m_age As Integer 
 
    Public ReadOnly Property Name() As String 
        Get 
            Return Me.m_name 
        End Get 
    End Property 
 
    Public Property Age() As Integer 
        Get 
            Return Me.m_age 
        End Get 
        Set(value As Integer) 
            Me.m_age = value 
        End Set 
    End Property 
 
    Public Sub New(name As String) 
        Me.m_name = name 
    End Sub 
End Class 

The first thing that you need to do is implement the generic IEquatable interface. It has a single method called Equals. Next, you need to override Object.Equals(Object) and Object.GetHashCode. MSDN states that if you implement generic IEquatable, you have to also override the base class implementations of Object.Equals(Object) and Object.GetHashCode so that their behavior is consistent with that of the generic IEquatable.Equals method.

Implement IEquatable

Example 2: IEquatable implementation

public class Person : IEquatable<Person> 
{ 
    private readonly string name; 
    private int age; 
 
    public string Name 
    { 
        get { return this.name; } 
    } 
 
    public int Age 
    { 
        get { return this.age; } 
        set { this.age = value; } 
    } 
 
    public Person(string name) 
    { 
        this.name = name; 
    } 
 
    bool IEquatable<Person>.Equals(Person other) 
    { 
        if (other == null) 
        { 
            return false; 
        } 
 
        return StringComparer.Ordinal.Equals(this.Name, other.Name); 
    } 
} 
Public Class Person 
    Implements IEquatable(Of Person) 
    Private ReadOnly m_name As String 
    Private m_age As Integer 
 
    Public ReadOnly Property Name() As String 
        Get 
            Return Me.m_name 
        End Get 
    End Property 
 
    Public Property Age() As Integer 
        Get 
            Return Me.m_age 
        End Get 
        Set(value As Integer) 
            Me.m_age = value 
        End Set 
    End Property 
 
    Public Sub New(name As String) 
        Me.m_name = name 
    End Sub 
 
    Public Function GenericEquals(other As Person) As Boolean Implements IEquatable(Of Person).[Equals] 
        If other Is Nothing Then 
            Return False 
        End If 
 
        Return StringComparer.Ordinal.Equals(Me.Name, other.Name) 
    End Function 
End Class 

Override Object.Equals(Object) and Object.GetHashCode

If you do override Object.Equals(Object), your overridden implementation is also called in calls to the static Equals(System.Object, System.Object) method on your class. This ensures that all invocations of the Equals method return consistent results. Furthermore, the GetHashCode method will be used by the framework when the distinct values need to be discovered.

Example 3: Equals and GetHashCode overrides

public override bool Equals(object obj) 
{ 
    return ((IEquatable<Person>)this).Equals(obj as Person); 
} 
 
public override int GetHashCode() 
{ 
    return this.Name.GetHashCode() ^ this.Age.GetHashCode(); 
} 
Public Overrides Function Equals(obj As Object) As Boolean 
    Return DirectCast(Me, IEquatable(Of Person)).Equals(TryCast(obj, Person)) 
End Function 
 
Public Overrides Function GetHashCode() As Integer 
    Return Me.Name.GetHashCode() Xor Me.Age.GetHashCode() 
End Function 

Override ToString

Next, you need to override the ToString method of your type so that distinct values and grid cells display a friendly representation of your class. Here is what the class might look like:

Example 4: ToString override

public override string ToString() 
{ 
    return this.Name; 
} 
Public Overrides Function ToString() As String 
    Return Me.Name 
End Function 

Define a TypeConverter for String Conversions

Next you will need to define a TypeConverter for string conversions. When RadGridView encounters a custom type it will use a plain TextBox for the field filter editors. The strings that user enters have to be converted to your custom type and vice versa. This can be achieved by specifying a TypeConverter on your class.

Example 5: Custom TypeConverter

public class PersonConverter : System.ComponentModel.TypeConverter 
{ 
    public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType) 
    { 
        if (sourceType == typeof(string)) 
        { 
            return true; 
        } 
 
        return base.CanConvertFrom(context, sourceType); 
    } 
 
    public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) 
    { 
        var stringValue = value as string; 
        if (stringValue != null) 
        { 
            return new Person(stringValue); 
        } 
 
        return base.ConvertFrom(context, culture, value); 
    } 
 
    public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, Type destinationType) 
    { 
        if (destinationType == typeof(string)) 
        { 
            return true; 
        } 
 
        return base.CanConvertTo(context, destinationType); 
    } 
 
    public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) 
    { 
        if (destinationType == typeof(string)) 
        { 
            return ((Person)value).ToString(); 
        } 
 
        return base.ConvertTo(context, culture, value, destinationType); 
    } 
} 
Public Class PersonConverter 
    Inherits System.ComponentModel.TypeConverter 
    Public Overrides Function CanConvertFrom(context As System.ComponentModel.ITypeDescriptorContext, sourceType As Type) As Boolean 
        If sourceType = GetType(String) Then 
            Return True 
        End If 
 
        Return MyBase.CanConvertFrom(context, sourceType) 
    End Function 
 
    Public Overrides Function ConvertFrom(context As System.ComponentModel.ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object) As Object 
        Dim stringValue = TryCast(value, String) 
        If stringValue IsNot Nothing Then 
            Return New Person(stringValue) 
        End If 
 
        Return MyBase.ConvertFrom(context, culture, value) 
    End Function 
 
    Public Overrides Function CanConvertTo(context As System.ComponentModel.ITypeDescriptorContext, destinationType As Type) As Boolean 
        If destinationType = GetType(String) Then 
            Return True 
        End If 
 
        Return MyBase.CanConvertTo(context, destinationType) 
    End Function 
 
    Public Overrides Function ConvertTo(context As System.ComponentModel.ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object, destinationType As Type) As Object 
        If destinationType = GetType(String) Then 
            Return DirectCast(value, Person).ToString() 
        End If 
 
        Return MyBase.ConvertTo(context, culture, value, destinationType) 
    End Function 
End Class 

Do not forget to add the TypeConverter attribute on your class definition and point it to the custom TypeConverter that you just created.

Example 6: Adding the TypeConverter attribute

[TypeConverter(typeof(PersonConverter))] 
public class Person : IEquatable<Person> 
{ 
    // ... 
} 
<TypeConverter(GetType(PersonConverter))> 
Public Class Person 
    Implements IEquatable(Of Person) 
 
    ' ... 
End Class 

If the plain TextBox does not suit your needs, you can provide your own field filter editor by overriding the GridViewDataColumn.CreateFieldFilterEditor method as described here. You will no longer need a TypeConverter if your custom field filter editor is able to produce instances of your custom type.

Override the Comparison Operators (Optional)

If you want to see the comparison filter operators (Is Less Than, etc.) you should override your custom type's comparison operators.

Example 7: Comparison operators override

public static bool operator <(Person left, Person right) 
{ 
    return left.Age < right.Age; 
} 
 
public static bool operator <=(Person left, Person right) 
{ 
    return left.Age <= right.Age; 
} 
 
public static bool operator >(Person left, Person right) 
{ 
    return left.Age > right.Age; 
} 
 
public static bool operator >=(Person left, Person right) 
{ 
    return left.Age >= right.Age; 
} 
Public Shared Operator <(left As Person, right As Person) As Boolean 
    Return left.Age < right.Age 
End Operator 
 
Public Shared Operator <=(left As Person, right As Person) As Boolean 
    Return left.Age <= right.Age 
End Operator 
 
Public Shared Operator >(left As Person, right As Person) As Boolean 
    Return left.Age > right.Age 
End Operator 
 
Public Shared Operator >=(left As Person, right As Person) As Boolean 
    Return left.Age >= right.Age 
End Operator 

See Also

In this article