2010年06月更新
将精华液蒸馏成较小的样品 - 此处提供的解决方案http://db.tt/v6r45a4
现在似乎是与标签控件相关的问题: 重新应用数据上下文(单击按钮)时,似乎一切都按预期工作。绑定刷新,但活动选项卡上的所有绑定都被破坏。
您可以通过选择任何标签并点击按钮来验证这一点,您会看到该标签上的控件变为kaput。添加了一堆日志记录语句,这是我的朋友到达这个概要和他的修复方法[在选项卡控件上设置DataContext =“{Binding}”]
但是我们仍然不确定为什么会这样做...
TabControl Data Context now set to ReproTabItemBug.MainViewModel
TabPage [LeftTabPage] Data Context now set to ReproTabItemBug.LeftViewModel
System.Windows.Data Error: 40 : BindingExpression path error: 'MiddleProp' property not found on 'object' ''MainViewModel' (HashCode=50608417)'. BindingExpression:Path=MiddleProp; DataItem='MainViewModel' (HashCode=50608417); target element is 'TextBox' (Name='MiddleTabTextbox'); target property is 'Text' (type 'String')
TabPage [MiddleTabPage] Data Context now set to ReproTabItemBug.MiddleViewModel
Middle tab textbox text changed to 634272638824920423
TabPage [RightTabPage] Data Context now set to ReproTabItemBug.RightViewModel
Middle tab textbox text changed to
上一篇文章 (免责声明:提前做好长篇大论......拿你的爆米花。我已经花了一天的时间在这上面了......)
我的ViewModel由三个POCO subViewModel组成。每个subVieModel都具有在上述3个部分中绑定的属性。每个subViewModel都作为标准的.net get-only属性公开(No INotifyPropertyChanged)
我的视图有3个部分。每个部分都有一个类似这样的DataContext setter。
...
<TabItem x:Name="_tabPageForVM2" DataContext="{Binding PropReturningSubVM2}">
<!-- followed by UI Items that are data-bound to props inside this sub-viewmodel -->
</TabItem>
...
总结
<MainView> <!--DataContext set programmmatically to an Instance Of MainViewModel) -->
<Control DataContext="{Binding PropReturningSubVM1}" >.. Section1 .. </Control>
<Control DataContext="{Binding PropReturningSubVM2}" >.. Section2 .. </Control>
<Control DataContext="{Binding PropReturningSubVM3}" >.. Section3 .. </Control>
</MainView>
现在这是令人费解的一点。 在正常启动时,我创建了一个MainViewModel实例(其子视图模型已传入其ctor)。监视窗口中的属性确认了这一点。
Trace.WriteLine("Before setting datacontext");
mainView.DataContext = null;
mainView.DataContext = mainViewModel;
//mainView.Refresh();
Trace.WriteLine("After setting datacontext");
一切都很完美。现在由于我无法控制的原因,有一种情况是UI被解除但视图仍然驻留在内存中。因此,为了在下次显示时清除它,我创建了我的viewmodel的新实例并重新应用了datacontext(通过调用与之前相同的初始化例程)
但是,当执行DataContext=mainViewModel
集时,我在输出窗口中看到一堆绑定错误。有趣的是,只有一个标签页(子视图模型)中的绑定被破坏。其他2个子视图模型正常运行 - 没有绑定错误。
System.Windows.Data Error: 40 : BindingExpression path error: 'RphGaugeMaxScale' property not found on 'object' ''MainViewModel' (HashCode=38546056)'. BindingExpression:Path=RphGaugeMaxScale; DataItem='MainViewModel' (HashCode=38546056); target element is 'Gauge' (Name='GaugeControl'); target property is 'MaxValue' (type 'Double')
System.Windows.Data Error: 40 : BindingExpression path error: 'RphGaugeMaxScale' property not found on 'object' ''MainViewModel' (HashCode=38546056)'. BindingExpression:Path=RphGaugeMaxScale; DataItem='MainViewModel' (HashCode=38546056); target element is 'Gauge' (Name='GaugeControl'); target property is 'MajorTickCount' (type 'Int32')
...
SubViewModel2上存在属性,但查找是使用MainViewModel。
接下来,我在MainView上添加了一个刷新方法(在重新应用DataContext之后调用),这样做
_tabPageForVM2.DataContext = null;
_tabPageForVM2.DataContext = mainVM.PropReturningSubVM2;
这解决了这个问题。
让我感到困惑的是
答案 0 :(得分:2)
您可以下载Snoop或其他显示可视树的工具,并看到TabItem的内容实际上不是此TabItem的可视子项,而是TabControl的可视子项。
因此TabItem是TabItem内容的逻辑子节点,TabControl是TabItem内容的可视子节点。 DataContext应该从逻辑父类继承,但在我看来,它是从TabItem或TabControl中随机继承的。
我建议的最佳解决方案是将绑定移动到这样的内容:
<TabItem x:Name="LeftTabPage" Header="LeftModel">
<StackPanel Orientation="Horizontal" DataContext="{Binding Left}">
<my:Gauge x:Name="gauge" Height="200" Width="200" Value="{Binding LeftProp}"/>
<Viewbox Height="200" Width="200" >
<my:Gauge x:Name="scaledGauge" Value="{Binding LeftProp}"/>
</Viewbox>
</StackPanel>
</TabItem>
<TabItem x:Name="MiddleTabPage" Header="{Binding}">
<TextBox x:Name="MiddleTabTextbox" DataContext="{Binding Middle}" Text="{Binding MiddleProp}" />
</TabItem>
<TabItem x:Name="RightTabPage" Header="RightModel">
<TextBox DataContext="{Binding Right}" Text="{Binding RightProp}"/>
</TabItem>
我想我明白了。我会试着解释一下。
如果第二个选项卡处于活动状态(例如),则MiddleTabTextbox是MiddleTabPage的逻辑子项,它是MyTabControl的可视子项(它有2个不同的父项,具有2个不同的DataContexts)。第一个选项卡不活动,它只有逻辑子 - StackPanel。单击DataContext按钮更改时。 StackPanel也没问题 - 它有一个父母。但MiddleTabTextbox应该做什么?从视觉或逻辑父母那里获取DataContext?从逻辑父级获取上下文似乎是合乎逻辑的:)我尝试了这种行为,但MiddleTabTextbox从可视子级获取它,即MyTabControl。因此,获取MiddleViewModel会导致MainViewModel出现大量绑定错误。我不知道为什么WPF从可视父节点继承DataContext而不是逻辑节点。
答案 1 :(得分:0)
此:
DataContext="{Binding PropReturningSubVM2}"
看起来很可疑。您正在使用Binding表达式,而不是指定Source或RelativeSource,这意味着您希望它在DataContext引用的任何对象上查找名为PropReturningSubVM2
的属性,并绑定结果该表达式返回到您的DataContext。
所以你在这里有一种循环引用的东西;你声明性地说你的DataContext是你的DataContext上的另一件事。在程序代码中,这很好,因为你可以完全控制它何时发生,你可以继续,然后继续前进。但XAML不是程序性的,它是声明性的;所以你宣称,“这是我想建立和执行的关系”,即使你宣布的关系是自相矛盾的。如果它完全起作用,那只是因为框架按照特定的顺序进行操作才能使其工作(首先,属性从父控件继承,然后应用绑定,并且由绑定引起的属性更改不会' t触发绑定再次运行)。显然,有时候不会按照特定的顺序发生,因此就是你的问题。
我建议删除循环引用。将绑定更改为以下内容:
DataContext="{Binding PropReturningSubVM2,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}}"
这会删除奇怪的循环引用,因为您不再基于自己的 DataContext绑定DataContext,现在您基于父TabControl的DataContext显式绑定它 - 所以没有更多的循环性,没有更多的排序依赖,并且希望不再有bug。
(如果合适,替换另一种类型而不是TabControl - 理想情况下,您可能希望使用以编程方式设置DataContext的父控件的类型开始。)
答案 2 :(得分:0)
据我所知,所有项目控件都会自动在其项目上设置DataContext。 对本地datacontext的绑定应该处理该行为。 我可以看到的是,当绑定重新评估本地DataContext是空的并且绑定解析为DataContext上面的级别时。 我会尝试通过设置BindingMode来设置绑定的updatebehaviours。如果这不起作用,我会尝试在适当的时候强制更新。 一种方法是使用多重绑定: 1.多绑定中的绑定是你现在的绑定 2. binding是对父datacontext的绑定
多值转换器只是在两个路径上传递第一个值 - 这样你就可以使用你想要的任何更改来更新绑定 - 2.绑定仅用于父datacontext(或其他值更改)时的额外更新。