I have a TextBlock
in my WPF/MVVM (with MVVM Light framework) game that is bound to a property that is supposed to reflect the number of employed workers. I have confirmed that the binding is intact, but I can't get it to update.
Here's the TextBlock
in my view:
<TextBlock x:Name="WorkersTextBlock"
FontFamily="Pericles"
DataContext="{Binding Guilds[0]}"
Text="{Binding Workers.Count,
StringFormat=Workers : {0},
FallbackValue=Workers : 99}" />
The property in my viewmodel:
public ObservableCollection<Guild> Guilds
{
get { return DataManager.Data.Guilds; }
}
Also in my viewmodel, the command to change a Worker
's Employer property:
private void ExecuteHireWorkerCommand()
{
if (SelectedWorker == null)
return;
SelectedWorker.Employer = DataManager.Data.Guilds[0];
Gold -= SelectedWorker.Salary;
_workerCollectionView.Refresh();
}
In DataManager, which is a singleton class that holds all of my data:
private ObservableCollection<Guild> _guilds = new ObservableCollection<Guild>();
public ObservableCollection<Guild> Guilds
{
get { return _guilds; }
}
private ObservableCollection<Worker> _workers = new ObservableCollection<Worker>();
public ObservableCollection<Worker> Workers
{
get { return _workers; }
}
In the Guild
model:
public ObservableCollection<Worker> Workers
{
get { return DataManager.Data.Workers.Where(w => w.Employer == this).ToObservableCollection(); }
}
The Employer property in Worker
is:
public Guild Employer { get; set; }
And last, my extension method (which I believe is the source of the problem):
public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
return new ObservableCollection<T>(source);
}
Messageboxes confirm that via the command, Worker
s' Employer properties are being updated properly but nothing I've tried makes the TextBlock
update. I've tried implementing RaisePropertyChanged
on everything I've listed here with no luck.
If I set the Employment property of a Worker
to the correct guild within the constructor when the data is initialized, the number in the TextBlock
displays correctly, but doesn't update after that. My hunch is that the LINQ filtering and extension methods in the Workers
property are causing this trouble, but I could be wrong.
If anybody has any ideas on how to get this to work, I'd love to hear them. Any advice at all on this matter would be greatly appreciated. If you need more code or information, please ask.
Thanks.
Update: I think that Ron is on the right path; the extension method may be breaking the binding. If this is the case, can anybody give me any advice on how to filter the Workers property in Guild
without breaking the binding? Also, as far as the setter issue goes, I added a setter to the Workers property but it never actually fires.
I think you need to redesign the underlying data structure a bit. But be that as it may, you could change it slightly to make it work.
Change the Workers
property to ICollectionView
like so:
public ICollectionView Workers { get; set; }
Then in the Guild model's contstructor you can populate the workers collection from your data manager like so:
Workers = CollectionViewSource.GetDefaultView(DataManager.Data.Workers);
and add a filter to your ICollectionView
like so:
Workers.Filter = (worker) =>
{
return (worker.Employer == this);
};
and call Workers.Refresh()
whenever the Workers
collection is updated.
That way your binding won't break and your Workers
collection will retain the same instance.
Oh, and add the UpdateSourceTrigger=PropertyChanged
to your TextBox
binding.
Like I said, I would look at redesigning the backing data structure entirely, but without knowing why or how you implemented it I cannot say more than that.