0

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

这是TextBlock我认为的:

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

我的视图模型中的属性:

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

同样在我的视图模型中,更改 aWorker的 Employer 属性的命令:

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

消息框通过命令确认,Workers 的 Employer 属性正在正确更新,但我没有尝试过任何TextBlock更新。我已经尝试实施RaisePropertyChanged我在这里列出的所有内容,但没有运气。

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

如果有人对如何使它起作用有任何想法,我很想听听他们的意见。任何关于此事的建议将不胜感激。如果您需要更多代码或信息,请询问。

谢谢。

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

4

3 回答 3

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 转换所有可用的公共属性设置器。

您在该属性上没有设置器,因此它不会修复它。

于 2013-07-18T17:28:44.947 回答
1

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

Workers将属性更改为ICollectionView

public ICollectionView Workers { get; set; }

然后在 Guild 模型的构造函数中,您可以从数据管理器中填充工作人员集合,如下所示:

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

并像这样添加一个过滤器ICollectionView

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

Workers.Refresh()并在Workers集合更新时调用。

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

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

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

于 2013-07-18T18:38:16.913 回答
0

我没有测试你的代码,但是在Guild模型中,返回一个new ObservableCollection(可以在你的扩展方法中看到)可能会破坏你的绑定,我建议重新设计一下,以便绑定视图始终与原始实例相关联ObservableCollection.

虽然我没有使用你的框架,但是当我实现 MVVM 模式时,我总是确保我的 ViewModel 的 observables 保持相同的实例,并且我使用该Clear方法来替换它们在我的OnModelChanged方法中的内容,这样你就没有处理提醒此类更改视图所需的通知,它由您的ObservableCollection.

于 2013-07-18T17:58:55.620 回答