Getting DataGrid Row by bound object - c#

I have a List<T> collection which is bound to a DataGrid. T is some class with some public properties, all have PropertyChangedEventHandler defined in order to update the grid properly, everything works just great. The only problem is that I don't know how to get reference to a row, to which my collection entry is bound.
For example:
List<T> collection = new List<T>();
// List has been filled with <T> objects
How can I determine to which DataGrid row (or cell), collection[23] object is bound to? How can I return reference to this row?
Thanks a lot for your help!!!

I used the ItemContainerGenerator.ContainerFromItem(binded_object) method of my DataGrid instance as Rachel suggested and got the actual DataGridRow reference, that was exactly what I needed.

Related

Unable to cast object of type AnonymousType To System.Data.DataRowView in WPF

I am beginner in WPF. I am trying to read Datagrid Selected Items with DataRow/DataRowView. My code is following--
foreach (System.Data.DataRowView row in dgDocument.SelectedItems)
{
txtPhotoIDNo.Text = row["PhotoIdNumber"].ToString();
}
but I am facing the following error--
"Unable to cast object of type
'<>f__AnonymousTypeb11[System.String,System.Byte,System.String,System.String,System.String,System.Byte[],System.Nullable1[System.DateTime],System.String,System.Nullable`1[System.DateTime],System.String,System.String]'
to type 'System.Data.DataRowView'."
When I am trying with following Way, that works fine-
(dgDocument.SelectedCells[2].Column.GetCellContent(item) as TextBlock).Text;
The problem is when I required to add a new column/change the datagrid column position, I required to change index for entire assigned value. To cover this problem, I want to assign value with the above mentioned way with column Name.
The content of SelectedItems list is going to be of the type that is bound to your DataGrid. So for example if I have a property List DataSource in my codebehind and I bind this list to be the ItemsSource of the data grid:
<DataGrid x:Name="grid" ItemsSource={Binding DataSource) />
the grid.SelectedItems will return me a List of strings.
In your case the ItemsSource of the DataGrid is bound to some kind of anonymous type. While I would generally discourage from using anonymous types in this particular case, the workaround for you would be the following:
foreach (dynamic item in dgDocument.SelectedItems)
{
txtPhotoIDNo.Text = Convert.ToString(item.???);
}
You need to substitute ??? with the name of the property from the anonymous class that contains the data you are looking to put into txtPhotoIDNo.
At Last I found my Problem. That is two Values of my SelectedItems was null.For this reason Key value pair does not prepare perfectly. The following code work perfectly.
foreach (System.Data.DataRowView row in dgDocument.SelectedItems)
{
txtPhotoIDNo.Text = row["PhotoIdNumber"].ToString();
}
foreach (dynamic row in dgBarDetails.ItemsSource)
{
myList.Add(row);
}
in my case row is an object of my class used to populate ItemsSource. It can be an int or string or whatever you have in ItemsSource

How to proceed related BindingSources when DataSource is empty

I would like to ask for advice. I meet this model situation:
BindingSource headOfFamillyBS, its DataSources is List of objects List<FamillyResources>
This object FamillyResources have properties, among others, List<FreeTimeActivities>
Than I have two other BindingSources, one freeTimeActivitiesBS, which have DataSource and DisplayMember solved by DataBinding this way:
this.freeTimeActivitiesBS.DataBindings.Add("DataSource", headOfFamillyBS, "view_frTmActvts");
than
this.freeTimeActivitiesBS.DisplayMember = "view_shortName";
The problem is:
When I call on List<FamillyResources> method .Clear(), it has no items, moreover property view_frTmActvts (which return list) is of course empty (because no items in source list - in headOfFamillyBS). Than BindingSource freeTimeActivitiesBS goes throw exception:
DataBinding cannot find a row in the list that is suitable for all
bindings.
Is there any elegant way, how to handle situation, that LIST properties of top BindingSource, which other BindingSource has as theirs DataSource, is null?
Should I manual handle ListChanged event of headOfFamillyBS and check if List is empty, than Clear bindings to all other connected BindigSources? This will be pretty slow in case of houndreds or thousands items in source list.
Thanks for any advices!

how to make an ItemsSource not in use?

I have a DataGrid in WPF (a class that extends DataGrid), and I would like to edit the items in it. But of course I am getting the following error:
Operation is not valid while ItemsSource is in use.
Access and modify elements with ItemsControl.ItemsSource instead.
I have tried changing the itemsSource of the DataGrid, and then adding the items, but I still get the same error. Something like:
public class MyDG:DataGrid{
public void add(){
List<TimesheetRecord> records = new List<TimesheetRecord>();
foreach(TimesheetRecord rec in this.Items){
records.Add(rec);
}
//DO SOME STUFF, ADD MORE ITEMS TO records
ItemCollection col = this.Items;
this.ItemsSource = records;
col.Clear();
foreach(TimesheetRecord rec in records){
col.add(red);//exception thrown here
}
this.ItemsSource = col;
}
}
I don't understand why I am getting that error, when I have already changed the itemsSource to a different list...?
I can't (easily) add the items to the list which is originally bound as the itemsSource, because that list exists in a different class. Would it be best for me to just have a global variable in the MyDG class that is List<TimesheetRecord> myItems = new List<TimesheetRecord>(); and then in the constructor for MyDG go this.ItemsSource = myItems
Or do you have any other suggestions how I should go about doing this? I am open to anything, as this is the first time I have used databinding, so I am probably doing something wrong...
Decalre records collection as:
ObservableCollection<TimesheetRecord> records = new ObservableCollection<TimesheetRecord>();
and keep it data-bound to the DataGrid. Manipulate records collection as needed, data binding will take care of keeping UI in sync with the collection.
You have to choose whether to use Items or ItemsSource, you can't use both interchangably. Attempting to modify Items while using ItemsSource assumes an implicit conversion that isn't supported, hence the error.
In this case, it seems like the best approach might be to just set Items and add to that collection directly. To use ItemsSource, you'd need to, exactly as you wrote, pass a reference to the ItemsSource collection (List<TimesheetRecord>) in to your DataGrid class.
Once you assign "records" to the ItemsSource, you've already updated your collection. There's no need to manually add items to the dataGrid.Items collection.

How to bind DataGridView to SubList

I have a Singleton BindingList list with objects.
I need to bind this list to DataGridView.
I need the grid to show only objects with specific property value (Property IsEmpty and I want to show only objects with IsEmpty=false).
I tried to iterate trough the DataGridView rows after the bind completed and hide rows with IsEmpty=false. This worked but the grid doesn't work well in some situation like sorting of columns.
My conclusion is I need to have a sublist of the original list with refrence to all the objects in the original list that have property IsEmpty=true.
Any idea what is the best way for doing this? (I need to preserve the ListChanged event in the new list for the binding work well)
You'll need to filter your data source before binding to your grid. The best way would like be with a call to linq's Where method.
IEnumerable<yourType> initialSource = getYourSource();
yourGrid.DataSource = initialSource.Where(x => !x.IsEmpty).ToList();
This will keep your original collection intact, and will bind the grid only to those objects that match the filter.
Edit
If you can't use linq, then why not a simple loop?
List<yourType> filteredSource = new List<yourType>();
foreach(var item in initialSource)
if (!item.isEmpty)
filteredSource.Add(item);
yourGrid.DataSource = filteredSource;

How to remove selected items from ListBox when a DataSource is assigned to it in C#?

How to remove selected items from ListBox when a datasource is assigned to it in C#?
When trying to remove, got error
"Items collection cannot be modified when the DataSource property is set."
But when i try to remove item from datasource (datatable) ,
it thorws error as "datarow is not in current row collection".
Find that item in the DataSource object and remove it, then re-bind the ListBox.
EDIT:
Here's how you delete from a DataTable as your DataSource, regardless of the .NET version.
DataRowView rowView = listBox.SelectedItem as DataRowView;
if (null == rowView)
{
return;
}
dt.Rows.Remove(rowView.Row);
I haven't tried with anything other than WinForms DataGridViews, but I highly recommend BindingListView, which is both faster than DataTables/Views and allows you to bind generic List<T>s as your DataSource.
Alternatively, use a list that implements IBindingList or inherits from BindingList. When objects are added or removed from a Binding List, any controls bound to it are automatically notified of the change and will update themselves accordingly. If you are using BindingList and your class also implements INotifyProperty changed, Any changes to class properties will also be updated automatically in the databinding control. For example, if a column in a datagrid(view) is bound to a property, "Name", and you change "Name" in the datasource, the datagrid will automatically update. If you add a new item to the datasource, the datagrid will update automatically. Binding List also supports notification in the other direction. If a user edits the "Name" field ina datagrid, the bound object will be updated automatically. Going off topic slightly, if you go a little further and impliment "SupportsSortingCore" and the associated methods in BindingList, you can add automatic sorting to your data. Clicking on a columnm header will automatically sort the list and display the header sort direction arrow.
If the ListBox has a datasource assigned, you must remove items from the datasource and then rebind the ListBox
You need to modify the data source rather than the Items collection of the control. Depending on what kind of data source you are binding to, there are going to be different things you have to do so that your UI updates.
The best way is find a collection that fits your needs and implements IBindingList or IBindingListView. Those two interfaces implement even handlers that listen for a CollectionChanged event and update your UI accordingly.
If your collection doesn't support those interfaces, you're going to have to re-bind your data source every time somebody adds/removes an item.
when you get the message "Items collection cannot be modified when the DataSource property is set."
setting the datasource to something else, empty list or null does not help when
the code initializecomponent is not completed.
to avoid that error, one must do the change of datasource or the item list during or after form load.
I know it does not seem to make sense. Hoever, the visual studio designer will generate code in the form designer.cs or vb that will add items to the listbox if any code that changes the items is found before end of initialize components
While Chris Doggett posted a valid solution, I ran into problems while using it. By using that method it was not allowing a subsequent GetChanges(DataRowState.Deleted) to work properly.
To better solve my problem, I only had to change a single line - the last line.
DataRowView rowView = listBox.SelectedItem as DataRowView;
if (null == rowView)
{
return;
}
rowView.Row.Delete();
This allowed my GetChanges call to work properly.
This worked for me
DataTable temp = (DataTable)lstBlocks.DataSource;
temp.Rows.RemoveAt(position);
its vary simple , assign a new blank value to listbox
eg..
Dim ABC As New List(Of String)()
ListBox1.DataSource = ABC
ListBox implementation is bugged, you need to create a new data source instance for the component for it to recognize a change.
Eg:
ActivitiesList.DataSource = _activities;
_activities = new List<Activity>(_activities);
_activities.Remove((Activity)ActivitiesList.SelectedItem);
ActivitiesList.DataSource = _activities;

Categories