I create a DataGridView as follows
private void IniciarGV()
{
using (var db = new Entities())
{
var ListaPantallas = (from a in db.PANTALLAS
select a).ToList();
if (ListaPantallas != null)
{
gvPermisos.DataSource = ListaPantallas;
gvPermisos.Columns["idPantalla"].Visible = false;
gvPermisos.Columns["nombrepantalla"].HeaderText = "Nombre";
//gvPermisos.Columns.Add(new DataGridViewCheckBoxColumn());
DataGridViewCheckBoxColumn checkBoxColumn = new DataGridViewCheckBoxColumn();
checkBoxColumn.HeaderText = "Seleccione";
checkBoxColumn.Width = 50;
checkBoxColumn.Name = "checkBoxColumn";
gvPermisos.Columns.Insert(0, checkBoxColumn);
//gvPermisos.EndEdit();
}
db.Dispose();
}
}
After that a i retrieve the privileges with the same method linq
var TraerPermisos = (from a in db.PERMISOS
where a.IDUSUARIO == UsuarioEditar.idUsuario
select a).ToList();
After i go through the gridview to match the privileges with the ids that i wanted to check so the user can edit them, but for some reason they always appear unmarked
so far this is what i encounter in every forum or google, but it doesnt seem to work
foreach (PERMISOS item in TraerPermisos)
{
foreach (DataGridViewRow row in gvPermisos.Rows)
{
var ValorPermisoEnGV = Convert.ToBoolean(row.Cells["checkBoxColumn"].Value);
var ValorPantalla = decimal.Parse(row.Cells["idPantalla"].Value.ToString());
if (ValorPantalla == item.IDPANTALLA)
{
row.Cells["checkBoxColumn"].Value = true;
}
}
}
this what my form looks like
I load the privileges on start
public ManejarUsuario()
{
InitializeComponent();
IniciarComboBox();
IniciarGV();
if (UsuarioEditar.idUsuario != 0)
{
CargarDatos();
btnCrearUsuario.Text = "Editar";
CargarPrivilegios();
}
}
Sorry about the long post, i've tried so many options but none seems to work
Life would be a lot easier if you just put a Boolean in the data driving the grid. First, let's make a holder class for your info
class Perms{
public int IdPantalla { get; set; }
public string NombrePantalla { get; set; }
public bool HasPermission { get; set; }
}
Then let's have the query produce a list of these instead:
//get all the IDPANTALLA this user has and put in a hash set
var TraerPermisos = (from a in db.PERMISOS
where a.IDUSUARIO == UsuarioEditar.idUsuario
select a.IDPANTALLA).ToHashSet();
//make a list of objects that includes a bool of whether the user has that permission
//note you should have this be a class level variable for ease of use later/elsewhere
ListaPantallas = (from a in db.PANTALLAS
select new Perms {
IdPantalla a.IdPantalla,
HasPermission = TraerPermisos.Contains(a.idPantalla) ,
NombrePantalla = a.nombrepantalla
}).ToList();
Then set up the grid:
gvPermisos.DataSource = ListaPantallas;
gvPermisos.Columns["IdPantalla"].Visible = false;
gvPermisos.Columns[ "NombrePantalla"].HeaderText = "Nombre";
gvPermisos.Columns[ "HadPermission"].HeaderText = "Seleccione";
The grid will find the bool property in the list and wire it up for you as a check column. It doesn't need you to make a column for it. Anything you tick is then stored in the underlying list as a true/false and you can eg foreach(var x in ListaPantallas) if(x.HasPermission...
When manipulating data in a DGV that is bound to a data source, manipulate the source. Also consider to make Perms implements INotifyPropertyChanged (and consider switching to using a binding list)
I need help on part of this code. I'm using a checkbox column in my DataGridView control. When I retrieve my data record, if a value exists then the checkbox should be checked, and if not it should remain unchecked. How to I accomplish that on a DataGridView with this kind of logic?
using (DataContext dtContext = new DataContext())
{
var query = (from i in dtContext.materialTBheader where i.proj == Proj_id select i);
foreach (var r in query)
{
if (!string.IsNullOrEmpty(r.materialheader_id.ToString()))
{
string[] row = { r.materialheader_id.ToString(), r.materialname, r.description, string.Format("{0:n2}", r.totalAmount), GetCount(r.materialname, txtMainProjectHeader_id, Convert.ToDecimal(r.totalAmount)), "", -- cell checkbox if record exist true checked if not false uncheck };
dGVMaterialHeaderList.Rows.Add(row);
}
}
}
It dosen't need to add your rows by foreach, use DataSource Property
Suppose you have a List of Person and you want to show in dataGridview, you have to option
1)add your column to data grid in visual studio properties window
2)add your column with coding
then map your data to grid
here is an simple example to help yo
public class Person
{
public int Id { get; set; }
public string LastName { get; set; }
public bool Married { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
//your data from ef
var myData = new List<Person>
{
new Person{Id=1,LastName="A1",Married =true},
new Person{Id=1,LastName="A2",Married =false},
new Person{Id=1,LastName="A3",Married =true},
};
//your columns
var idColumn = new System.Windows.Forms.DataGridViewTextBoxColumn
{
Name = "Id",
HeaderText = "Id",
DataPropertyName = "Id"
};
var lastNameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn
{
Name = "LastName",
HeaderText = "LastName",
DataPropertyName = "LastName"
};
var marriedColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn
{
Name="Married",
HeaderText="Married",
DataPropertyName= "Married"
};
// add your columns to grid
dGVMaterialHeaderList.Columns.Add(idColumn);
dGVMaterialHeaderList.Columns.Add(lastNameColumn);
dGVMaterialHeaderList.Columns.Add(marriedColumn);
dGVMaterialHeaderList.AutoGenerateColumns = false;
//bind your data
dGVMaterialHeaderList.DataSource = myData;
}
In your case the answer is something like below:
using (DataContext dtContext = new DataContext())
{
var query = (from i in dtContext.materialTBheader where i.proj == Proj_id select i).ToList();
var gridData = query.Select(r=>new
{
materialheader_id = r.materialheader_id.ToString(),
r.materialname,
r.description,
totalAmount = string.Format("{0:n2}", r.totalAmount),
Count = GetCount(r.materialname, txtMainProjectHeader_id, Convert.ToDecimal(r.totalAmount)),
isExist = !string.IsNullOrEmpty(r.materialheader_id.ToString())?true:false
}).ToList();
dGVMaterialHeaderList.DataSource = gridData;
}
I don't know your data structure but I know this is not best approach you choose
I hope this can help you
I'm trying to write to a model for the first time to use in my view: the first time I write to the model I get an ArgumentOutOfRangeException.
Getting error on first write to array:
private IAdditionalQuestionsService _service;
private SelectedAdditionalQuestionAnswerModel _model;
private void InitializeController()
{
_service = GetObject<IAdditionalQuestionsService>();
//GetPageHeaderText(inst);
ViewBag.GetPageTitle = "Additional Questions";
}
[HttpGet]
public virtual ActionResult Edit()
{
Institution inst = _service.GetInstitution(State.GetInstitutionRecordId());
_model = GetObject<SelectedAdditionalQuestionAnswerModel>();
_model.AddQuestAnswModel = new List<AdditionalQuestionAnswerModel>();
GetPageConfiguration1(inst);
return View(_model);
}
AdditionalQuestionAnswerModel m = GetObject<AdditionalQuestionAnswerModel>();
int c = 0;
foreach (var x in inst.AdditionalQuestions)
{
foreach (var y in x.AdditionalQuestionAnswers)
{
// Error is happening on next line *************
_model.AddQuestAnswModel[c].QuestionText = x.QuestionText;
_model.AddQuestAnswModel[c].InstitutionId = x.InstitutionId;
_model.AddQuestAnswModel[c].AdditionalQuestionId = x.Id;
_model.AddQuestAnswModel[c].AnswerText = y.AnswerText;
_model.AddQuestAnswModel[c].IsSelected = false;
c++;
}
}
You can't use _model.AddQuestAnswModel[c] because you never added any items to your list.
Instead of that, create a new object and set its values and then add the item to your list.
Something like this:
AdditionalQuestionAnswerModel newItem = new AdditionalQuestionAnswerModel();
//set the values here to newItem
_model.AddQuestAnswModel.Add(newItem);
You're firstly instantiating your list
_model.AddQuestAnswModel = new List<AdditionalQuestionAnswerModel>();
then you try to access to the first element
_model.AddQuestAnswModel[c] // c == 0
without adding any element to the list.
Add an element before trying to access to a list by index, or more simple:
foreach (var y in x.AdditionalQuestionAnswers)
{
AdditionalQuestionAnswerModel newObj = new AdditionalQuestionAnswerModel
{
QuestionText = x.QuestionText;
InstitutionId = x.InstitutionId;
AdditionalQuestionId = x.Id;
AnswerText = y.AnswerText;
IsSelected = false;
};
_model.AddQuestAnswModel.Add(newObj);
}
Ir means that there no item in your _model.AddQuestAnswModel at the indicated postition, and from your code, I see that _model.AddQuestAnswModel has only be initiated with new List<AdditionalQuestionAnswerModel>(), so it does not contain items (unless you're doing it in the contructor).
You need to fill it like so :
_model.AddQuestAnswModel.Add(item);
I have a datagridview in my form. It fills by selecting country with cities of country.I have set the property (AllowUsersToAddRow = True)
but when i run my project user can't add or edit or delete any row.I checked it.It is not readonly(readonly = false) and It is enable (Enabled = true)
What's the problem?
Code of fill datagridview:
private void cmbCountryValues_SelectedIndexChanged(object sender, EventArgs e)
{
dgvCityValues.Enabled = cmbCountryValues.SelectedIndex>=0;
if (!dgvCityValues.Enabled)
{
dgvCityValues.DataSource = null;
return;
}
int CountryId = int.Parse(cmbCountryValues.SelectedValue.ToString());
dgvValues.DataSource = from record in Program.dal.Cities(CountryId) select new { record.City};
}
If you find this question useful don't forgot to vote it.
To give a simplified example, if I do the equivalent of your query, such as:
var cities = new City[] { new City("New York","NY"), new City("Sydney","SY"), new City("London","LN") };
dataGridView.DataSource = cities;
I get the same result as you - no option to add new rows, but if I change to BindingList<T> and set this to AllowNew it all works:
var cities = new City[] { new City("New York","NY"), new City("Sydney","SY"), new City("London","LN") };
var citiesBinding = new BindingList<City>(cities);
citiesBinding.AllowNew = true;
dataGridView.DataSource = citiesBinding;
EDIT - with a solution for your particular example:
private class City
{
public string Name { get; set; }
}
private void cmbCountryValues_SelectedIndexChanged(object sender, EventArgs e)
{
dgvCityValues.Enabled = cmbCountryValues.SelectedIndex >= 0;
if (!dgvCityValues.Enabled)
{
dgvCityValues.DataSource = null;
return;
}
int CountryId = int.Parse(cmbCountryValues.SelectedValue.ToString());
var queryResults = from record in Program.dal.Cities(CountryId) select new City { Name = record.City };
var queryBinding = new BindingList<City>(queryResults.ToList());
queryBinding.AllowNew = true;
dgvValues.DataSource = queryBinding;
}
Note that a) I had to change the anonymous type in the query select into a concrete type City and also change the IEnumerable<T> returned by Linq query to an IList<T> compatible type to create the BindingList<T>. This should work, however :)
I have a datagridview on my form and I populate it with this:
dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })
.OrderBy(s => s.Apellidos)
.ToList();
Now, I use the s.Apellidos as the default sort, but I'd also like to allow users to sort when clicking on the column header.
This sort will not modify the data in any way, it's just a client side bonus to allow for easier searching for information when scanning the screen with their eyes.
Thanks for the suggestions.
Set all the column's (which can be sortable by users) SortMode property to Automatic
dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })
.OrderBy(s => s.Apellidos)
.ToList();
foreach(DataGridViewColumn column in dataGridView1.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
Edit: As your datagridview is bound with a linq query, it will not be sorted. So please go through this [404 dead link, see next section] which explains how to create a sortable binding list and to then feed it as datasource to datagridview.
Code as recovered from dead link
Link from above is 404-dead. I recovered the code from the Internet Wayback Machine archive of the page.
public Form1()
{
InitializeComponent();
SortableBindingList<person> persons = new SortableBindingList<person>();
persons.Add(new Person(1, "timvw", new DateTime(1980, 04, 30)));
persons.Add(new Person(2, "John Doe", DateTime.Now));
this.dataGridView1.AutoGenerateColumns = false;
this.ColumnId.DataPropertyName = "Id";
this.ColumnName.DataPropertyName = "Name";
this.ColumnBirthday.DataPropertyName = "Birthday";
this.dataGridView1.DataSource = persons;
}
As Niraj suggested, use a SortableBindingList. I've used this very successfully with the DataGridView.
Here's a link to the updated code I used - Presenting the SortableBindingList - Take Two - archive
Just add the two source files to your project, and you'll be in business.
Source is in SortableBindingList.zip - 404 dead link
One more way to do this is using "System.Linq.Dynamic" library. You can get this library from Nuget. No need of any custom implementations or sortable List :)
using System.Linq.Dynamic;
private bool sortAscending = false;
private void dataGridView_ColumnHeaderMouseClick ( object sender, DataGridViewCellMouseEventArgs e )
{
if ( sortAscending )
dataGridView.DataSource = list.OrderBy ( dataGridView.Columns [ e.ColumnIndex ].DataPropertyName ).ToList ( );
else
dataGridView.DataSource = list.OrderBy ( dataGridView.Columns [ e.ColumnIndex ].DataPropertyName ).Reverse ( ).ToList ( );
sortAscending = !sortAscending;
}
You don't need to create a binding datasource. If you want to apply sorting for all of your columns, here is a more generic solution of mine;
private int _previousIndex;
private bool _sortDirection;
private void gridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex == _previousIndex)
_sortDirection ^= true; // toggle direction
gridView.DataSource = SortData(
(List<MainGridViewModel>)gridReview.DataSource, gridReview.Columns[e.ColumnIndex].Name, _sortDirection);
_previousIndex = e.ColumnIndex;
}
public List<MainGridViewModel> SortData(List<MainGridViewModel> list, string column, bool ascending)
{
return ascending ?
list.OrderBy(_ => _.GetType().GetProperty(column).GetValue(_)).ToList() :
list.OrderByDescending(_ => _.GetType().GetProperty(column).GetValue(_)).ToList();
}
Make sure you subscribe your data grid to the event ColumnHeaderMouseClick. When the user clicks on the column it will sort by descending. If the same column header is clicked again, sorting will be applied by ascending.
your data grid needs to be bound to a sortable list in the first place.
Create this event handler:
void MakeColumnsSortable_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
//Add this as an event on DataBindingComplete
DataGridView dataGridView = sender as DataGridView;
if (dataGridView == null)
{
var ex = new InvalidOperationException("This event is for a DataGridView type senders only.");
ex.Data.Add("Sender type", sender.GetType().Name);
throw ex;
}
foreach (DataGridViewColumn column in dataGridView.Columns)
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
And initialize the event of each of your datragrids like this:
dataGridView1.DataBindingComplete += MakeColumnsSortable_DataBindingComplete;
You can use DataGridViewColoumnHeaderMouseClick event like this :
Private string order = String.Empty;
private void dgvDepartment_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (order == "d")
{
order = "a";
dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth }) .OrderBy(s => s.Apellidos).ToList();
}
else
{
order = "d";
dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth }.OrderByDescending(s => s.Apellidos) .ToList()
}
}
there is quite simply solution when using Entity Framework (version 6 in this case). I'm not sure but it seems to ObservableCollectionExtensions.ToBindingList<T> method returns implementation of sortable binding list. I haven't found source code to confirm this supposition but object returning from this method works with DataGridView very well especially when sorting columns by clicking on its headers.
The code is very simply and relies only on .net and entity framework classes:
using System.Data.Entity;
IEnumerable<Item> items = MethodCreatingItems();
var observableItems = new System.Collections.ObjectModel.ObservableCollection<Item>(items);
System.ComponentModel.BindingList<Item> source = observableItems.ToBindingList();
MyDataGridView.DataSource = source;
put this line in your windows form (on load or better in a public method like "binddata" ):
//
// bind the data and make the grid sortable
//
this.datagridview1.MakeSortable( myenumerablecollection );
Put this code in a file called DataGridViewExtensions.cs (or similar)
// MakeSortable extension.
// this will make any enumerable collection sortable on a datagrid view.
//
// BEGIN MAKESORTABLE - Mark A. Lloyd
//
// Enables sort on all cols of a DatagridView
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
public static class DataGridViewExtensions
{
public static void MakeSortable<T>(
this DataGridView dataGridView,
IEnumerable<T> dataSource,
SortOrder defaultSort = SortOrder.Ascending,
SortOrder initialSort = SortOrder.None)
{
var sortProviderDictionary = new Dictionary<int, Func<SortOrder, IEnumerable<T>>>();
var previousSortOrderDictionary = new Dictionary<int, SortOrder>();
var itemType = typeof(T);
dataGridView.DataSource = dataSource;
foreach (DataGridViewColumn c in dataGridView.Columns)
{
object Provider(T info) => itemType.GetProperty(c.Name)?.GetValue(info);
sortProviderDictionary[c.Index] = so => so != defaultSort ?
dataSource.OrderByDescending<T, object>(Provider) :
dataSource.OrderBy<T,object>(Provider);
previousSortOrderDictionary[c.Index] = initialSort;
}
async Task DoSort(int index)
{
switch (previousSortOrderDictionary[index])
{
case SortOrder.Ascending:
previousSortOrderDictionary[index] = SortOrder.Descending;
break;
case SortOrder.None:
case SortOrder.Descending:
previousSortOrderDictionary[index] = SortOrder.Ascending;
break;
default:
throw new ArgumentOutOfRangeException();
}
IEnumerable<T> sorted = null;
dataGridView.Cursor = Cursors.WaitCursor;
dataGridView.Enabled = false;
await Task.Run(() => sorted = sortProviderDictionary[index](previousSortOrderDictionary[index]).ToList());
dataGridView.DataSource = sorted;
dataGridView.Enabled = true;
dataGridView.Cursor = Cursors.Default;
}
dataGridView.ColumnHeaderMouseClick+= (object sender, DataGridViewCellMouseEventArgs e) => DoSort(index: e.ColumnIndex);
}
}
KISS : Keep it simple, stupid
Way A:
Implement an own SortableBindingList class when like to use DataBinding and sorting.
Way B:
Use a List<string> sorting works also but does not work with DataBinding.
I suggest using a DataTable.DefaultView as a DataSource. Then the line below.
foreach (DataGridViewColumn column in gridview.Columns)
{
column.SortMode = DataGridViewColumnSortMode.Automatic;
}
After that the gridview itself will manage sorting(Ascending or Descending is supported.)
If you get an error message like
An unhandled exception of type 'System.NullReferenceException'
occurred in System.Windows.Forms.dll
if you work with SortableBindingList, your code probably uses some loops over DataGridView rows and also try to access the empty last row! (BindingSource = null)
If you don't need to allow the user to add new rows directly in the DataGridView this line of code easily solve the issue:
InitializeComponent();
m_dataGridView.AllowUserToAddRows = false; // after components initialized
...
Create a class which contains all properties you need, and populate them in the constructor
class Student
{
int _StudentId;
public int StudentId {get;}
string _Name;
public string Name {get;}
...
public Student(int studentId, string name ...)
{ _StudentId = studentId; _Name = name; ... }
}
Create an IComparer < Student > class, to be able to sort
class StudentSorter : IComparer<Student>
{
public enum SField {StudentId, Name ... }
SField _sField; SortOrder _sortOrder;
public StudentSorder(SField field, SortOrder order)
{ _sField = field; _sortOrder = order;}
public int Compare(Student x, Student y)
{
if (_SortOrder == SortOrder.Descending)
{
Student tmp = x;
x = y;
y = tmp;
}
if (x == null || y == null)
return 0;
int result = 0;
switch (_sField)
{
case SField.StudentId:
result = x.StudentId.CompareTo(y.StudentId);
break;
case SField.Name:
result = x.Name.CompareTo(y.Name);
break;
...
}
return result;
}
}
Within the form containing the datagrid add
ListDictionary sortOrderLD = new ListDictionary(); //if less than 10 columns
private SortOrder SetOrderDirection(string column)
{
if (sortOrderLD.Contains(column))
{
sortOrderLD[column] = (SortOrder)sortOrderLD[column] == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
}
else
{
sortOrderLD.Add(column, SortOrder.Ascending);
}
return (SortOrder)sortOrderLD[column];
}
Within datagridview_ColumnHeaderMouseClick event handler do something like this
private void dgv_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
StudentSorter sorter = null;
string column = dGV.Columns[e.ColumnIndex].DataPropertyName; //Use column name if you set it
if (column == "StudentId")
{
sorter = new StudentSorter(StudentSorter.SField.StudentId, SetOrderDirection(column));
}
else if (column == "Name")
{
sorter = new StudentSorter(StudentSorter.SField.Name, SetOrderDirection(column));
}
...
List<Student> lstFD = datagridview.DataSource as List<Student>;
lstFD.Sort(sorter);
datagridview.DataSource = lstFD;
datagridview.Refresh();
}
Hope this helps
Just in case somebody still looks for it, I did it on VS 2008 C#.
On the Event ColumnHeaderMouseClick, add a databinding for the gridview, and send the order by field like a parameter. You can get the clicked field as follows:
dgView.Columns[e.ColumnIndex].Name
In my case the header's names are similar to view field names.
I have a BindingList<> object bind as a data source to dataGridView.
BindingList x1;
x1 = new BindingList<sourceObject>();
BindingSource bsx1 = new BindingSource();
bsx1.DataSource = x1;
dataGridView1.DataSource = bsx1;
When I clicked the column header, no sorting takes place.
I used the SortableBindingList answer provided by Tom Bushell.
Having included two source files into my project
SortableBindingList.cs
PropertyComparer.cs
Then this change is made to my code:
Be.Timvw.Framework.ComponentModel.SortableBindingList x1; // 1
x1 = new Be.Timvw.Framework.ComponentModel.SortableBindingList<sourceObject>(); // 2
BindingSource bsx1 = new BindingSource();
bsx1.DataSource = x1;
dataGridView1.DataSource = bsx1;
After these changes I performed a build on my program. I am now able to sort by clicking the column headers. Only two lines need changing, they are highlighted in the code snippet above by trailing comments.
In my case, the problem was that I had set my DataSource as an object, which is why it didn't get sorted. After changing from object to a DataTable it workd well without any code complement.
If using a DataTable: dgv.DataSource = (DataTable)table
You can automatically enable Sorting for objects that contain the IComparable Interface. After creating the DataTable, when adding the columns be sure to set the type also to at least object:
table.Columns.Add("ColumnName", typeof(object))
Otherwise, if you do Not specifically give it a type, it converts the object to a string.
I spent a fair amount of time creating a dgv_ColumnHeaderMouseClick() event because it was Not sorting the DataGridView correctly, then to find that all you need to do is specify the type for the column name, and it sorts properly. And the reason it was not sorting correctly previously was because without specifying the type for DataTable columns, it will convert objects to strings.
Just instead of passing a list to the datagrid, you store the search result as a datatable.
dataGridView1.DataSource = students
.Select(s => new {
ID = s.StudentId,
RUDE = s.RUDE,
Nombre = s.Name,
Apellidos = s.LastNameFather + " " + s.LastNameMother,
Nacido = s.DateOfBirth })
.OrderBy(s => s.Apellidos)
.ToDataTable();