Below is the code behind in my view model:
private DataRow selectedErrorRow;
public DataRow SelectedErrorRow
{
get { return selectedErrorRow; }
set { selectedErrorRow = value; base.RaisePropertyChanged("SelectedErrorRow"); }
}
Then this in my view:
<DataGrid SelectedItem="{Binding SelectedErrorRow,Mode=TwoWay}"
The binding somewhat works... It "gets" the value when the datagrid is drawn but it never sets it when a new value is selected/highlighted.... Any ideas?
Note the item source for the datagrid is a DataTable.
EDIT:
The Datagrid is in a PopUp, when the datagrid gets drawn it will get the binded value (null). However once I selected/highlight a row it will NOT 'set' anything. It will however 'set' the binded value null when its redrawn (the popup is open, i selected a row, close it,and reopen it). The thing is It never sets the value to anything but null, and it only sets it during the secound time its drawn.
I needed to change what I was binding to. It needs to bind to a DataRowView and not a DataRow.
I had this issue previously and figured it out myself.
It is not intuitive to know what is the issue since you cannot see the real value in debug but when you data bind a DataTable on a datagrid at first you think it has a DataTable type as source right ? Well you are wrong if you think that.
Actually the datagrid or the binding (i still don't know where it happen as i only see DataTable on my side) does a cast/convert of DataTable object like DataTable.AsDataView() to convert it to a DataView by itself so the selected item make sense to me to be DataRowView.
I know it's old question but this might shed light to those who get here and also look to set value as data source especially converters
Try this:
DataGrid SelectedItem="{Binding Path=SelectedErrorRow,Mode=TwoWay}
This should solve your problem.
Related
TYFYT. I need some help understanding what's going on here and how binding to a datagrid works. I am coming from a knowledge base of working with WinForms so maybe my approach is all wrong?...
I have a WPF window, with a Datagrid.
In the ViewModel I have DataTable that I have populated with my Booking objects. The cols across the top correspond to a TimeSlot, and the row headers correspond to an instructor.
void DisplayBookings()
{
foreach (Booking booking in bookings)
{ //Add each Booking object to the DataTable
DtBookings.Rows[booking.GridRow][booking.SlotTimeId + 1] = booking;
}
}
In the window xaml I have the datagrid bound to the DtBookings.
<DataGrid x:Name="grdInstructors"
ItemsSource="{Binding DtBookings}" DisplayMemberPath="Id"
SelectedItem="{Binding SelectedCell}"
When I run this, it's working fine. I see the bookings in the correct cells in the grid.
Trying to do work against the grid is what is raising questions.
Q1. What is the grid cell holding? I have put the Booking objects in the ViewModel.DataTable and in the grid cell I see "myNamespace.Models.Booking". This is fine but when I try to access it I get the error "'Unable to cast object of type 'System.String' to type 'MyObject' (Booking)
So is the datagrid only holding a String that is the name of the object? I could accept that answer since I'm thinking the View should only be a visual representation of the objects.
If that is so, then how should SelectedItem=ViewModel.MySelectedItem work? (In this case it doesn't). Initially I thought it should have a copy of the object so that when I click on the cell it will tell the ViewModel what object I have chosen. This normally works when I am bound to an ObservableCollection, but in this case it is bound to a DataTable so I'm not getting the same result.
And on a final note I have noticed that DisplayMemberPath="Id" doesn't work in this case. (It's not really an issue since I usually override `ToString())
It would be great to have a definitive answer on this before I add logic to work around the problem and defeat the point of MVVM and Binding.
TYVM.
To get the currently displayed value from a WPF combobox, I am getting GetSelectedItem (which gives me a dataRowView since my itemSource is a DataView) and then getting the appropriate column.
I was hoping there could be straightforward way to get the Display Value like how we have the SelectedValue property.
Is anyone aware of a better approach?
You use the ADO.Net class DataTable, right?
You can set a displayed value quite straightforward:
<ComboBox x:Name="myComboBox" ItemsSource="{Binding}" DisplayMemberPath="SomeColumn"
SelectedValuePath="SomeColumn"/>
In this example the combobox displays the value of the column SomeColumn. Put a correct column name instead of this dummy one.
And in code-behind:
myComboBox.DataContext = myDataSet.Customers; //any table
var selectedValue = myComboBox.SelectedValue; //The displayed value (SomeColumn)
var fullRow = myComboBox.SelectedITem; //dataRowView, I think
I am using WPFToolKit DataGrid in my application. I have bound the DataGrid to a XMlDocument.
The grid displays the Data from XML. I have to remove all the bindings in DataGrid and reset it during some event.
Now my question is how do I remove the DataBinding between DataGrid and XMLDocument.
I have tried something like this ::
dg.SetValue(DataGrid.BindingGroupProperty, null); //doesn't work
What am I doing wrong?
To undo a binding in WPF, simply set the property that was previously bound to a some other value. In the case of DataGrid, its data is usually bound to the ItemsSource property so setting that to null will remove its previous binding. But if you have any other properties in the DataGrid that are bound, you will have to set those to "unbound" values as well. Which ones will depend on your situation. But in your example the code would be:
dg.ItemsSource = null;
The following line solved my problem ::
BindingOperations.ClearAllBindings(dg);
Try changing the dataGrid.DataContext to null or an empty string.
The above didn't work for me. What works for me if binding:
dataGrid.ItemsSource = null;
dataGrid.Columns.Clear();
dataGrid.Items.Clear();
dataGrid.Items.Refresh();
If not binding:
dataGrid.Columns.Clear();
dataGrid.Items.Clear();
dataGrid.Items.Refresh();
I've run into an issue I was surprised I couldn't find any discussion about (except WPF MVVM ComboBox SelectedItem or SelectedValue not working maybe).
I have a MVVM form that has 2 ctors, one is for "new item creation", the other is for "item modification". I have a combobox that represents one of the item's properties.
In the modification ctor, the property bound to ItemsSource is initialized, and then the property bound to SelectedItem is set. But nothing is selected in the UI, unless I delay (even a tiny bit) the SelectedItem set.
How can I solve this ? I decently can't keep a timer with a totally random interval to fix the issue :D
Thank you for your help
It seems like the elegant way to ensure the ItemsSource is initialized before I set the SelectedItem from VM is to have the binding source of ItemsSource (whatever it is) declared in my view resources.
I'm sure someone can lead me to the light now that I've pointed that out.
I have tried with a CollectionViewSource but didn't find the way to use its Filter capability without breaking the MVVM pattern. Plus I don't know how to re-raise the Filter as I used to with ICollectionView.Filter (filtered-out items depend on another combobox selection, nothing really fanciful imo).
Perhaps the resource declared in the view and used as ItemsSource has not necessarily to be a CVS, I'm looking for suggestions here.
--Edit--
I found out that the IsSynchronizedWithCurrentItem="True" solution spread all over the web is actually working. I was mislead because it didn't fix my problem at first try due to a remaining SelectedValuePath that wasn't used anymore on my control.
public MyViewModel()
{
this.Items = ...;
//this.SelectedItem = ...;
// select in separate message so that the ItemsSource has definitely been set
this.Dispatcher.BeginInvoke(delegate
{
this.SelectedItem = ...;
});
}
I have a listbox on my form that looks like this:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged"/>
I am binding the following collection to it:
ObservableCollection<Hand> oHands = new ObservableCollection<Hand>();
using the following code:
lbResults.DataContext = oHands;
Binding binding = new Binding();
lbResults.SetBinding(ListBox.ItemsSourceProperty, binding);
The oHands collection gets populated via a background worker that announces via an event whenever a new Hand object is available. The ListBox refreshes perfectly when something is added. The ToString() result of the Hand object is displayed and that is what I want - so far so good. However, when the background worker finishes
void finder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
oHands = new ObservableCollection<Hand>(oHands.OrderBy(o => o.PotSize));
lbResults.SetBinding(ListBox.ItemsSourceProperty, new Binding());
}
The items in the list are still showing up in the original order. I can confirm that the list is re-ordered but the items are still showing up in the original order. How do I refresh this binding?
you dont want to assign oHands a new collection. just Clear() the collection then add the results from the operation. don't update the binding
instead of replacing the entire observable collection, you could just clear it and add all your new items. that wouldn't affect your binding.
You could also use a CollectionViewSource as your binding, and set the order on that instead of reordering the whole collection.
Wouldn't it be a lot easier to just set the itemsource directly?
lbResults.ItemsSource = oHands;
You're really just supposed to inherit from the INotifyPropertyChanged interface, but heres another way to force an update to a binding:
BindingExpression exp = BindingOperations.GetBindingExpression(lbResults, Listbox.ItemsSourceProperty)
exp.UpdateTarget()
Edit: I also just noticed you aren't setting any binding in the XAML and appear to be doing it programmatically with an empty Binding. I haven't tried that way before, so see if changing your XAML to this might help:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged" ItemsSource="{Binding Path=oHands}"/>
Then you set lbResults.DataContext to point to the class that has the member oHands. This is what worked for me in my project (in IronPython, so forgive me if my examples didn't convert to C# perfectly).