Search code examples
c#wpfmvvmdata-binding

How to use ElementName when binding to UIElement within template?


I am trying to figure out how to get around the issue of using ElementName when binding to a listbox that is in the same template as the item I am trying to bind. When I run the code shown below, I get the following error:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

commandParameter was null.

Additionally, there is no indication that there is any binding error in the Debug output window.

I have tried using the x:Reference method suggested in Binding ElementName inside a DataTemplate but this threw the following error:

System.Windows.Markup.XamlParseException: 'Cannot call MarkupExtension.ProvideValue because of a cyclical dependency. Properties inside a MarkupExtension cannot reference objects that reference the result of the MarkupExtension

My code is shown below:

<ContentControl Grid.Row="0" Grid.RowSpan="2" HorizontalAlignment="Center" Width="Auto" 
                        Height="Auto" VerticalAlignment="Top" Name="AddDeviceContentControl">
            <ContentControl.Style>
                <Style TargetType="ContentControl">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ChooseNewDevice}" Value="true">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="ContentControl">
                                        <ListBox Name="AddDeviceList" ItemsSource="{Binding Source={StaticResource DeviceTypes}}" >
                                            <ie:Interaction.Triggers>
                                                <ie:EventTrigger EventName="SelectionChanged">
                                                    <ie:InvokeCommandAction 
                                                        Command="{Binding AddDeviceCommand}" 
                                                        CommandParameter="{Binding ElementName=AddDeviceList, Path=SelectedItem}"/>
                                                </ie:EventTrigger>
                                            </ie:Interaction.Triggers>
                                        </ListBox>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>

Note: I am using a content control and template here because I want the list to be invisible unless the ChooseNewDevice bool is true. I chose not to use a popup because I want the listbox SelectedItem to clear when the list is closed, but the popup saves the state. If there is some way to clear the listbox SelectedItem in XAML (in order to follow MVVM) that would also solve my problem.


Solution

  • As suggested by other people here, you should replace your Interaction Trigger in XAML by moving that logic on your ViewModel, triggered by SelectedItem property changed.

    Your XAML would become simplier, something like this:

    <ListBox ItemsSource="{Binding Source={StaticResource DeviceTypes}}" 
         SelectedItem="{Binding SelectedItem}">
        <ListBox.Style>
        <Style TargetType="ListBox">
            <Setter Property="Visibility" Value="Visible"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding ChooseNewDevice}" Value="true">
                    <Setter Property="Visibility" Value="Hidden"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ListBox.Style>