24
votes

In Silverlight 2 I'm using a usercontrol which inherits the datacontext of the page that it's embedded on. This datacontext contains question text, a question type and a collection of answers. In the user control is a listbox which is bound to the collection of answers. As shown below:

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

This listbox has an associated style to display the answers in the form of radio buttons or checkboxes (which I would like to hide or show depending on the question type) as:

<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>

So my question is then: how do you access the parent data context in order to get the QuestionType (as this is a property on the user control datacontext itself, not a property on an AnswerItem in the AnswerList)?

Alternatively is there a better way to switch styles dynamically in the xaml based on a binding value?

4
Do you have any way to change your accepted answer? The one below, by Roboblob, is a correct answer. The accepted answer is incorrect for Silverlight and misleading.Gone Coding

4 Answers

55
votes

In Silverlight 3 and higher you can use Element to Element binding and point to the controls DataContext and then some property in my example its Threshold property.

So first name your control (for example in my example its x:Name="control")

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

then inside this control in your ListBox ItemTemplate you can access parent DataContext like this:

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

I've just been battling with a similar problem. Mine consists of a combobox that displays for each listitem. The top-level DataContext is bound to my viewmodel (MVVM) and looks like this:

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

Because the combobox is within the ItemTemplate (=DataTemplate) for the listbox, the DataContext for each list item is set to the appropriate item in DataForTheListBox, the combobox can no longer see the DataForTheComboBoxList it needs from the top-level DataContext.

My (dirty, ugly) workaround involves setting the complete combobox list on each item in the list, so it becomes visible to the DataContext in this list item.

First you make a partial class for your listbox data type. (Generally this will be coming from a service reference, so you cant touch the generated code directly without potentially losing it). This partial class contains a new property referring to your combobox list item type:

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");
            }
        }
    }
}

Next you have to set this property on each element in the DataForTheListBox collection

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

Then back in your XAML:

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

Dont forget that for the combobox to correctly display the current item, the selected item must refer to the actual item in the combobox's ItemsSource. If you are getting data from a webservice that has IDs or objects to represent the item for the combobox, you must re-reference them to point to the actual collection.

1
votes

If you're trying to do this with a DataForm he same method shown by Roboblob doesn't work.

This question may have some useful information

1
votes

On of the very cool things you can do with silverlight is to use the Tag attribute to store a reference to the object to which it is bound.

First, in your class, declare a property like this

public IMyObject Current 
{
  get {return this;}
}

Then on the control raising the event in you can get a reference to the object

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

So now that I have the object, it's reasonable for an object to have reference to its parent so then you bind to

Current.Parent.QuestionType