编辑
该解决方案有时完全可以使我摆脱最初的疑惑,为什么它首先会起作用。毕竟,这些项目不是可视树的一部分。最后,这是完全有道理的:
- 该集合中的按钮不在可视树中,因此元素绑定不起作用。
- 应用模板将它们放入可视化树和绑定,如果此时应用,则开始工作。
- 这证实了可疑的比赛条件。
我的一位同事进行了一些扩展调试,也显示了该问题 - 在绑定成功的情况下,首先调用 OnApplyBinding。所以在不调整逻辑树的情况下使用集合是有缺陷的。
感谢您重回正轨的回复!
原帖
我有一个公开 ObservableCollection 的视图控件,我的视图可以包含任意元素,例如按钮。注意按钮上的ElementName绑定:
<local:ViperView>
<local:ViperView.MenuItems>
<Button Content="{Binding ElementName=btn, Path=Content}" />
</local:ViperView.MenuItems>
<Grid>
<Button x:Name="btn" Content="HELLO WORLD" />
</Grid>
</local:ViperView>
控件的 ControlTemplate 仅使用 ItemsControl 呈现内容:
<ControlTemplate ...
...
<ItemsControl
x:Name="PART_NavigationMenuItemsHost"
ItemsSource="{Binding MenuItems, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
上面的视图分配给我的主视图模型的 ActiveView 属性。主窗口仅通过数据绑定显示视图。
现在的问题是:如果视图在创建后没有立即分配给视图模型,则该视图中的 ElementName 绑定无法可靠地工作。
ElementName 绑定的工作方式如下:
MainViewModel.ActiveView = new ViperView();
ElementName 绑定有时使用正常优先级工作:
var view = new ViperView();
Dispatcher.BeginInvoke(() => MainViewModel.ActiveView view);
如果视图模型属性设置为低优先级,则ElementName 绑定总是失败:
var view = new ViperView();
Dispatcher.BeginInvoke(DispatcherPriority.Render, () => MainViewModel.ActiveView = view);
如果属性是从工作线程设置的, ElementName 绑定有时会起作用(绑定引擎编组回 UI 线程):
var view = new ViperView();
Task.Factory.StartNew(() => MainViewModel.ActiveView = view);
如果工作线程有延迟,ElementName 绑定总是失败:
var view = new ViperView();
var view = new ViperView();
Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
MainViewModel.ActiveView = view;
});
我对此没有答案。这似乎与时间有关。例如,如果我在上面的任务示例中添加一个简短的 Thread.Sleep,这总是会导致绑定中断,而没有睡眠,它只是有时会中断。
这对我来说是一个很好的展示 - 任何指针都值得赞赏......
谢谢你的建议菲利普