Silverlight, как установить контекст данных элемента списка в корневую модель представления для представления, в котором он находится

В моем проекте у меня есть список, который использует шаблон данных. В этом шаблоне данных у меня есть кнопка. Когда список генерирует результаты, источником элемента для этого списка устанавливается некоторая коллекция свойств, назовем ее Results[0].

Проблема, с которой я сталкиваюсь, заключается в том, что когда я нажимаю кнопку для вызова метода из модели представления, метод не может быть найден, потому что вызов обращается к контексту списка, а не к корневому представлению. Я использую набор инструментов SimpleMVVM, который использует локатор, аналогичный набору инструментов MVVMLight.

Один из подходов, который я использовал, заключался в том, чтобы явно установить контекст данных для кнопки, объявив модель представления в ресурсах пользовательского управления и установив ее статически.

<UserControl.Resources>
    <formatter:HighlightConverter x:Key="FormatConverter" />
    <vml:SearchViewModel x:Key="vm" />
</UserControl.Resources>

а затем кнопка содержит

<HyperlinkButton HorizontalAlignment="Left"
    Click="Button_Click"
    Content="{Binding Type}"
    Style="{StaticResource ListBoxtTitleHyperlink}">
 <i:Interaction.Triggers>
     <i:EventTrigger EventName="Click">
        <ei:CallMethodAction MethodName="GetDetailID" TargetObject="{Binding Source={StaticResource vm}}" />
     </i:EventTrigger>
 </i:Interaction.Triggers>
</HyperlinkButton>

Это работает в том смысле, что теперь я могу получить доступ к методу, но он создает новую модель представления, а не позволяет мне получить доступ к корневой модели представления представления. Следовательно, я теряю все свойства, которые могли быть у меня в предыдущей модели представления, поэтому я не могу передать их методу в качестве параметров.

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

Чтобы увидеть полную реализацию кода, вы можете загрузить пример проекта из папки SkyDrive< /а>

Обновление Я начинаю награду за этот вопрос, так как он поставил меня в тупик. Не стесняйтесь загружать пример проекта для справки. Для ясности цель этого вопроса состоит в том, чтобы узнать, как выполнить следующее 1. Выберите строку из списка 2. Событие selectionchanged установит для свойства текстовое значение, отображаемое в пользовательском интерфейсе (двусторонняя привязка RecordID с использованием Inotify 3. Нажмите на кнопку в шаблоне элемента и вызовите метод, хранящийся в ViewModel, используя триггеры взаимодействия и отобразив в окне сообщения значение свойства RecordID.

Шаги I и 2 выполнены. Где я застрял, так это в понимании того, как получить кнопку, которая является частью шаблона элемента списка, чтобы найти модель корневого представления и вызвать метод этой виртуальной машины без создания экземпляра новой ViewModel, которая сбросит все ранее сохраненные свойства.

заранее спасибо


person rlcrews    schedule 12.08.2011    source источник


Ответы (2)


Добавьте ресурс программно. Привязка StaticResource может выдать ошибку во время разработки, но во время выполнения она должна просто работать.

UserControl имеет свойство Resources, которое возвращает ссылку на ResourceDictionary. Вы можете добавить ViewModel к этому, и эффект будет таким же, как в вашем примере Xaml, за исключением того, что вы можете повторно использовать существующую ViewModel.

Предполагая, что ваша инфраструктура MVVM уже заполнила DataContext UserControl с помощью ViewModel, вы можете использовать код C#, аналогичный следующему, для настройки ресурса.

this.Resources.Add("vm", this.DataContext);

Если DataContext уже установлен в конструкторе UserControl, он может перейти туда. В противном случае вам нужно будет найти хук, который вызывается позже в жизненном цикле UserControl.

Изменить: просмотрев ваш код. Я бы предложил следующие модификации.

  1. Не устанавливайте ни DataContext, ни StaticResource "vm" в XAML.
  2. Используйте следующий код в качестве конструктора класса TemplateView.

Код:

public TemplateView()
{
    var templateViewModel = new TemplateViewModel();
    this.DataContext = templateViewModel;
    this.Resources.Add("vm", templateViewModel);
    InitializeComponent();
}

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

person Scott Munro    schedule 15.08.2011
comment
Спасибо, Скотт. Я так понимаю, вы правильно добавляете ресурс (datacontext) в конструкторе usercontrol для списка или для всего элемента управления? Псевдокод: this.uc.Resources[vm]; ? - person rlcrews; 16.08.2011
comment
Спасибо, Скотт. Я попробовал этот подход, но клиент не скомпилировался, потому что контекст, по-видимому, был установлен дважды. (С точки зрения и в коде позади) Я загрузил пример проекта с кодом на месте, если вы (или кто-либо еще) хотите взглянуть. skydrive.live.com/ - person rlcrews; 16.08.2011
comment
Я скачал проект, но обнаружил, что он скомпилирован нормально. Я не нашел код, который вы указали выше. Если у вас все еще есть версия, в которой у вас были проблемы с дважды установленным DataContext, я был бы рад взглянуть на это. - person Scott Munro; 17.08.2011
comment
Прости за этого, Скотт. Я обновил проект и перезалил его. Я также добавил комментарии в файл TemplateViewModel.cs, описывающие рассматриваемую область. Имена методов разные, но концепция та же. Еще раз спасибо за ваше время. - person rlcrews; 17.08.2011
comment
@randyc: я обновил свой ответ некоторым кодом, который использовал для запуска вашего решения. - person Scott Munro; 18.08.2011
comment
Скотт, Большое спасибо за ваше время и объяснение по настройке конструктора. Это решение работает. - person rlcrews; 18.08.2011
comment
Рад, что смог помочь. Обратите внимание, что всего несколько минут назад я упростил код — я понял, что вам не обязательно нужен ViewModelLocator. - person Scott Munro; 18.08.2011

Я разработчик WPF и точно не знаю, будет ли это работать в Silverlight, но обычно я меняю привязку вашего целевого объекта на что-то вроде

 <ei:CallMethodAction MethodName="GetDetailID" TargetObject="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}}"/> 

Фактически, просмотр дерева до тех пор, пока он не найдет первого предка ListBox, а затем проверка его свойства DataContext, которое, если я правильно прочитал ваш вопрос, является вашей ViewModel.

person Mark Green    schedule 16.08.2011
comment
Спасибо, Марк, полностью согласен. Проблема с Silverlight заключается в том, что у него нет доступа к режиму FindAncestor в Silverlight. Это позволяет вам указывать только на себя или объект шаблона - person rlcrews; 16.08.2011