WPF中的绑定属性不会更新

时间:2013-07-18 17:24:57

标签: c# wpf observablecollection textblock

我的WPF / MVVM(带MVVM Light框架)游戏中有一个TextBlock,该游戏绑定到一个应该反映就业人数的属性。我已经确认绑定是完整的,但我无法更新它。

以下是我视图中的TextBlock

<TextBlock x:Name="WorkersTextBlock"
           FontFamily="Pericles"
           DataContext="{Binding Guilds[0]}"
           Text="{Binding Workers.Count,
                          StringFormat=Workers : {0},
                          FallbackValue=Workers : 99}" />

我的viewmodel中的属性:

public ObservableCollection<Guild> Guilds
{
    get { return DataManager.Data.Guilds; }
}

同样在我的viewmodel中,更改Worker的雇主属性的命令:

private void ExecuteHireWorkerCommand()
{
    if (SelectedWorker == null)
        return;

    SelectedWorker.Employer = DataManager.Data.Guilds[0];
    Gold -= SelectedWorker.Salary;
    _workerCollectionView.Refresh();
}

在DataManager中,它是一个包含我所有数据的单例类:

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; }
}

Guild模型中:

public ObservableCollection<Worker> Workers
{
    get { return DataManager.Data.Workers.Where(w => w.Employer == this).ToObservableCollection(); }
}

Worker中的雇主财产是:

public Guild Employer { get; set; }

最后,我的扩展方法(我认为是问题的根源):

public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> source)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }
    return new ObservableCollection<T>(source);
}

消息框确认通过该命令,Worker s'雇主属性正在正确更新,但我尝试过的任何内容都不会使TextBlock更新。我已经尝试在我列出的所有内容上实施RaisePropertyChanged,但没有运气。

如果我在初始化数据时将Worker的Employment属性设置为构造函数中的正确公会,则TextBlock中的数字会正确显示,但之后不会更新。我的预感是Workers属性中的LINQ过滤和扩展方法导致了这个问题,但我可能错了。

如果有人对如何让它发挥作用有任何想法,我很乐意听到它们。任何有关此事的建议都将不胜感激。如果您需要更多代码或信息,请询问。

感谢。

更新:我认为罗恩正走在正确的道路上;扩展方法可能会打破绑定。如果是这种情况,任何人都可以给我任何关于如何在不破坏绑定的情况下过滤Guild中的Workers属性的建议吗?此外,就setter问题而言,我在Workers属性中添加了一个setter,但它实际上从未触发过。

3 个答案:

答案 0 :(得分:1)

public ObservableCollection<Worker> Workers
{
    get { return DataManager.Data.Workers.Where(w => w.Employer == this).ToObservableCollection(); }
}

当此属性实际更改时,您不会通知绑定系统。如果基础集合本身发生了变化,你会没事的。但是你甚至没有保留对该底层集合的引用 - 你只是返回它。

正常模式会是这样的(假设您在INotifyPropertyChanged中实施Guild

private ObservableCollection<Worker> _Workers;
public ObservableCollection<Worker> Workers
{
    get { return _Workers; }
    set
    {
        if (value != _Workers)
        {
            _Workers = value;
            NotifyPropertyChanged("Workers")
        }
    }
}

但这在某种程度上取决于你如何设置对象。无论如何,您需要通知系统集合正在以某种方式发生变化。

编辑:你在评论中提到你使用了魔法。我去看了文档。根据它的运作方式,它说

  

5)显式或隐式应用MagicAttribute转换所有可用的公共属性setter。

你没有该属性的setter,所以它不会修复它。

答案 1 :(得分:1)

我认为您需要重新设计底层数据结构。但尽管如此,你可以稍微改变它以使其发挥作用。

Workers属性更改为ICollectionView,如下所示:

public ICollectionView Workers { get; set; }

然后在Guild模型的contstructor中,您可以从数据管理器中填充workers集合,如下所示:

Workers = CollectionViewSource.GetDefaultView(DataManager.Data.Workers);

并向ICollectionView添加过滤器,如下所示:

Workers.Filter = (worker) => { return (worker.Employer == this); };

并在更新Workers.Refresh()集合时致电Workers

这样你的绑定就不会中断,你的Workers集合将保留相同的实例。

哦,并将UpdateSourceTrigger=PropertyChanged添加到您的TextBox绑定中。

就像我说的那样,我会考虑完全重新设计支持数据结构,但不知道为什么或如何实现它,我不能说更多。

答案 2 :(得分:0)

我尚未测试您的代码,但在Guild模型中,返回new ObservableCollection(可在您的扩展方法中看到)可能会破坏您的绑定,I建议稍微重新设计,以便绑定视图始终与ObservableCollection的原始实例相关联。

虽然我没有使用你的框架,但是当我实现MVVM模式时,我总是确保我的ViewModel的observable仍然是同一个实例,我使用Clear方法来替换我的OnModelChanged方法。 {1}}方法,这样您就不必处理提醒此类更改视图所需的通知,而是由ObservableCollection处理。