我们有一个屏幕及其视图模型:
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);
}
我们确信:
问题是NodeClickedCommand从未绑定,为什么?
答案 0 :(得分:0)
我认为问题可能在于项目生成和命令绑定的顺序。您的自定义节点项可能会在绑定尝试解析命令之后添加到布局树中,因此datatemplate中的绑定无法遍历布局树以解析您的元素。
您写道ListBox适用于此设置,因此请尝试深入了解它,以确定它生成项目的位置,并确保您遵循类似的模式。
答案 1 :(得分:0)
这是命名容器。 ItemTemplates将由可视循环中的“稍后”控件呈现,因此命名容器的范围与UserControl中的位置不同。因此,Screen不是范围内的有效元素。
由于我们没有看到自定义控件的内部工作原理,我现在最好的解决方案是将您的节点包装在单独的视图模型中,并让这些视图模型引用NodeClickedCommand。
public class NodeViewModel : BaseViewModel
{
public Node Node { get; set; }
public ICommand NodeClickedCommand { get; set; }
}
public class ScreenViewModel : BaseViewModel
{
[NotifyPropertyChanged]
public List<NodeViewModel> Nodes { get; set; }
public ICommand NodeClickedCommand { get; set; }
public ScreenViewModel()
{
NodeClickedCommand = new RelayCommand(NodeClicked);
// ....
// Some code that binds Nodes.
// ....
// This code here whatever it does, when it gets the list of
// nodes, wrap them inside a NodeViewModel instead like this
var nvm = new NodeViewModel()
{
NodeClickedCommand = this.NodeClickedCommand,
Node = Node
};
nodes.Add(nvm);
}
private void NodeClicked()
{
MessageBox.Show("This is never shown");
}
}
然后您的XAML将如下所示:
<UserControl x:Class="ScreenView"
x:Name="Screen"
>
<CustomControl Nodes="{Binding Nodes}">
<CustomControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding NodeClickedCommand}">
<TextBlock>hello</TextBlock>
</Button>
</DataTemplate>
</CustomControl.ItemTemplate>
</CustomControl>
您仍然会从ScreenViewModel引用相同的ICommand,因此您不会创建该特定命令的多个实例。
答案 2 :(得分:0)
似乎使用 ContentPresenter 而不是 ItemTemplate.LoadContent 解决了这个问题:
foreach(Node node in Nodes)
{
ContentPresenter contentPresenter = new ContentPresenter();
contentPresenter.Content = node;
contentPresenter.ContentTemplate = ItemTemplate;
this._canvas.Children.Add(contentPresenter);
// FrameworkElement frameworkElement = (FrameworkElement)ItemTemplate.LoadContent();
// frameworkElement.DataContext = node ;
// this._canvas.Children.Add(frameworkElement);
}
感谢dain,因为他指出了我正确的方向。