Привязка ElementName не работает в пользовательском элементе управления в Silverlight 4 - PullRequest
0 голосов
/ 04 мая 2011

У нас есть экран и модель его вида:

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 правильно привязан к View
  • Allузлы отображаются правильно
  • Обычная привязка работает правильно
  • В VS
  • Сценарий Simmilar работает с ListBox и ListBox.ItemTemplate

Проблема в том, что NodeClickedCommand никогда не привязывается, почему?

Ответы [ 3 ]

0 голосов
/ 04 мая 2011

Это контейнер имен. Шаблоны 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>

Вы по-прежнему ссылаетесь на ту же ICommand из ScreenViewModel, поэтому вы не создаете несколько экземпляров этой конкретной команды.

0 голосов
/ 05 мая 2011

Кажется, что использование 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);
}

Благодаря Дейну, он указал мне правильное направление.

0 голосов
/ 04 мая 2011

Я думаю, что проблема может быть в порядке генерации элементов и привязки команд.Ваши элементы пользовательского узла могут быть добавлены в дерево макета позже, чем привязка пытается разрешить команду, поэтому привязка в шаблоне данных не может пройти по дереву макета для разрешения вашего элемента.

Вы написали, что ListBox работаетс этой настройкой, поэтому попробуйте немного покопаться в нем, чтобы увидеть, в какой момент он генерирует элементы, и убедитесь, что вы следуете подобному шаблону.

...