Sorting DataTable bound to DataGrid with custom datatypes - sorting

I have a DataTable where all column datatypes are of a custom class called CellModel.
The table's defaultview is bound to a DataGrid, which autogenerates some templatecolumns where I setup DataTemplates and Bindings, and showing the data works perfect.
My issue is when i try to sort on the columns.
I want to sort on a specific property within the custom class, but the default sorter won't recognize the custom class, so it invokes ToString() instead. By overriding and returning my property i'm able to sort, but it is PAINFULLY slow.
I've tried to implement a custom sorter on DataGrid.Sorting event like this:
private void DG_Sorting(object sender, DataGridSortingEventArgs e)
{
var column = e.Column;
e.Handled = true;
var direction = (column.SortDirection != ListSortDirection.Ascending)
? ListSortDirection.Ascending
: ListSortDirection.Descending;
var lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(DG.ItemsSource);
column.SortDirection = direction;
lcv.CustomSort = new MyComparer();
lcv.Refresh();
}
But GetDefaultView returns BindingListCollectionView which don't support CustomSorters..
I'm lost. How do I implement a custom comparer for sorting the data?

Found the issue. by using the lcv.SortDescriptions and implementing the IComparer interface on the CellModel class, i was able to use custom comparison.
Also, the sorting speed was influenced since i had Virtualization disabled on the Datagrid. by enabling Virtualization, the sorting was faster.

Related

Is Dynamic LINQ still in use and suitable for narrowing-down search results?

I am developing an ASP.NET MVC3 application in C#.
I am trying to implement in my application a "narrow-down" functionality applied the result-set obtained from a search.
In short, after I perform a search and the results displayed in the center of the page, I would like to have on the left/right side of the page a CheckBoxList helper for each property of the search result. The CheckBox of each CheckBoxList represent the distinct values of the property.
For instance if I search Product and it has a Color property with values blue, red and yellow, I create a CheckBoxList with text Color and three CheckBox-es one for each color.
After a research on the Web I found this Dynamic LINQ library made available by Scott Guthrie. Since the most recent example/tutorial I found is from 2009, I was wondering whether this library is actually good (and maintained) or not.
In the latter case is jQuery the best way to implement such functionality?
You can solve it by building the needed predicate expressions dynamically, using purely .NET framework.
See code sample below. Depending on the criteria, this will filter on multiple properties. I've used IQuerable because this will enable both In-Memory as remote scenario's such as Entity Framework. If you're going with Entity Framework, you could also just build an EntitySQL string dynamically. I expect that will perform better.
There is a small portion of reflection involved (GetProperty). But this could be improved by performing caching inside the BuildPredicate method.
public class Item
{
public string Color { get; set; }
public int Value { get; set; }
public string Category { get; set; }
}
class Program
{
static void Main(string[] args)
{
var list = new List<Item>()
{
new Item (){ Category = "Big", Color = "Blue", Value = 5 },
new Item (){ Category = "Small", Color = "Red", Value = 5 },
new Item (){ Category = "Big", Color = "Green", Value = 6 },
};
var criteria = new Dictionary<string, object>();
criteria["Category"] = "Big";
criteria["Value"] = 5;
var query = DoDynamicWhere(list.AsQueryable(), criteria);
var result = query.ToList();
}
static IQueryable<T> DoDynamicWhere<T>(IQueryable<T> list, Dictionary<string, object> criteria)
{
var temp = list;
//create a predicate for each supplied criterium and filter on it.
foreach (var key in criteria.Keys)
{
temp = temp.Where(BuildPredicate<T>(key, criteria[key]));
}
return temp;
}
//Create i.<prop> == <value> dynamically
static Expression<Func<TType, bool>> BuildPredicate<TType>(string property, object value)
{
var itemParameter = Expression.Parameter(typeof(TType), "i");
var expression = Expression.Lambda<Func<TType, bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
itemParameter,
typeof(TType).GetProperty(property)),
Expression.Constant(value)
),
itemParameter);
return expression;
}
}
I don't really get why would you need the Dynamic LINQ here? Are the item properties not known at compile-time? If you can access a given item properties by name, eg. var prop = myitem['Color'], you don't need Dynamic LINQ.
It depends on how you render the results. There is a lot of ways to achieve the desired behavior, in general:
Fully client-side. If you do everything client-side (fetching data, rendering, paging) - jQuery would be the best way to go.
Server-side + client-side. If you render results on the server, you may add HTML attributes (for each property) to each search result markup and filter those client-side. The only problem in this case can be paging (if you do paging server-side, you will be able to filter the current page only)
Fully server-side. Post the form with search parameters and narrow down the search results using LINQ - match the existing items' properties with form values.
EDIT
If I were you (and would need to filter results server-side), I'd do something like:
var filtered = myItems.Where(i => i.Properties.Match(formValues))
where Match is an extension method that checks if a given list of properties matches provided values. Simple as this - no Dynamic LINQ needed.
EDIT 2
Do you need to map the LINQ query to the database query (LINQ to SQL)? That would complicate things a bit, but is still doable by chaining multiple .Where(...) clauses. Just loop over the filter properties and add .Where(...) to the query from previous iteration.
you may have a look at PredicateBuilder from the author of C# 4.0 in a Nutshell
As already pointed out by #Piotr Szmyd probabbly you don't need dynamic Linq. Iterating over all properties of T doesn'require dynamic linq. Dynamic Linq is mainly usefull to build complete queries on the client side and send it in string format to the server.
However now, it become obsolete, since Mvc 4 supports client side queries through Api Controllers returning an IQueryable.
If you just need to iterate over all properties of T you can do it with reflection and by building the LambdaExpressions that will compose the filtering criterion. You can do it with the static methods of the Expression class.
By using such static methods you can build dynamically expressions like m => m.Name= "Nick" with a couple instructions...than you put in and them...done you get and expression you can apply to an exixting IQueryable
LINQ implementation still has not changed so there should be no problem using the dynamic LINQ library. It simply creates LINQ expressions from strings.
You can use AJAX to call action methods that run the LINQ query and return JSON data. JQuery would populate HTML from the returned data.

Why RadFilter is not detecting Columns correctly?

I am having trouble that RadFilter is not detecting the columns correctly. I bind my RadGrid through code in Asp like:
RadGrid1.DataSource = myDataSource;
RadGrid.DataBind();
RadFilter is only binding those columns which are bind through and ignoring those who are in . Is this a known issue? as I didn't see any thread talking about this problem. Waiting for help immediately.
Regards,
Wasim.
If I am understanding you correctly, you need to add FilterFieldEditors to the RadFilter for any field that is not represented in the DataSource for the grid.
You can add filters dynamically with the following code.
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!IsPostBack)
{
var fieldName = "myDynamicFilterField";
var friendlyName = "Friendly Dynamic Field";
var newTextFieldEditor = new RadFilterTextFieldEditor();
filter.FieldEditors.Add(newTextFieldEditor);
newTextFieldEditor.FieldName = fieldName; // name filter will used for filter exressions
newTextFieldEditor.DisplayName = friendlyName; // name use will see when they selected a field to filter
// ** Repeat for all dynamic fields **
}
}
I was able to get a working version using auto generated columns and custom added columns, but ran into many other problems. I ended up just dynamically creating all the filter fields (as shown above) from the data source and applying the filter expression to the grid.

Updating many fields in a database using reflection

I'm just discovering LINQ and finding it great. One problem thoush is that I have to copy a lot of fields during an update in a 3-tier (layer) application. The algorithm I used shows a typical LINQ update of a class.
I receive the object FromPresentation from the presentation layer.
I use LINQ to get the object with the same ID from the database.
I change a lot of fields
Save the changes.
And the corresponding code:
using (var ctx = new AppDataDataContext())
{
var OBJ =
(from Usu in ctx.usuarios
where Usu.ID == FromPresentation.ID
select Usu).SingleOrDefault();
if (OBJ != null)
{
OBJ.Nome = FromPresentation.Nome;
OBJ.NomeCurto = FromPresentation.NomeCurto;
OBJ.Login = FromPresentation.Login;
OBJ.Senha = FromPresentation.Senha;
OBJ.SuperUsuario = FromPresentation.SuperUsuario;
OBJ.Ativo = FromPresentation.Ativo;
// a lot more fields
ctx.SubmitChanges();
return OBJ.ID;
}
}
The problem is that I have a lot of fields. I even tried using reflection (using this question for guidance) to copy the fields but LINQ is not notified of the changes so it doesn't save anything.
How can I use reflection to copy values to a LINQ object so it can be updated in the database?
Don't use reflection for this, that would be reinventing the wheel - use a mapper like AutoMapper to do the work for you.
AutoMapper uses a fluent configuration API to define an object-object
mapping strategy. AutoMapper uses a convention-based matching
algorithm to match up source to destination values. Currently,
AutoMapper is geared towards model projection scenarios to flatten
complex object models to DTOs and other simple objects, whose design
is better suited for serialization, communication, messaging, or
simply an anti-corruption layer between the domain and application
layer.
You could try updating the properties rather than the fields.
eg
private static void UpdateForType(Type type, MyObject source, MyObject destination)
{
var myObjectProperties = type.GetProperties(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo pi in myObjectProperties)
pi.SetValue(destination, pi.GetValue(source, null), null);
}

WPF BindingListCollectionView to ListCollectionView for DataTable as ItemsSource

I want to do custom sorting on a ListView which has a DataTable as ItemsSource:
myListView.ItemsSource = (data as DataTable);
And this are the first lines of my sorting function:
DataView view = (myListView.ItemsSource as DataTable).DefaultView;
ListCollectionView coll = (ListCollectionView)CollectionViewSource.GetDefaultView(view);
The second line throws an execption like:
Unable to cast "System.Windows.Data.BindingListCollectionView" to "System.Windows.Data.ListCollectionView"
Has anyone a solution? Thx 4 answers
It returns an ICollectionView that is not a ListCollectionView. You most likely want a view on top of a view to get the features that ListCollectionView has. And since ICollectionView implements CollectionChanged, you wouldn't want to use BindingListCollectionView.
DataView view = (myListView.ItemsSource as DataTable).DefaultView;
ListCollectionView coll = new ListCollectionView(CollectionViewSource.GetDefaultView(view));
Although an alternative would be:
DataView view = (myListView.ItemsSource as DataTable).DefaultView;
BindingListCollectionView coll = new BindingListCollectionView(view);
If you only wanted only one view.
If you are binding directly to a WPF control, it is best to bind directly to it without making a BindingListCollectionView/ListCollectionView, as DefaultView already allows sorting of the DataTable.
Binding binding = new Binding() { Source = (myListView.ItemsSource as DataTable) };
this.myListView.SetBinding(myListView.ItemsSourceProperty, binding);
DataView view = (myListView.ItemsSource as DataTable).DefaultView;
view.Sort = "Age";
Hopefully Helpful,
TamusJRoyce

Using DataObjectTypeName in DataObjectSource

The functionality I am trying to use is:
- Create a ObjectDataSource for selection and updating controls on a web page (User Control).
- Use the DataObjectTypeName to have an object created that would send the data to an UpdateMethod.
- Before the values are populated in the DataObjectTypeName’s object, I would like to pre-populate the object so the unused items in the class are not defaulted to zeros and empty strings without me knowing whether the zero or default string was set by the user or by the application.
I cannot find a way to pre-populate the values (this was an issue back in 2006 with framework 2.0). One might ask “Why would anyone need to pre-populate the object?”. The simple answer is: I want to be able to randomly place controls on different User Controls and not have to be concerned with which UpdateMethod needs to handle which fields of an object.
For Example, let’s say I have a class (that reflects a SQL Table) that includes the fields: FirstName, LastName, Address, City, State, Zip. I may want to give the user the option to change the FirstName and LastName and not even see the Address, City, State, Zip (or vice-versa). I do not want to create two UpdateMethods where one handled FirstName and LastName and the other method handles the other fields. I am working with a Class of some 40+ columns from multiple tables and I may want some fields on one screen and not another and decide later to change those fields from one screen to another (which breaks my UpdateMethods without me knowing).
I hope I explained my issue well enough.
Thanks
This is hardly a solution to the problem, but it's my best stab at it.
I have a GridView with its DataSourceID set to an ObjectDataSource.
Whenever a row is updated, I want the property values in the object to be selectively updated - that is - only updated if they appear as columns in the GridView.
I've created the following extension:
public static class GridViewExtensions
{
public static void EnableLimitUpdateToGridViewColumns(this GridView gridView)
{
_gridView = gridView;
if (_gridView.DataSourceObject != null)
{
((ObjectDataSource)_gridView.DataSourceObject)
.Updating += new ObjectDataSourceMethodEventHandler(objectDataSource_Updating);
}
}
private static GridView _gridView;
private static void objectDataSource_Updating(object sender, ObjectDataSourceMethodEventArgs e)
{
var newObject = ((object)e.InputParameters[0]);
var oldObjects = ((ObjectDataSource)_gridView.DataSourceObject).Select().Cast<object>();
Type type = oldObjects.First().GetType();
object oldObject = null;
foreach (var obj in oldObjects)
{
if (type.GetProperty(_gridView.DataKeyNames.First()).GetValue(obj, null).ToString() ==
type.GetProperty(_gridView.DataKeyNames.First()).GetValue(newObject, null).ToString())
{
oldObject = obj;
break;
}
}
if (oldObject == null) return;
var dynamicColumns = _gridView.Columns.OfType<DynamicField>();
foreach (var property in type.GetProperties())
{
if (dynamicColumns.Where(c => c.DataField == property.Name).Count() == 0)
{
property.SetValue(newObject, property.GetValue(oldObject, null), null);
}
}
}
}
And in the Page_Init event of my page, I apply it to the GridView, like so:
protected void Page_Init()
{
GridView1.EnableLimitUpdateToGridViewColumns();
}
This is working well for me at the moment.
You could probably apply similar logic to other controls, e.g. ListView or DetailsView.
I'm currently scratching my head to think of a way this can be done in a rendering-agnostic manner - i.e. without having to know about the rendering control being used.
I hope this ends up as a normal feature of the GridView or ObjectDataSource control rather than having to hack it.

Resources