ElementName 绑定在 Silverlight 4 的自定义控件中不起作用
我们有一个屏幕及其视图模型:
public class ScreenViewModel : BaseViewModel
{
[NotifyPropertyChanged]
public List<Node> Nodes { get; set; }
public ICommand NodeClickedCommand { get; set; }
public ScreenViewModel()
{
NodeClickedCommand = new RelayCommand(NodeClicked);
// ....
// Some code that binds Nodes.
// ....
}
private void NodeClicked()
{
MessageBox.Show("This is never shown");
}
}
在此页面上,我们有自定义控件 (CustomControl) 和以下 xaml 来绑定命令:
<UserControl x:Class="ScreenView"
x:Name="Screen"
>
<CustomControl Nodes="{Binding Nodes}">
<CustomControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding ElementName=Screen,
Path=DataContext.NodeClickedCommand}">
<TextBlock>hello</TextBlock>
</Button>
</DataTemplate>
</CustomControl.ItemTemplate>
</CustomControl>
我们的自定义 SL 控件使用上面的模板 (DataTemplate) 来显示它的子项:
foreach(Node node in Nodes)
{
FrameworkElement frameworkElement = (FrameworkElement)ItemTemplate.LoadContent();
frameworkElement.DataContext = node ;
this._canvas.Children.Add(frameworkElement);
}
我们确信:
- ViewModel 是正确绑定到视图
- 所有节点都正确显示
- 常规绑定工作正常
- 如果我们使用 Command="{Binding NodeClickedCommand}" 进行绑定,则VS 命令绑定不会出现任何绑定警告
- ,但是当然,这会绑定到应该存在于单个节点上的命令,并且我们想要绑定到屏幕视图模型上存在的命令。
- 类似的场景适用于 ListBox 和 ListBox.ItemTemplate
问题是 NodeClickedCommand 从未绑定,为什么?
We have a screen and its view model:
public class ScreenViewModel : BaseViewModel
{
[NotifyPropertyChanged]
public List<Node> Nodes { get; set; }
public ICommand NodeClickedCommand { get; set; }
public ScreenViewModel()
{
NodeClickedCommand = new RelayCommand(NodeClicked);
// ....
// Some code that binds Nodes.
// ....
}
private void NodeClicked()
{
MessageBox.Show("This is never shown");
}
}
On this page we have custom control (CustomControl) and following xaml to bind the command:
<UserControl x:Class="ScreenView"
x:Name="Screen"
>
<CustomControl Nodes="{Binding Nodes}">
<CustomControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding ElementName=Screen,
Path=DataContext.NodeClickedCommand}">
<TextBlock>hello</TextBlock>
</Button>
</DataTemplate>
</CustomControl.ItemTemplate>
</CustomControl>
Our custom SL control uses the above template (DataTemplate) to display it's children:
foreach(Node node in Nodes)
{
FrameworkElement frameworkElement = (FrameworkElement)ItemTemplate.LoadContent();
frameworkElement.DataContext = node ;
this._canvas.Children.Add(frameworkElement);
}
We are sure that:
- ViewModel is correctly bound to View
- All nodes are displayed correctly
- Regular binding works correctly
- There are no binding warnings in VS
- Command binding works if we bind with Command="{Binding NodeClickedCommand}", but of course this binds to command that should exist on single node and we want to bind to command that exists on the screen view model.
- Simmilar scenario works with ListBox and ListBox.ItemTemplate
The problem is that NodeClickedCommand is never bound, why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为问题可能在于项目生成和命令绑定的顺序。您的自定义节点项可能会在绑定尝试解析命令之后添加到布局树中,因此数据模板中的绑定无法遍历布局树来解析您的元素。
您写到 ListBox 可以使用此设置,因此请尝试深入研究一下它在哪个点生成项目,并确保您遵循类似的模式。
I think the problem might be with the order of item generation and command binding. Your custom node items might be added to the layout tree later than the binding tries to resolve the command, so the binding in the datatemplate can't traverse the layout tree to resolve your element.
You wrote that ListBox works with this setup, so try to dig into it a bit to see at which point does it generate the items and make sure you follow a similar pattern.
这是命名容器。 ItemTemplates 将在视觉周期中“稍后”由控件呈现,因此命名容器的范围与 UserControl 中的位置不同。因此,Screen 不是范围内的有效元素。
由于我们看不到自定义控件的内部工作原理,因此我现在为您提供的最佳解决方案是将节点包装在单独的视图模型中,并让这些视图模型引用 NodeClickedCommand。
那么您的 XAML 将如下所示:
您仍然会从 ScreenViewModel 引用相同的 ICommand,因此您不会创建该特定命令的多个实例。
It's the naming container. The ItemTemplates will be rendered by a control "later" in the visual cycle, so the naming container is a different scope than the location in the UserControl. Therefore, Screen is not a valid element in the scope.
Since we don't see the inner workings of your custom control, my best solution for you right now is wrap your nodes in separate view models and have those view models reference the NodeClickedCommand.
Then your XAML would look like this:
You would still reference the same ICommand from the ScreenViewModel, so you are not creating multiple instances of that specific command.
似乎使用 ContentPresenter 而不是 ItemTemplate.LoadContent 解决了这个问题:
感谢 dain,他为我指明了正确的方向。
It seems that using ContentPresenter instead of ItemTemplate.LoadContent solves this issue:
Thanks to dain as he pointed me to the right direction.