Search code examples
c#wpfobservablecollectiontextblock

Bound property in WPF doesn't update


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, Workers' 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.


Solution

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