Как передать выбранный элемент списка в модель представления - PullRequest
0 голосов
/ 07 июня 2011

Это актуальный вопрос, который я обновил, чтобы, надеюсь, он стал немного более понятным.Короче говоря, я пытаюсь выполнить передачу свойства из выбранного элемента списка в модель представления, чтобы это свойство можно было использовать в новом запросе.В приведенном ниже коде Listbox наследует привязку данных от родительского объекта.Список содержит шаблоны данных (пользовательские элементы управления), используемые для отображения подробных результатов.

Проблема, с которой я сталкиваюсь, заключается в том, что в пользовательском элементе управления есть расширитель, который при нажатии вызывает команду из ViewModel.Из того, что я вижу, объект Listbox теряет свой контекст данных, поэтому для того, чтобы команда вызывалась при расширении расширителя, я должен явно установить datacontext расширителя.Делая это, кажется, создается новая модель представления, которая сбрасывает мое привязанное свойство (SelectedItemsID) в null.

Есть ли способ передать выбранный элемент из представления в модель представления и предотвратить сброс значения в нолькогда кнопка вызывает команду из элемента шаблона списка?

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

Может ли это быть достигнуто за пределами Prism или MVVMLite?

Исходное сообщение выглядит следующим образом:

В моем проекте у меня есть usercontrol списка, который содержит пользовательский шаблон данных.

<ListBox x:Name="ResultListBox"
             HorizontalAlignment="Stretch"
             Background="{x:Null}"
             BorderThickness="0"
             HorizontalContentAlignment="Stretch"
             ItemsSource="{Binding SearchResults[0].Results,
                                   Mode=TwoWay}"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled"
             SelectionChanged="ResultListBox_SelectionChanged">
        <ListBox.ItemTemplate>

            <DataTemplate>
                <dts:TypeTemplateSelector Content="{Binding}" HorizontalContentAlignment="Stretch">
                    <!--  CFS Template  -->
                    <dts:TypeTemplateSelector.CFSTemplate>
                        <DataTemplate>
                                <qr:srchCFS />
                        </DataTemplate>
                    </dts:TypeTemplateSelector.CFSTemplate>

                    <!--  Person Template  -->
                    <dts:TypeTemplateSelector.PersonTemplate>
                        <DataTemplate>
                                <qr:srchPerson /> 
                        </DataTemplate>
                    </dts:TypeTemplateSelector.PersonTemplate>

                   <!-- removed for brevity -->

            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

SelectionChanged вызывает следующий метод из кода

private void ResultListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (((ListBox)sender).SelectedItem != null)
        _ViewModel.SelectedItemID = (((ListBox)sender).SelectedItem as QueryResult).ID.ToString();
        this.NotifyPropertyChanged(_ViewModel.SelectedItemID);//binds to VM
    }

В ViewModel у меня есть следующее свойство

public string SelectedItemID
    {
        get
        {
            return this._SelectedItemID;
        }
        set
        {
            if (this._SelectedItemID == value) 
                return;
            this._SelectedItemID = value;
        }

    }

шаблон списка содержит пользовательский макет с элементом управления Expander.Элемент управления Expander используется для отображения более подробной информации, связанной с выбранным элементом.Эти детали (коллекция) создаются путем нового вызова моего прокси.Чтобы сделать это с помощью элемента управления Expander, я использовал выражения InvokeCommandAction

<toolkit:Expander Height="auto"
                          Margin="0,0,-2,0"
                          Foreground="#FFFFC21C"
                          Header="View Details"
                          IsExpanded="False"
                          DataContext="{Binding Source={StaticResource SearchViewModelDataSource}}"
                          Style="{StaticResource DetailExpander}">

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Expanded">
                    <i:InvokeCommandAction Command="{Binding GetCfsResultCommand}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>

В ViewModel вызываемая команда делегата GetCFSResultCommandExecute довольно проста

private void GetCfsResultCommandExecute(object parameter)
    {
        long IdResult;
        if (long.TryParse(SelectedItemID, out IdResult))
        {
            this.CallForServiceResults = this._DataModel.GetCFSResults(IdResult);}

Проблема, с которой я столкнулсяпри выборе Элемента списка, происходит событие selectionloaded, и свойство SelectedItemID обновляется с правильным идентификатором из выбранного элемента.Когда я нажимаю на расширитель, команда запускается, но для свойства SelectedItemID устанавливается значение NULL.Я проследил это с помощью Silverlight-Spy, и события соответствуют тому, что вы ожидаете, когда по нажатию на расширитель элемент списка теряет фокус, расширитель (переключается) получает фокус, и есть LeftMouseDownEvent, но я не вижу ничего, что объясняет, почемусвойство устанавливается в нуль.Я добавил тот же код, который использовался в событии изменения выбора, в событие LostFocus в элементе listboxt и все еще получил тот же результат.

Буду признателен за помощь в понимании того, почему открытое свойство SelectedItemID имеет значение NULL, когда для кнопки расширения, являющейся частью элемента управления списка, установлено значение NULL.И, конечно, я ДЕЙСТВИТЕЛЬНО был бы признателен за любую помощь в изучении того, как не задавать для свойства нулевое значение и сохранять привязанный идентификатор.

Обновление Я попытался удалить ссылку на текст данных из Expander.как это было предложено, чтобы быть проблемой.Из того, что у меня есть, поскольку это элемент шаблона данных, он «выходит» из визуального дерева и теряет ссылку на текстовый элемент данных элемента управления, который наследуется от родительского объекта.Если я попытаюсь установить текст данных в коде для элемента управления, все привязки к свойствам будут потеряны.

Моя следующая попытка состояла в том, чтобы установить текст данных для элемента управления расширением в конструкторе как

private SearchViewModel _ViewModel;
    public srchCFS()
    {
        InitializeComponent();
        this.cfsExpander.DataContext = this._ViewModel;
    }

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

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

Ответы [ 2 ]

0 голосов
/ 14 июня 2011

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

private void ResultListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (((ListBox)sender).SelectedItem != null)
            _ViewModel.SelectedItemID = (((ListBox)sender).SelectedItem as QueryResult).ID.ToString();
            MySelectedValue = (((ListBox)sender).SelectedItem as QueryResult).ID.ToString();
        this.NotifyPropertyChanged(_ViewModel.SelectedItemID);
    }

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

Для тех, кто заинтересован, обработчик изменения универсального свойства

        public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
0 голосов
/ 07 июня 2011

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

DataContext="{Binding Source={StaticResource SearchViewModelDataSource}}"

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

Надеюсь, это поможет!


Редактировать

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

private SearchViewModel _ViewModel;
public srchCFS()
{
    InitializeComponent();
    this.cfsExpander.DataContext = this._ViewModel;
}

Вместо использования this.cfsExpander вы можете попробовать использовать метод FindName. Может быть, это вернет вам правильный экземпляр.

object item = this.FindName("expander_name");
if ((item!=null)&&(item is Expander))
{
    Expander exp = item as Expander;
    exp.DataContext = this._ViewModel;
 }

Попробуйте, если это работает для вас.
Конечно, this._ViewModel должен предоставлять свойство типа ICommand с именем GetCfsResultCommand, но я думаю, что это уже сделано.

...