Editing this entire post to clarify... I cannot seem to nail this:
BackgroundWorker receives data from a WCF service that is a list of objects. The service reference is configured to be ObservableCollection.
I pass the ObservableCollection via a delegate into my main UI thread and set it equal to the UI threads Local Collection.
A listbox is bound to this local collection and does not update. I've added the following to my collection:
public ObservableCollection<EmployeeData> _empData { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<EmployeeData> EmpData
{
get { return _empData ; }
set
{
_empData = value;
OnPropertyChanged("EmpData");
}
}
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(p));
}
This even fires but the PropertyChanged is always null. My XAML listbox has a binding declared as:
ItemsSource="{Binding Path=EmpData}"
No matter what I do EmpData updates but the ListBox does not, I've tried several other methods but nothing ever changes in the listbox, its always just null.
I've been working on this for over a day now, I cannot seem to get this whole automatic updating thing to 'click'.
I'm not sure that I understand exactly what you are doing, but here are a couple of suggestions.
Have a single ObservableCollection
Bind your itemcollection (or listbox, or whatever) to this
Depending on the user, clear and fill that observablecollection with list data
Have the background worker update the list and refresh the observable collection if anything has changed.
Ideally your EmployeeData class will implement the INotifyPropertyChanged interface, so that property changes will get automatically updated in your view.
Related
I'm in a situation where I need to empty a property in my model whenever someone changes a value in a combobox.
A side effect of this is, that whenever I change the value of the Combobox-Bound variable, the Combobox SelectionChanged event is triggered.
Is there anyway to know who is triggering this event. I'd like to know if it is triggered manually or by binding.
I'm looking in to the sender, but they look about the same.
Thank you,
This is a prime example of why WPF developers should use the MVVM design pattern. By relying on SelectionChanged events to control the flow of your code, you're losing your own control over what should happen and are resorting to having to know how an event is being triggered so that you can respond. That's reactive, not pro-active. As a developer, you should always know what can and will affect your code flow.
Rather, set your WPF page or control's DataContext to a viewmodel class that wraps your model and bind your combobox to the ViewModel properties, specifically SelectedItem in this case. This will simplify your coding immensely.
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> Names = new ObservableCollection<string>();
private string _selectedName;
private YourModel _model;
public ViewModel(YourModel model)
{
_model = model;
}
public string SelectedName
{
get { return _model.SelectedName; }
set
{
_model.SelectedName = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<ComboBox ItemsSource="{Binding Names}" SelectedItem="{Binding SelectedName}" />
Dropodownclosed is the event I best use here instead of selectionchanged. This will make sure that the event is triggered not by the data that is bound but by user interface interaction.
I'm playing with ICollectionView right now, and am encountering a problem where I think I understand the "why", but not the "how do I fix it". :)
I have a ComboBox that's databound to an ICollectionView, and it is initially set with the following code:
NameView = CollectionViewSource.GetDefaultView( names); // names is an IEnumerable<string> that comes from a LINQ query
NameView.CurrentChanged += new EventHandler(NameView_CurrentChanged);
Everything works great until I execute a piece of code that generates a new IEnumerable<string> and sets NameView again with the same code as above. Once I do this, CurrentItem is no longer working properly.
I've run into this problem before with ObservableCollection<string> databound to ComboBoxes, and I get around the "unbinding" problem by using Clear() and Add() instead of setting the ObservableCollection<string> property to a new ObservableCollection<string>.
My questions include:
1. If I wanted to be able to just set the property to a new collection, can I re-establish databinding with the new collection somehow? If so, how? If not, can you explain the WPFisms behind why this is fundamentally not possible?
2. What's the best way to deal with changes in an ObservableCollection<string> or ICollectionView? Is my approach of just Clearing and Adding the only way to do it?
When you bind your WPF Controls to ICollectionViews (Happens when the XAML is parsed withing your InitializeComponent-call - You should really define the bindings in XAML!), the Controls subscribe to the required events published by your collection (e.g. CollectionChanged).
Your collection property is just a reference to a memory address. When you bend this to a new collection (i.e. a new address), the DataBinding won't notice. You can't expect the original Collection to publish something like "IAmOuttaHere", and clearly the controls wouldn't listen to a new collection saying "I'm the new guy". But if I see this correctly, your snippet does nothing but add an eventhandler to the CurrentChanged (meaning your observe when some other item in the Combobox is being selected)
Binding is all about notification, so - as long as you don't tell your controls that the collection has been exchanged, they will stick to the initial collection. Please try to implement INotifyPropertyChanged like so:
public class ViewModel : INotifyPropertyChanged
{
private ICollectionView myCollection;
public ICollectionView MyCollection
{
get
{
return this.myCollection;
}
set
{
this.myCollection = value;
this.OnPropertyChanged("MyCollection");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public void ExchangeCollection()
{
this.MyCollection = new CollectionView(....)
}
}
Any bindings should be made to MyCollection. Although, personally, I don't define ICollectionViews myself, since they are not really as nice to work with as for example a nifty IList and they are auto-wrapped around any collection anyway as soon as a binding is defined.
Hope this helps
Sebi
in my Silverlight 4 Application I have an ObservableCollection that I data bind to two different listboxes. The listboxitems showing the content of the MyClass-Object. When I add an item to the ObservableCollection, the new item is displayed in both listboxes properly.
I have set the Binding Mode to two way, so that editing the listboxitems will update the model automatically. This works so far. My problem is, that the content of the other listbox doesn't update with the updated model. Adding a new item will properly show up on the other listbox, but the updates of the content (which I checked happens) won't.
Any ideas how to achieve: The content of the other listbox should update automatically, when the I update the content in one listbox.
Thanks in advance,
Frank
To expand on what luke said your class needs to implement INotifyPropertyChanged and your properties need to throw the PropertyChanged event in their setters.
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged; // This may be named wrong
private string _myString = null;
public string MyString
{
get { return _myString; }
set
{
if(value == _myString)
return;
_myString = value;
var eh = PropertyChanged;
if(eh != null)
eh(this, new PropertyChangedEventArgs("MyString"));
}
}
}
The MyString property will notify the UI that it has changed which will trigger the binding to update.
you need to make sure that your objects in the observable collection implement INotifyPropertyChanged and they should post change events when your content properties change.
I am building a simple roulette app. The player(UI) puts together a list of bets and submits them to the table object to be evaluated and paid out. I've got the code to work and the game process goes smoothly. The problem is that after a turn I can't get the player balance(textblock) or the betlist(listview) to update. Is there some sort of global window refresh command I am missing, or do I have to manually set each of these to update somehow?
WPF can take care of updating these values for you automatically, but you have to let it know when things have changed. Typically, this is done by using DependencyProperties on your model objects, but it can also be done by implementing INotifyPropertyChanged. In either case, when you update a property's value, the PropertyChanged event gets called; WPF automatically subscribes to this event when it binds to a value, and will update the UI when a change occurs. Without this notification, WPF won't check to see if the values in your object have changed, and you won't see the change reflected on the screen.
What about implementing INotifyPropertyChanged, and bind the balance and the betlist to the controls you are using?
Something like:
public class Player : INotifyPropertyChanged
{
private int _balance;
#region Properties
public int Balance
{
get { return this._balance; }
set
{
if (this._balance != value)
{
this._balance = value;
NotifyPropertyChanged("Balance");
}
}
}
public BindingList<Bet> BetList { get; set; }
#endregion // Properties
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public class Bet
{
// some code
}
For the binding list you wouldn't need to implement anything since it implements an interface that notifies changes to whatever is bound to (IRaiseItemChangedEvents). But then again you could be using a different approach.
I have a WPF Toolkit DataGrid bound to an ObservableCollection of Car in my view model. Car has a PropertyChanged event and the setters of each of its two string properties fire the event. I also have the grid's SelectedItem property bound to a property in the view model of type Car, also called SelectedItem.
On the same window as the grid, I have buttons for add, modify and delete. Add and modify open a dialog window with two textboxes, one for each Car property. Delete just shows a confirm dialog then does the delete.
For add and delete, I add or delete an item from the ObservableCollection and the grid updates itself as expected. However, for modify it does not. At first, my Car did not use PropertyChanged and after some searching I found that it needed to for the grid to update when an individual item's properties changed. But now that I am using PropertyChanged, the grid still doesn't update.
I've tried changing the values of the SelectedItem in my view model as well as directly changing the item on the collection.
What am I doing wrong?
Make sure you're implementing INotifyPropertyChanged and not just raising a PropertyChanged event. Also, when raising PropertyChanged, you must pass "this" as the sender, otherwise WPF will ignore the event.
Below is a simple base class that implements INotifyPropertyChanged.
public class Person : INotifyPropertyChanged {
private string name;
public string Name {
get { return name; }
set {
if (name != value) {
name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Sounds like the classic problem with ObservableCollection. ObservableCollection only notifies of additions, deletions, etc. on it's self. It will NOT notify of changes to properties of whatever you have stored in it. This is why your add/delete operations work as expected.
What you should do is use a CollectionView and bind to that:
ObservableCollection<MyObject> myCollection = new ObservableCollection<MyObject>();
ICollectionView view = CollectionViewSource.GetDefaultView(myCollection);
using this method also has the benifit that grouping and sorting are built into the view.