Доступ к родительскому тексту данных в списке в Silverlight

В Silverlight 2 я использую пользовательский контроль, который наследует текст данных страницы, на которой он встроен. Этот текстовый текст содержит текст вопроса, тип вопроса и набор ответов. В пользовательском элементе управления есть список, который привязан к коллекции ответов. Как показано ниже:

<ListBox DataContext="{Binding}" x:Name="AnswerListBox" ItemContainerStyle="{StaticResource QuestionStyle}" Grid.Row="1" Width="Auto" Grid.Column="2" ItemsSource="{Binding Path=AnswerList}" BorderBrush="{x:Null}" />       

Этот список имеет связанный стиль для отображения ответов в виде переключателей или флажков (которые я хотел бы скрыть или показать в зависимости от типа вопроса) как:

<Style TargetType="ListBoxItem" x:Key="QuestionStyle">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">                      
                        <StackPanel Background="Transparent" >
                            <RadioButton Visibility="{Binding Path=QuestionType, Converter={StaticResource QuestionTypeConverter}, ConverterParameter='RadioButtonStyle'}" Height="auto" Margin="0,0,0,10"  IsChecked="{TemplateBinding IsSelected}" IsHitTestVisible="False" Content="{Binding Path=AnswerText}">
                            </RadioButton>
                            <CheckBox Visibility="{Binding Path=QuestionType, Converter={StaticResource QuestionTypeConverter}, ConverterParameter='CheckBoxStyle'}" Height="auto" Margin="0,0,0,10" Content="{Binding Path=AnswerText}">
                            </CheckBox>
                        </StackPanel>                                                
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Итак, мой вопрос заключается в следующем: как получить доступ к родительскому контексту данных, чтобы получить QuestionType (так как это свойство самого текстового элемента управления пользователя, а не свойство AnswerItem в AnswerList)?

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

4 ответа

Решение

В Silverlight 3 и выше вы можете использовать привязку Элемент к элементу и указать элемент управления DataContext, а затем некоторые свойства в моем примере его свойство Threshold.

Итак, назовите ваш элемент управления (например, в моем примере это x:Name="control")

<UserControl x:Class="SomeApp.Views.MainPageView" x:Name="control" >

затем внутри этого элемента управления в вашем ListBox ItemTemplate вы можете получить доступ к родительскому DataContext следующим образом:

    <ListBox ItemsSource="{Binding Path=SomeItems}" >
        <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding ElementName=control, Path=DataContext.Threshold}"/>
            </StackPanel>
        </DataTemplate>
       </ListBox.ItemTemplate>         
       </ListBox>

Одна из самых крутых вещей, которые вы можете сделать с помощью Silverlight, - это использовать атрибут Tag для хранения ссылки на объект, с которым он связан.

Во-первых, в вашем классе объявите свойство, подобное этому

public IMyObject Current 
{
  get {return this;}
}

Затем на элемент управления, вызывающий событие, вы можете получить ссылку на объект

var fe= (FrameworkElement) sender;
var src = fe.Tag as IMyObject;

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

Current.Parent.QuestionType

Я только что боролся с подобной проблемой. Мой состоит из выпадающего списка, который отображается для каждого списка. DataContext верхнего уровня привязан к моей модели представления (MVVM) и выглядит следующим образом:

class ViewModel{
    ObservableCollection<ComboboxListItemType> DataForTheComboBoxList;
    ObservableCollection<MyDataType> DataForTheListBox;
    ...
}

Поскольку выпадающий список находится внутри ItemTemplate (=DataTemplate) для списка, DataContext для каждого элемента списка устанавливается на соответствующий элемент в DataForTheListBox, этот выпадающий список больше не может видеть необходимый DataForTheComboBoxList из DataContext верхнего уровня.

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

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

public partial class MyDataType
{
    private ObservableCollection<ComboboxListItemType> m_AllComboboxItems;
    public ObservableCollection<ComboboxListItemType> AllComboboxItems
    {
        get { return m_AllComboboxItems; }
        set
        {
            if (m_AllComboboxItems != value)
            {
                m_AllComboboxItems = value;
                RaisePropertyChanged("AllComboboxItems");
            }
        }
    }
}

Далее вы должны установить это свойство для каждого элемента в коллекции DataForTheListBox

// in ViewModel class
foreach(var x in this.DataForTheListBox)
{
    x.AllComboboxItems = this.DataForTheComboBoxList;
}

Тогда вернитесь в свой XAML:

<DataTemplate x:Key="ListBoxItemTemplate">
    ...
    <Combobox
        ItemsSource="{Binding AllComboboxItems}"
        SelectedItem="{Binding CurrentBlah}"/>
</DataTemplate>

Не забывайте, что для того, чтобы в комбинированном окне правильно отображался текущий элемент, выбранный элемент должен ссылаться на фактический элемент в элементе ItemsSource этого комбинированного списка. Если вы получаете данные из веб-службы, у которой есть идентификаторы или объекты, представляющие элемент для комбинированного списка, вы должны повторно сослаться на них, чтобы указать на фактическую коллекцию.

Если вы пытаетесь сделать это с помощью DataForm, тот же метод, показанный Roboblob, не работает.

Этот вопрос может иметь некоторую полезную информацию

Другие вопросы по тегам