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).
Related
I have a datagrid in my View. The datagrid's itemssource property is bound as such
ItemsSource="{Binding}"
Addtionally in the codebehind I have set datacontext by doing the following:
DataContext = ProcedureDatabaseViewModel.Procedures();
The Procedures function in the viewmodel successfully outputs a list which the DataGrid successfully displays.
The issue now is that the datacontext of the entire page is now set to the above. The result of this is that other elements that interect with the VM no longer work. I.E. buttons with commands that are found in the VM. I have tried removing the setting of the datacontext in the code behind but cannot figure out how to populate the datagrid otherwise. Please note that when the DataContext is not set in the code behind the context is changed to the VM, I believe, and thus, the other elements begin working again. I have tried changing the Itemssource property to target the list of objects that I wish to populate the datagrid with but it hasnt worked.
The list is
List<procedure> Procedures
and it is in the ProdureDatabaseViewModel. I tried to target it as
ItemsSource="{Binding ProdureDatabaseViewModel.Procedures}"
But this has not worked either.
Can someone please advise me on the correct way to do this?
The cleanest way is to use ItemsSource to bind your Procedures collection to your DataGrid. For this to work, Procedures has to be a Property. To avoid further issues, use an ObservableCollection. It should look like this:
ObservableCollection<procedure> Procedures { get; set; }
Then you should be able to simply bind it via
ItemsSource="{Binding Procedures}"
I prefer to set the DataContext in the constructor of the view to the complete ViewModel (which I created for this special view).
So I do something like this in the constructor of the view:
public View(ProcedureDatabaseViewModel viewModel)
{
this.DataContext = viewModel;
}
This way everything else should still work and you can use more than just the procedures.
Next you bind the procedures to your datagrid:
ItemsSource="{Binding ProcedureList}"
Do note that for this to work, "Procedures" needs to be a property. It's not clear in your question if it is a function, a property or just a simple class member. If it is a function, you can do it like this in your view model:
public List<procedure> ProcedureList
{
get { return this.Procedures(); }
}
I have tried removing the setting of the datacontext in the code
behind but cannot figure out how to populate the datagrid otherwise.
You can set the DataContext of your DataGrid separately like so, MyDataGrid.DataContext = ProcedureDatabaseViewModel.Procedures();.
And apply separate DataContext for your Page.
In a DataGrid view, the blank row add entries is convenient but quickly get's lost when the list is large. Can you change it's default location from the bottom to the top of the DataGrid view?
I've rarely used a DataGrid and know nothing about its ability to add rows, but coming from a WPF/MVVM point of view, you don't need that anyway. Generally in WPF/MVVM, we manipulate data rather than UI controls, so the solution is easy. First we Bind our collection of data (in whatever shape we chose) to the DataGrid.ItemsSource property:
public ObservableCollection<DataItem> SomeDataCollection { get; set; }
...
<DataGrid ItemsSource="{Binding SomeDataCollection}" ... />
Now, if we want to add a new item to the bottom of the DataGrid we can do this in the code behind/view model:
SomeDataCollection.Add(new DataItem());
Then if we want to add a new item to the start of the collection, we can just do this:
SomeDataCollection.Insert(0, new DataItem());
Of course, you'll need to implement the INotifyPropertyChanged interface in your code behind/view model to make this work, but (hopefully) you'll be doing that anyway.
UPDATE >>>
Sorry, I misunderstood you. I found a NewItemPlaceholderPosition Enumeration on MSDN that is used by an ItemCollection.IEditableCollectionView.NewItemPlaceholderPosition Property. Unfortunately, those linked pages don't have an code examples, so I found one in the in wpf datagrid how to get the blank row on top? post here on StackOverflow. From the answer by #woodyiii in that post:
var view = CollectionView.GetDefaultCollectionView(EmployeeList)
as IEditableCollectionView;
if(view!=null)
view.NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtBeginning;
This does mean that you'd have to use a CollectionView to get this to work, but it seems like the only way... the NewItemPlaceholderPosition Enumeration isn't used by anything apart from the various CollectionView classes.
This worked for me:
IEditableCollectionView cv = (IEditableCollectionView)grdSchedule.Items;
cv.NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtBeginning;
Where grdSchedule is the the name of your DataGrid
I have an ItemsControl whose ItemsSource I assign (via code) an ObservableCollection (lets call it Items) of INotifyPropertyChanged objects (data model). This data model has a PointCollection property.
The view (XAML) binds to this PointCollection on a PolyLine (on the Points attribute).
Initially when i set this Items collection to the ItemsControl.ItemsSource, i can see that the lines are indeed rendered.
Issue:
When I set the ItemsControl.ItemsSource to something else (like another ObservableCollection which doesn't have any lines) THEN set it back to the original collection, I am unable to see the lines, even though the collection SHOULD render them because the collection data model's contain the PointCollection.
From what I was able to research, there is something particularly tricky about binding to a PointCollection. I was wondering if anybody has tackled this before and/or know of a way to get this to render (i.e. invalidate the control to somehow force a redraw)???
Thanks.
Alvin,
I have no idea if this will work but, have you tried creating a new PointCollection?:
PointCollection newCollection = new PointCollection( oldCollection );
myItemsControl.ItemsSource = newCollection;
If that doesn't work, maybe it may be necessary use a more WPF based syntax:
myItemsControl.SetValue( ItemsControl.PointsProperty, newCollection );
I am struggling with some PointCollection issues myself so if either of these options help, let me know.
I have a WPF sorting/binding issue. (Disclaimer: I am very new to WPF and databinding so apologise if I am asking a really dumb question :-))
Firstly, I have a linqToSql entity class Contact with an EntitySet<Booking> property Bookings on it.
If I directly bind this Bookings property to a ListView, the application seems to correctly notify of changes to the selected item in the ListView, such that a textbox with {Binding Path=Bookings/Comments} updates correctly.
// This code works, but Bookings is unsorted
var binding = new Binding();
binding.Source = contact.Bookings;
bookings.SetBinding(ItemsControl.ItemsSourceProperty, binding);
However, as I can't seem to be able to find a way to sort an EntitySet (see this post), I am trying to bind instead to an Observable collection, e.g:
// This code doesn't notify of selected item changes in the ListView
var binding = new Binding();
binding.Source = new ObservableCollection<Booking>(contact.Bookings.OrderByDescending(b => b.TravelDate).ToList());
bookings.SetBinding(ItemsControl.ItemsSourceProperty, binding);
But this doesn't seem to notify the comments textbox correctly such that it updates.
If anyone has a solution for either sorting the data before or after its bound, or another solution that will work that would be much appreciated.
You should bind to a CollectionView rather than the collection itself. That will allow you to specify whatever sorting criteria you require. Example:
var collectionView = new ListCollectionView(contact.Bookings);
collectionView.SortDescriptions.Add(new SortDescription("TravelDate", ListSortDirection.Ascending));
var binding = new Binding();
binding.Source = collectionView;
bookings.SetBinding(ItemsControl.ItemsSourceProperty, binding);
Hainesy,
Does the Booking object implement INotifyPropertyChanged to notify change in Comments property?
If not, you cannot expect TextBox which is bound to Comments property to be updated automatically when Comments change
Using ObservableCollection in this case will only get you the benefit of updating the view with changes when Booking objects are added or deleted from the collection
-Rajesh
I have a WPF DataGrid with some data. You can add rows through a separate window. The DataContext is the same, a LINQ-to-SQL object. Binding is also the same, I bind the ItemsSource property to a table.
In the other window, when the user clicks on Save, I create a row programmatically and add it using InsertOnSubmit. After that I use the DataContext's SubmitChanges method.
My problem is that the DataGrid isn't updated. If I restart the application I can see the new row, so it's in the database, but I couldn't find a way to refresh the DataGrid.
So far I've tried to use UpdateTarget on the BindingExpression of the DataGrid, but it didn't help. I've also tried dataGrid.Items.Refresh() — same result. How can I fix this?
try datagrid.Items.Refresh() from here http://programmer.wrighton.org/2009/01/wpf-datagrid-items-refresh.html
The reason it's not updating is that LINQ-to-SQL doesn't implement INotifyCollectionChanged, so WPF has no way to tell that the ItemsSource has been updated. The least terrifying way to fix this, is to copy your LINQ-to-SQL results to an ObservableCollection - when you do the Insert, also add to the observable collection. Then you'll see the update.
I ran into same problem and found that best place for ObservableCollection is DataContext. It has some partial methods generated by designer that can be used to update collection. This code works pretty well:
partial class DataClassesDataContext
{
private ObservableCollection<Task> taskCollection;
public ReadOnlyObservableCollection<Task> TaskView { get; private set; }
partial void OnCreated()
{
taskCollection = new ObservableCollection<Task>(Tasks);
TaskView = new ReadOnlyObservableCollection<Task>(taskCollection);
}
partial void InsertTask(Task instance)
{
taskCollection.Add(instance);
this.ExecuteDynamicInsert(instance);
}
partial void DeleteTask(Task instance)
{
taskCollection.Remove(instance);
this.ExecuteDynamicDelete(instance);
}
}
The problem is that you need to refresh your LINQ-to-SQL DataContext. The DataContext's won't properly recognize the new row even after a submit changes. You need to dispose the DataContext you have and create a new one. In most cases DataContext should be used for one short operation and not as a long standing object.
If you have a case when you have to reload a grid in another window , you can simply close that window and invoke it again.
Or just invoke the search code again (usually the search button)> I have solved it in my case like this.
For some reason Items.Refresh() is not working for me.
What did work was to make my underlying collection inherit ObservableCollection and then call its Add method.
((ContactUIObjects)dgrdContacts.ItemsSource).Add(new ContactUIObject(o));
ContactUIObjects is just the grids underlying collection.