How to refresh a WPF DataGrid? - wpf

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.

Related

Does (ReactiveUI 6.2) ReactiveList Support Binding in Winforms DataGridView Datasource?

I am re-factoring an old winforms project into using MVVM via ReactiveUI. The binding part works great so far except the Grid.Datasource = ReactiveList doesn't seem to give any update on changing/adding/deleting.
I just wanted to confirm here, since the DataGridView.Datasource support databinding list only, ReactiveUI.ReactiveList will not work here? or there could be some possible get arounds.
I've just been struggling with this, the approach I've come up with is to wrap a ReactiveList on the ViewModel with a ReactiveDerivedBindingList.
I've created a BindingSource based on the item ViewModel class for design time support, then I replace this at runtime:
private void CreateDerivedBindings()
{
this.Articles = this.ViewModel.Articles.CreateDerivedBindingList(x => x);
this.ViewModel.Articles.ItemChanged.Subscribe(_ => this.Articles.Reset());
//this.Articles.ChangeTrackingEnabled = true;
this.articlesDataGridView.DataSource = this.Articles;
}
private IReactiveDerivedBindingList<ArticleViewModel> Articles { get; set; }
Subscribing to ItemChanged of the underlying ReactiveList (which has ChangeTrackingEnabled turned on) enables items in the grid to update as they are changed. Setting ChangeTrackingEnabled on the derived binding list didn't do anything. This is a brute force approach, I'd guess causing a full refresh of the grid, maybe there is a more finessed approach.
A simpler approach could be to use a ReactiveBindingList on the ViewModel, but I've not tried this as the class is winforms specific and I'm aiming for a ViewModel which could be reused with WPF.
ReactiveList supports INotifyCollectionChanged, I don't know of any other way that lists can signal they have changed. I could be misinformed about Winforms Grid though!

Generic WinForms ListView (with regards to Tag)

I'm trying to improve on a Winforms project where datatable rows are stored in the Tag property of ListViewItems. When the datatable is refactored to List<T> (or actually classes containing lists) it would help immensely if I could make the Tag property generic by using a subclass of ListView.
In the best of worlds, I'd want the Tag property to be replaced by a public T Tag{get; set;} that wraps base.Tag and casts it.
Second best would be Obsoleting Tag and providing a new property like TypedTag working like above.
I think this would involve subclassing or composite aggregation of at least ListView, ListViewItemCollection, SelectedListViewItemCollection and ListViewItem, and I'm not sure how to do it.
In short:
ListView<Employee> lvwEmployees;
should result in this being possible:
Employee selected = lvwEmployees.SelectedItems[0].TypedTag;
And give a compilation error for this:
DataRow selected = lvwEmployees.SelectedItems[0].TypedTag;
Is it possible? Is it already done?
Project is dotnet 2.0 but I think I'll try to have it upgraded if it helps this matter.
EDIT: It turns out that the owner constructor argument is all a certain collection needs to hook up to the inner collection. Hence the following works:
ListView a = new ListView();
a.Items.Add("Hello");
Assert.AreEqual(1, new ListView.ListViewItemCollection(a).Count);
This makes it rather easy to create a generic tagged ListView. I'll post a complete solution later. :)
EDIT2: Here's the solution:
http://thecarlr.blogspot.com/2010/11/generic-listview.html
EDIT3: For designer support, just add a non generic subclass and use that.
Example: If you intended to use ListView<Employee> in the form, create a ListViewEmployee : ListView<Employee> in another file, and use ListViewEmployee in the form.
The easiest way to add one of theese listviews would be to add a normal listview to the form, and then change it's type in the source files. (And if you don't know where it's declared or instantiated, find out or use the normal listview instead.)
You made the wrong class generic. SelectedItems[0] is a ListViewItem, not a ListView.
There isn't anything you can do to change the type of the Items and SelectedItems properties. You can certainly derive your own class from ListViewItem and just add the property you want to store. No need for another Tag property. You'll have no trouble adding them but you'll need to cast back to your derived class when you retrieve them back from the Selected/Items collection.
In general, avoid this kind of code by using the ListView only as a view of your model. The ListViewItem.Index should then always be good to get a typesafe reference back from your model.
Here's the solution:
http://thecarlr.blogspot.com/2010/11/generic-listview.html
Enjoy :)
/Carl
VS Designer simply cannot handle abstract or generic controls (not for want of asking).
One way around that limitation is to write a type safe wrapper around a standard ListView.
Something like this:
public class TypedListView<T> where T : class
{
public TypedObjectListView(ListView lv) {
this.lv = lv;
}
private ListView lv;
public virtual T SelectedObject {
get { return (T)this.lv.SelectedItems[0].Tag; }
}
// Lots more methods/properties
}
You create a normal ListView in Designer, and then when you wanted to access it, you create and use your adapter instead. Like this:
var typedListView = new TypedListView<Employee>(this.listView1);
Employee selectedEmployee = typedListView.SelectedObject;
You would need to provide a typed version of every ListView properties or method you wanted to use.
The ObjectListView project takes this approach to create a TypedObjectListView which does exactly what are you asking for.

WPF bindings not refreshing

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).

DataContext, DataBinding and Element Binding in Silverlight

I'm having one hell of a time trying to get my databinding to work correctly. I have reason to believe that what I'm trying to accomplish can't be done, but we'll see what answers I get.
I've got a UserControl. This UserControl contains nothing more than a button. Now within the code behind, I've got a property name IsBookmarked. When IsBookmarked is set, code is run that animates the look of the button. The idea is that you click the button and it visually changes. We'll call this UserControl a Bookmark control.
Now I have another control, which we'll call the FormControl. My FormControl contains a child Bookmark control. I've tried to do databinding on my Bookmark control, but it's not working. Here's some code to help you out.
This is the XAML and Loaded event handler of my control. As you can see it contains a child element that is a custom control (bookmark). So once this control loads, it's DataContext is set to an new instance of an Employee object. Silverlight also sets the DataContext property of my child bookmark control to the same instance. I've verified this by debugging. If my parent has a valid DataContext set then why can't my child control (bookmark) property databind to it?
<UserControl ......>
<q:Bookmark x:Name="BookMarkControl1" IsBookmarked="{Binding IsSiteBookmarked}" />
</UserControl>
public void Control_Loaded(object sender, EventArgs e)
{
DataContext = new Employee { IsSiteBookmarked = True };
}
This is my custom control below. Obviously it contains more than this, but for readability I've trimmed it down to the property I'm trying to databind to.
//this is the bookmark control. I've included this control within another control, and I'm trying to databind to properties within my parents DataContext
public partial class Bookmark : UserControl
{
bool _IsBookmarked= false;
public bool IsBookmarked
{
get {return _IsBookmarked;}
set {
_IsBookmarked= value;
SwitchMode(value);
}
}
}
UPDATE
Got some javascript errors that I should mention. Firebug reports a AG_E_PARSER_BAD_PROPERTY_VALUE exception. It doesn't seem like my databinding is even working yet.
Make your IsBookmarked property on the Bookmark control a dependency property.
I presume Control_Loaded is a part of your FormControl, in which case I'm not sure you are using DataContext properly. Best double check that.
UPDATE: Yes, you are using the DataContext properly. AG_E_PARSER_BAD_PROPERTY_VALUE indicates you need to make the IsBookmarked property a dependency property, like so:
Public Property IsBookmarked() As Boolean
Get
Return Me.GetValue(IsBookmarkedProperty)
End Get
Set(ByVal value As Boolean)
Me.SetValue(IsBookmarkedProperty, value)
End Set
End Property
Public Shared ReadOnly IsBookmarkedProperty As DependencyProperty = DependencyProperty.Register("IsBookmarked", GetType(Boolean), GetType(Bookmark), New PropertyMetadata(New PropertyChangedCallback(AddressOf OnIsBookmarkedPropertyChanged)))
Private Shared Sub OnIsBookmarkedPropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim cntrl As Bookmark = TryCast(d, Bookmark)
cntrl.SetIsBookmarked(e.NewValue)
End Sub
If you only need to store the value for later use, then you don't need to do anything in the OnIsBookmarkedPropertyChanged procedure, But I put some code there as an example anyway.
Good Luck!
I don't recall the exact order in which databinding is evaluated (and I'm too lazy to go look it up), but as I recall, it initially happens BEFORE the form's Loaded event fires, and without making the IsBookmarked property a dependency property, or at least using INotifyPropertyChanged, it may have trouble establishing the datacontext appropriately. I'd recommend either implementing INotifyPropertyChanged or making IsBookmarked a dependency property. DataBinding is tough enough to get right in the best of circumstances (see my long, bad-tempered rant about it here), and you'll just be making it more difficult on yourself if you aren't setting up your properties in the way that it expects.
The control exposes a IsSiteBookmarked property(which I believe should be a DependencyProperty) but the control is binding to a IsBookmarked which is not shown. Is this intentional? Have you checked your Visual Studio output window for binding errors?
Addition 1:
Since you have fixed the typo in your question and added that there is an error being reported.
Start by clearing up the AG_E_PARSER_BAD_PROPERTY_VALUE problem. Is there a line number and start position in the error message? Start looking there. One strategy is to start taking out XAML until there is no longer an error. This will narrow down the offending code.
Running in debug, mode check for binding errors in the output window.
You might want to also post the Employee class code, especially the IsSiteBookmarked property.
Typically when doing databinding to an object you will want to leverage the INotifyPropertyChanged interface and implement that so that the control can properly invalidate it's property value. Unless you use INotifyPropertyChanged with Mode=TwoWay then any code that changes your DataContext's IsSiteBookmarked will have no effect.

WPF Collections and Databinding

I am new to WPF and trying to wrap my head around WPF's framework, what it does and does not do for you.
To clarify this, I would like to know what is the difference between this:
public List<MyCustomObject> MyCustomObjects
{
get { return (List<MyCustomObject>)GetValue(MyCustomObjectsProperty); }
set { SetValue(MyCustomObjectsProperty, value); }
}
public static readonly DependencyProperty MyCustomObjectsProperty =
DependencyProperty.Register("MyCustomObjects", typeof(List<MyCustomObject>),
typeof(Main), new UIPropertyMetadata(new List<MyCustomObject>()));
and this:
public ObservableCollection<MyCustomObject> MyCustomObjects { get; set; }
public Main ()
{
MyCustomObjects = new ObservableCollection<<MyCustomObject>();
}
Ok, we must put some order into things, there's a few concepts mixed in together here.
First of all, you're asking what the difference is between a field-backed property and a dependency property. Google would be your best friend, however I recommend this blog post by WPF's vanguard Josh Smith: Overview of dependency properties in WPF
In short: dependency properties support the richness that is WPF: Styling, animation, binding, metadata, and more.
Secondly, you're asking what the difference is between a List and an ObservableCollection. Well the latter provides change notifications (in the forms of events) on any change to the collection (addition, removal, change of order, clearing, etc.), and the former does not. You can read more about that here: The ObservableCollection Class
In short: ObservableCollection provides change notifications which are required for the UI to automatically reflect changes in the view model.
In addition to Aviad and Reed's answers, I would like to point out a serious bug in your first code sample :
public static readonly DependencyProperty MyCustomObjectsProperty =
DependencyProperty.Register("MyCustomObjects", typeof(List<MyCustomObject>),
typeof(Main), new UIPropertyMetadata(new List<MyCustomObject>()));
The new List<MyCustomObject>() used as the default value will be created only once, so by default all instances of your type will share the same List<MyCustomObject> instance, which is probably not what you want... The only sensible default value here is null
In the first case, you're setting up a Dependency Property containing a List<T> instance.
In the second, you're making a normal CLR property, but having it setup as an ObservableCollection<T>.
For WPF Data Binding, there are some differences here.
Typically, you want all of your properties in the DataContext (which is the object that, by default, things "bind" to) to either implement INotifyPropertyChanged or to be a Dependency Property. This lets the binding framework know when changes are made to that object. Normally, though, you'd only use a Dependency Property if your working with a custom control - it's usually a better idea to have your object to which your data bound be a separate class, assigned to the DataContext. (For details here, see Josh Smith on MVVM or my recent detailed post on MVVM...)
However, with a collection, you typically also want the binding system to know when the items within the collection change (ie: an item is added). ObservableCollection<T> handles this by implementing INotifyCollectionChanged.
By using the second approach (using an ObservableCollection<T>), your UI can tell when items were added or removed from the collection - not just when a new collection is assigned. This lets things work automatically, like a ListBox adding elements when a new item is added to your collection.
1:
You're using a dependency property to "tell" the framework when that property is changed. This will have the following consequences for your binding:
MyCustomObjects.Add(new MyCustomObject()); //Wont update the view through databinding
MyCustomObjects = new List<MyCustomObject>(); //Will update the view through databinding
You could gain the same databinding functionality by implementing INotifyPropertyChanged on which ever class exposes the property, but dependency properties a capable of much more than just notifying about changes. These are rather advanced features though, which you aren't likely to come across in your average joe app :)
2:
You're using an observable collection, which implements INotifyCollectionChanged for you, to tell the databinding whenever the content of the collection has changed. This will have the opposite consequences than #1:
MyCustomObjects.Add(new MyCustomObject()); //Will update the view through databinding
MyCustomObjects = new ObservableCollection<MyCustomObject>(); //Won't update the view through databinding

Resources