我的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,但它实际上从未触发过。
答案 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
处理。