DataMemberBinding="{Binding FileExtension}"
The one that doesn't work binds to the object itself and uses a value converter:
DataMemberBinding="{Binding Converter={StaticResource FilePathConverterForSearch}}"
Sorting works based on the property I have set in SortMemberPath, but filtering does not. How can I get filtering to work on this column?
Thanks,
Aaron
15 Answers, 1 is accepted
Can you try to set the DataType of this column and see whether filtering will start working. The column needs to know the type and when there is a converter the type is "lost", i.e. the converter returns an object which can be anything, but I am sure that in your case it is always of the same type. Thus you should specify this type as the DataType of the column.
In case this does not help, can you please prepare a very small dummy project that mimics your original setup and send it to us. We will examine and debug it immediately to see what is the problem. You will have to open a separate support ticket in order to attach the project.
I hope this helps. We are looking forward to hearing from you.
Ross
the Telerik team
DataType="FileModelItem"
This throws XAML parse errors, I also tried adding the xml namespace for the class and setting the datatype using that:
xmlns:Schemas="clr-namespace:MyApp.Models;assembly=MyApp"
...
DataType="Schemas:FileModelItem"
or
DataType="{Schemas:FileModelItem}
and every variation of those that I could think of. The only thing that does not throw a XAML parse error is binding the DataType to a ValueConverter and returning the type from the value converter. This however still does not get filtering to work.
I was not aware that your type was complex. Filtering will not work on a complex type unless you do several things.
Since we perform filtering by using LINQ (i.e. Where), in order to filter complex types, you will need to do several things.
The only possible way to enable filtering on such a complex type would be to implement the IComparable and IEquatable interfaces. Additionally, the class should have its Equals and GetHashCode methods overriden. You must have GetHashCode, since it appears that the Distinct LINQ Extension method uses it. The class should also override the "==" and "!=" operators.
If all of these conditions are met, then the data engine should be able to compare items of this type and thus perform filtering on a list of such objects.
In case you are not happy with the stock filtering control, which is designed for simple types like strings, dates, numbers, etc. you can always develop your very own filtering control like I have described in my blog post.
If you don't want to do all of this, you can create a new string property on your business object that will somehow return a string representation of this FileModeItem. You will then show this new string column and you will have normal string filtering and you will not have to do anything additional. Simply hide the complex type column and show the string one.
Still, if you decide to stick to the complex type, you will have to set the column's DataType. Silverlight 4 will not allow you to set it like you have tried in XAML (let's hope SL 5 will...). You have to assign the DataType either either in code-behind or expose this particular type as a static resource and then data bind to it, if you really really want to specify the DataType in XAML.
I hope this helps.
Ross
the Telerik team
I have 2 columns (GridViewDataColumn) to be filterable, the columns are constructed on the run time, the data of this columns is binded, the Data Type of them is 'String', so I set it as follows:
column.DataType = typeof(string);
I do see the Filter sign on them and the Filter window is openned, but with 'Error on Page' in the left bottom corner of the application and the using of the filter is impossible.The error is: 'At least one object must implement IComparable', but 'String' already implements IComparable... So I don't know what I'm missing
Thanks,
Viki
If you send us a sample project that demonstrates this, we will debug it. We need something that we can run and debug in order to help you.
Greetings,Ross
the Telerik team
Xaml:
<
telerikGrid:RadGridView
x:Name
=
"gridManagerReportResult"
Grid.Row
=
"3"
Grid.Column
=
"1"
RowIndicatorVisibility
=
"Collapsed"
ScrollMode
=
"RealTime"
AutoGenerateColumns
=
"False"
VerticalAlignment
=
"Stretch"
HorizontalAlignment
=
"Stretch"
AutoExpandGroups
=
"True"
telerikControl:StyleManager.Theme
=
"{StaticResource RosterTheme}"
ElementExporting
=
"gridManagerReportResult_ElementExporting"
>
<
telerikGrid:RadGridView.Columns
>
<
telerikGrid:GridViewDataColumn
Header
=
"Data Type"
UniqueName
=
"DataType"
DataMemberBinding
=
"{Binding Data, Converter={StaticResource ValueConverter}, ConverterParameter=Name}"
IsReadOnly
=
"True"
/>
</
telerikGrid:RadGridView.Columns
>
</
telerikGrid:RadGridView
>
C#:
private
void
InitializeData()
{
try
{
// Build the dynamic Days columns
BuildDaysColumns();
gridManagerReportResult.ItemsSource = m_DataTable.Table.EmployeesData;
// Enabling the column's Filter on the 'Data Type' column
var column =
this
.gridManagerReportResult.Columns[
"DataType"
]
as
GridViewBoundColumnBase;
if
(column !=
null
)
{
column.DataType =
typeof
(
string
);
}
}
catch
(Exception ex)
{
}
}
private
void
BuildDaysColumns()
{
if
(m_DataTable.Table !=
null
&& m_DataTable.Table.ReportTime.Duration.Days > 0)
{
int
iDaysCount = m_DataTable.Table.ReportTime.Duration.Days;
for
(
int
iDayIndex = 0; iDayIndex < iDaysCount; iDayIndex++)
{
GridViewDataColumn column =
new
GridViewDataColumn();
// Set the Binding to SingleEmployeeReport => RowObject.Data
Binding binding =
new
Binding(
"Data"
);
binding.Mode = BindingMode.OneWay;
// Set the Converter
binding.Converter =
new
PS_DayColumnConverter();
binding.ConverterParameter = iDayIndex;
column.DataMemberBinding = binding;
column.Header = m_DataTable.Table.ReportTime.Start.AddDays(iDayIndex).ToString(
"ddd, d/MM/yy"
);
column.IsReadOnly =
true
;
column.Width = 130;
// Enabling the column's Filter
column.DataType =
typeof
(
string
);
gridManagerReportResult.Columns.Add(column);
}
}
}
public
class
PS_DayColumnConverter : IValueConverter
{
#region IValueConverter Members
public
object
Convert(
object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
{
if
(parameter ==
null
)
return
null
;
EmployeeDataType data = value
as
EmployeeDataType;
// The 'parameter' is 'Name' for Data Type name
if
(parameter.ToString().Equals(
"Name"
))
{
return
data.DataName; //DataName is 'String'
}
// If 'parameter' isn't 'Name' it is column index
int
iColumnIndex = (
int
)parameter;
List<String> lstDaysData = data.DataValues;
if
(iColumnIndex >= lstDaysData.Count)
{
return
null
;
}
return
lstDaysData[iColumnIndex]; //lstDaysData is List<String>
}
public
object
ConvertBack(
object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
{
return
null
;
}
#endregion
}
Unfortunately, I am not able build and to run the code snippets that you have provided. I can only make wild guesses by looking at that code.
What I can see is that you are binding the column to a EmployeeDataType and then you are telling it that its DataType is a string, which is obviously wrong. The EmployeeDataType type is not a string.
Filtering, Sorting and Grouping are always performed on the raw data values -- they don't take IValueConverters in mind, since IValueConverters are for display purposes only and they do not play any part in the data engine. This is how RadGridView is designed and works.
So the bottom-line is that if your column is bound to a EmployeeDataType, you can's simply lie to it and tell it that its DataType is a string when it obviously is not.
But then again, I don't have anything that I can run and debug so this is just my wild guess.
Greetings,
the Telerik team
Yes. We can not compare objects unless they are comparable in some way.
However, you no longer need to override the '==' and '!=' operators since we now build a LINQ expression that uses an Object.Equals method call instead of the equality and inequality operators.
But in case you want your filtering control to show the IsGreaterThan, IsLessThan, IsGreaterThanOrEqual and IsLessThanOrEqual options you would have to override the respective operators on your custom class: >, <, >=, <=.
I hope this helps.
Ross
the Telerik team
I simplified the code, so now the columns that are created on the run time, their content is bound to data of List<String> type (where column index (date) is an index in this List, so each column is of String type).
So do I have now simplier way (than to implement IComparable and IEquantable interfaces and override Equals and GetHashCode methods) to get the column to be filterable?
Here is the relevant code:
private
void
BuildDaysColumns()
{
if
(m_DataTable.Table !=
null
&& m_DataTable.Table.ReportTime.Duration.Days > 0)
{
int
iDaysCount = m_DataTable.Table.ReportTime.Duration.Days;
for
(
int
iDayIndex = 0; iDayIndex < iDaysCount; iDayIndex++)
{
GridViewDataColumn column =
new
GridViewDataColumn();
// Set the Binding to SingleEmployeeReport => RowObject.Data
Binding binding =
new
Binding(
"DataValues"
);
binding.Mode = BindingMode.OneWay;
// Set the Converter
binding.Converter =
new
PS_DayColumnConverter();
binding.ConverterParameter = iDayIndex;
column.DataMemberBinding = binding;
column.Header = m_DataTable.Table.ReportTime.Start.AddDays(iDayIndex).ToString(
"ddd, d/MM/yy"
);
column.IsReadOnly =
true
;
column.Width = 130;
column.DataType =
typeof
(List<
string
>);
// ????????
gridManagerReportResult.Columns.Add(column);
}
}
}
public
class
PS_DayColumnConverter : IValueConverter
{
#region IValueConverter Members
public
object
Convert(
object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
{
if
(parameter ==
null
)
return
null
;
int
iColumnIndex = (
int
)parameter;
List<String> lstDaysData = value
as
List<
string
>;
if
(iColumnIndex >= lstDaysData.Count)
{
return
null
;
}
return
lstDaysData[iColumnIndex];
}
public
object
ConvertBack(
object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
{
return
null
;
}
#endregion
}
Thanks,
Viki
No. If you bind a column to show a property which is a List<string> you will not get any filtering at all.
How do you compare two List<string> instances? You can't.
List<string> list1 = <<something>>;
List<string> list2 = <<something else>>;
What does list1 < list2 return? What does list1 > list2 return? It will not even compile.
This will not work.
Ross
the Telerik team
Let me try to explain this once more.
Filtering, Sorting and Grouping are always performed on the raw data values -- they don't take IValueConverters in mind, since IValueConverters are for display purposes only and they do not play any part in the data engine.
So if your column is data-bound to something of type X, this X has to meet the filtering requirements in order for filtering to work. Which List<string> does not, obviously.
Please, forget what you see in the grid cells after the IValueConverter has been applied. The IValueConverter is used for display purposes only.
Data operations are performed on the raw values that the column is bound to.
And List<string> obviously can not be filtered, so there is absolutely no point in binding your column to a list of strings.
I hope that this makes sense.
Ross
the Telerik team
Thank you for your detailed and well argued response :)
The columns that are bound to the List<String> data are constructed on the run time and their amount is unknown before it, so I don't see another option of their data binding, but to List.
So, thanks again!
Viki
"The only possible way to enable filtering on such a complex type would be to implement the IComparable and IEquatable interfaces. Additionally, the class should have its Equals and GetHashCode methods overriden. You must have GetHashCode, since it appears that the Distinct LINQ Extension method uses it. The class should also override the "==" and "!=" operators."
in yours documentation and for WPF also.
Any way I faced with similar issue in WPF project and a few hours to find solution. My complex type satisfies all requirements(implement the IComparable and IEquatable and etc). But it doesn't help as it needs magic in xaml. As result I got working sorting and almost filtering but got another issue.
I have column that you can see below(I wonder how to continue message after pasting code block, probably IE issue but still one more issue). Applying FilterMemberPath property kills filtering and popup shows empty list. Without this property Filter popup just calls ToString method. Also I tried to use FilterMemberPath="Name" grid use member of the parent object. So do I have another solutions than overriding ToString in my complex type and rewriting filter popup?
<
telerik:GridViewDataColumn
DataMemberBinding
=
"{Binding IndexType}"
FilterMemberPath
=
"IndexType.Name"
Header
=
"{x:Static localization:Resources.Type}"
>
<
telerik:GridViewDataColumn.CellTemplate
>
<
DataTemplate
>
<
TextBlock
Text
=
"{Binding IndexType.Name}"
/>
</
DataTemplate
>
</
telerik:GridViewDataColumn.CellTemplate
>
</
telerik:GridViewDataColumn
>
I will try to elaborate more on how the filtering actually works. It is a data operation which means it is done by building and executing a LINQ query appending proper Where clause over the source collection.
For example:
var result = collection.Where(item => item.Property ==
"Something"
);
The reason for not working should be that our data engine cannot build a valid LINQ query based on the specified binding. So, if you can build such a LINQ query appending Where clause based on the DataMemberBinding set for a column, then RadGridView should also be able to filter the respective column. Otherwise, this will not be possible.
If you can build a valid LINQ query and RadGridView still does not respond accordingly, then may I ask you to isolate the case in a demo project which we can check locally? You can open a new support ticket and attach it there.
Regards,
Dimitrina
Telerik
Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.