Link to home
Start Free TrialLog in
Avatar of Sid Price
Sid PriceFlag for United States of America

asked on

Binding items in ListItem

I have a WPF form that has a CheckBox and ListView on it, among other controls. The ListView displays data using a UserControl as the ListViewItems. What I am trying to do is to bind the Visibility of some controls in the UserControl to the IsChecked property of the CheckBox. Note that I have a converter for the Boolean to Visibilty conversion.

So, how can I bind the Visibity of the controls in the UserControl to the CheckBox?
Thanks,
Sid.
Avatar of Ravi Vaddadi
Ravi Vaddadi
Flag of United States of America image

use ElementName and Path properties of binding

{ Binding ElementName=<CheckBoxControlName> Path=IsChecked }

OR

{ Binding RelativeSource { RelativeSource Mode=FindAncestor, AncestorType={x:Type CheckBox, AcestorLevel=<BasedOnYourHierarchy> }
Avatar of Sid Price

ASKER

Many thanks for the input. I am trying to use the "RelativeSource" approach but so far I cannot get it working in this scenario.

Here are the two UserControls that I am working with, first the parent UserControl ("CodeDisplay"):
<UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:l="clr-namespace:DebugBar"
	mc:Ignorable="d"
	x:Class="CodeDisplay"
	x:Name="CodeDisplayControl"
	d:DesignWidth="640" d:DesignHeight="480" ScrollViewer.VerticalScrollBarVisibility="Disabled">
	<UserControl.Resources>
		<l:BooleanToVisibilityConverter x:Key="cvtItemValue" IsReversed="True"/>		
	</UserControl.Resources>
		<Grid>
			<Grid.RowDefinitions>
				<RowDefinition Height="auto" />
				<RowDefinition Height="*" />
			</Grid.RowDefinitions>
			<Expander	Header="Details" 
						Margin="0,0,0,1"
						Grid.Row="0"
						d:LayoutOverrides="Height"
						ScrollViewer.VerticalScrollBarVisibility="Disabled" FontSize="10.667">
				<Grid Background="White">
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="0.1*"/>
						<ColumnDefinition Width="0.3*"/>
						<ColumnDefinition Width="0.3*"/>
						<ColumnDefinition Width="0.3*"/>
					</Grid.ColumnDefinitions>
					<Grid.RowDefinitions>
						<RowDefinition Height="0.5*" />
						<RowDefinition Height="0.5*" />
					</Grid.RowDefinitions>

					<CheckBox	x:Name="chkShowSource"
								IsChecked="{Binding ShowSource, ElementName=CodeDisplayControl}"
								Grid.Column="1"
								Grid.Row="0">Show Source</CheckBox>
					<Border Grid.Column="2" Grid.RowSpan="2" BorderThickness="1" CornerRadius="5" BorderBrush="#FFB1B1B1" Margin="0" Padding="4">
						<StackPanel>
							<TextBlock>File</TextBlock>
							<TextBlock x:Name="txtFilename">???</TextBlock>
						</StackPanel>
					</Border>
					<Border Grid.Column="3" Grid.RowSpan="2" BorderThickness="1" CornerRadius="5"  BorderBrush="#FFB1B1B1" Padding="4">
						<StackPanel>
							<TextBlock>Function</TextBlock>
							<TextBlock x:Name="txtFunctionName">???</TextBlock>
						</StackPanel>
					</Border>
					
				</Grid>
			</Expander>
			<ListView	x:Name="lstCode"
						Grid.Row="1"
						IsEnabled="{Binding FullLicense, ElementName=CodeDisplayControl}"
						ItemContainerStyle="{DynamicResource IDB_ListBoxItemStyle}" 
						ScrollViewer.CanContentScroll="False"  BorderBrush="#FF908882" Margin="0,9,0,0"/>

	</Grid>
</UserControl>

Open in new window

The ListView in the above control is bound to a collection of the following UserControl.
<UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
	xmlns:l="clr-namespace:DebugBar"
	mc:Ignorable="d"
	x:Class="CodeLine"
	x:Name="UserControl"
	d:DesignWidth="640" d:DesignHeight="480">

	<UserControl.Resources>
		<l:BooleanToVisibilityConverter x:Key="cvtItemValueHidden"  UseHidden="True" />
		<l:BooleanToVisibilityConverter x:Key="cvtItemValue" />
	</UserControl.Resources>
	
	<Grid x:Name="LayoutRoot">
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="15px"/>
			<ColumnDefinition Width="*"/>
		</Grid.ColumnDefinitions>
		<Image	x:Name="imgLineState"
				Source="images/BlankIcon.png"
				Grid.Column="0" VerticalAlignment="Top" Margin="0,2,0,0"
				/>
		<StackPanel Orientation="Vertical" Grid.Column="1">
			<TextBlock	x:Name="txtLine"
			Visibility="{Binding RelativeSource={RelativeSource
						Mode=FindAncestor,
						AncestorType={x:Type UserControl},
						AncestorLevel=4}, Path=ShowSource,
						Converter={StaticResource cvtItemValue}}"
						VerticalAlignment="Top"
						Margin="2,2,0,0"
						Text="{Binding LineText}"
						Foreground="#FF6CCC6C"
						HorizontalAlignment="Center"/>
			<ListBox	x:Name="lstAsmLines"
			ItemContainerStyle="{DynamicResource IDB_ListBoxItemStyle}"
			Visibility="Visible"
			BorderBrush="{x:Null}" 
			ScrollViewer.VerticalScrollBarVisibility="Hidden"
			ScrollViewer.HorizontalScrollBarVisibility="Hidden" Margin="-19,0,0,0"/>
		</StackPanel>
	</Grid>
</UserControl>

Open in new window

The binding that is not working is the "txtLine" TextBlock in the above control. I am unclear how to figure out the "AncestorLevel" for this binding and have tried several different values.

Thanks,
Sid.
Did u try without specifying the ancestorlevel ?
1. IsChecked="{Binding ShowSource, ElementName=CodeDisplayControl}" where is ShowSource? I see that you are specifying element name as the user control and path as showsource.. this binding does not seem right

2. Same thing with IsEnabled="{Binding FullLicense, ElementName=CodeDisplayControl}"

I think you will have to do some reading on Binding and how it works

Here is a good point to start
To answer your first post: If you look at the code and also what I wrote you will see that yes I am using AncestorLevel.

To answer your second post:

1. I did not include the code behind, in that the UserControl has a property called "ShowSource." Also note that I wrote that this binding is working.

2. Again, the "FullLicense" property is defined in the code behind and once again this is working.

So .. perhaps we can focus on my specific question, here is what I wrote and is my specific problem:

The binding that is not working is the "txtLine" TextBlock in the above control. I am unclear how to figure out the "AncestorLevel" for this binding and have tried several different values.

I believe I have an intermediate understanding of WPF data binding, this is part of a much larger application that has many working examples of data binding and in trying to research how to do this particular binding I have already read the article to which you linked.

Thanks,
Sid.
So you r tyring to bind txtLine it to a view model property that is datacontext of a control is up in a hierarchy?
Yes that is correct,
Sid.
You do not need to do all that.. as long as you are not changing the data context.. it is in the same data context and you can use that property directly...

I thought you were trying to bind to an element property (not view model property).

Try this and see

Visibility="{Binding ShowSource,                               Converter={StaticResource cvtItemValue}}"
                                    VerticalAlignment="Top"
                                    Margin="2,2,0,0"
                                    Text="{Binding LineText}"
                                    Foreground="#FF6CCC6C"
                                    HorizontalAlignment="Center"/>

Also,  You need to remove ElementName from  ListView Binding as below.

<ListView      x:Name="lstCode"
                                    Grid.Row="1"
                                    IsEnabled="{Binding FullLicense }"
                                    ItemContainerStyle="{DynamicResource IDB_ListBoxItemStyle}"
                                    ScrollViewer.CanContentScroll="False"  BorderBrush="#FF908882" Margin="0,9,0,0"/>


This link might help you.
Thank you. However this still does not work, which suggests the DataContext is not correct for the property "ShowSource". Is there some way to know what the DataContext actually is?

This is the binding I have now:

<TextBlock	x:Name="txtLine"
			Visibility="{Binding ShowSource, Converter={StaticResource cvtItemValue}}"
			VerticalAlignment="Top"
			Margin="2,2,0,0"
			Text="{Binding LineText}"
			Foreground="#FF6CCC6C"
			HorizontalAlignment="Center"/>

Open in new window


Sid.
Add xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" to the UserControl at the top

and

<TextBlock      x:Name="txtLine"
                  Visibility="{Binding ShowSource,  diag:PresentationTraceSources.TraceLevel="High", Converter={StaticResource cvtItemValue}}"
                  VerticalAlignment="Top"
                  Margin="2,2,0,0"
                  Text="{Binding LineText}"
                  Foreground="#FF6CCC6C"
                  HorizontalAlignment="Center"/>
This would write to output window

Also,

Visual Studio -> Tools -> Options -> Debugging -> Output Window -> WPF Trace Settings -> Data Binding -> All
Thanks again.

The issue is that the DataContext is indeed incorrect. The DataContext of the UserControl with the TextBlock being bound is the UserControl itself. The property being bound to the TextBlock is on a UserControl that is higher up the visual tree.

The property "ShowSource" is on a UserControl (class "CodeDisplay"). CodeDisplay has a ListView and the items being displayed by the ListView are UserControls (class "CodeLine"). The TextBlock is on CodeLine.

Looking at the VS output window I see:

System.Windows.Data Error: 40 : BindingExpression path error: 'ShowSource' property not found on 'object' ''CodeLine' (Name='UserControl')'. BindingExpression:Path=ShowSource; DataItem='CodeLine' (Name='UserControl'); target element is 'TextBlock' (Name='txtLine'); target property is 'Visibility' (type 'Visibility')

Open in new window


Sid.
Could you post your view models pls?
Due to NDA I am unable to post the code behind files.
Try this and see

            Visibility="{Binding RelativeSource={RelativeSource
                                    Mode=FindAncestor,
                                    AncestorType={x:Type UserControl }}, Path=DataContext.ShowSource,
                                    Converter={StaticResource cvtItemValue}}"
No this still is not working.

System.Windows.Data Information: 41 : BindingExpression path error: 'ShowSource' property not found for 'object' because data item is null.  This could happen because the data provider has not produced any data yet. BindingExpression:Path=DataContext.ShowSource; DataItem='CodeLine' (Name='UserControl'); target element is 'TextBlock' (Name='txtLine'); target property is 'Visibility' (type 'Visibility')
System.Windows.Data Information: 20 : BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=DataContext.ShowSource; DataItem='CodeLine' (Name='UserControl'); target element is 'TextBlock' (Name='txtLine'); target property is 'Visibility' (type 'Visibility')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=DataContext.ShowSource; DataItem='CodeLine' (Name='UserControl'); target element is 'TextBlock' (Name='txtLine'); target property is 'Visibility' (type 'Visibility')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=DataContext.ShowSource; DataItem='CodeLine' (Name='UserControl'); target element is 'TextBlock' (Name='txtLine'); target property is 'Visibility' (type 'Visibility')

Open in new window

Sid.
Please Try AncestorLevel = 2
The binding is still not working.
Sid.
Wats the error now?
In the interests of clarity and because I have had to make some layout changes I am going to paste the two UserControl classes again so that it is clear where we are in trying to debug what is wrong.

First is the "CodeDisplay" UserControl:

<UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:l="clr-namespace:DebugBar"
	mc:Ignorable="d"
	x:Class="CodeDisplay"
	x:Name="CodeDisplayControl"
	d:DesignWidth="640" d:DesignHeight="480" ScrollViewer.VerticalScrollBarVisibility="Disabled">
	<UserControl.Resources>
		<l:BooleanToVisibilityConverter x:Key="cvtItemValue" IsReversed="True"/>		
	</UserControl.Resources>
		<Grid>
			<Grid.RowDefinitions>
				<RowDefinition Height="auto" />
				<RowDefinition Height="*" />
			</Grid.RowDefinitions>
			<Expander	Header="Details" 
						Margin="0,0,0,1"
						Grid.Row="0"
						d:LayoutOverrides="Height"
						ScrollViewer.VerticalScrollBarVisibility="Disabled" FontSize="10.667">
				<Grid Background="White">
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="0.1*"/>
						<ColumnDefinition Width="0.3*"/>
						<ColumnDefinition Width="0.3*"/>
						<ColumnDefinition Width="0.3*"/>
					</Grid.ColumnDefinitions>
					<Grid.RowDefinitions>
						<RowDefinition Height="0.5*" />
						<RowDefinition Height="0.5*" />
					</Grid.RowDefinitions>
					<!--, ElementName=CodeDisplayControl-->
					<CheckBox	x:Name="chkShowSource"
								IsChecked="{Binding ShowSource, ElementName=CodeDisplayControl}"
								Grid.Column="1"
								Grid.Row="0">Show Source</CheckBox>
					<Border Grid.Column="2" Grid.RowSpan="2" BorderThickness="1" CornerRadius="5" BorderBrush="#FFB1B1B1" Margin="0" Padding="4">
						<StackPanel>
							<TextBlock>File</TextBlock>
							<TextBlock x:Name="txtFilename">???</TextBlock>
						</StackPanel>
					</Border>
					<Border Grid.Column="3" Grid.RowSpan="2" BorderThickness="1" CornerRadius="5"  BorderBrush="#FFB1B1B1" Padding="4">
						<StackPanel>
							<TextBlock>Function</TextBlock>
							<TextBlock x:Name="txtFunctionName">???</TextBlock>
						</StackPanel>
					</Border>
					
				</Grid>
			</Expander>
			<!--, ElementName=CodeDisplayControl-->
			<ListView	x:Name="lstCode"
				DataContext="CodeDisplayControl"
				Grid.Row="1"
				IsEnabled="{Binding FullLicense, ElementName=CodeDisplayControl}"
				ItemContainerStyle="{DynamicResource IDB_ListBoxItemStyle}" 
				ScrollViewer.CanContentScroll="False"  BorderBrush="#FF908882" Margin="0,9,0,0"/>
	</Grid>
</UserControl>

Open in new window


Second is the "CodeLine" user control:

<UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
	xmlns:l="clr-namespace:DebugBar"
	mc:Ignorable="d"
	x:Class="CodeLine"
	x:Name="UserControl"
	d:DesignWidth="640" d:DesignHeight="480">

	<UserControl.Resources>
		<l:BooleanToVisibilityConverter x:Key="cvtItemValueHidden"  UseHidden="True" />
		<l:BooleanToVisibilityConverter x:Key="cvtItemValue" />
	</UserControl.Resources>
	
	<Grid x:Name="LayoutRoot">
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="15px"/>	<!--Column is used to display an informative icon, e.g. PC position
											breakpoint state or similar-->
			<ColumnDefinition Width="*"/>
		</Grid.ColumnDefinitions>
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto" />
			<RowDefinition Height="*" />
		</Grid.RowDefinitions>
		<StackPanel	Grid.Row="0"
					Grid.Column="0"
					Grid.ColumnSpan="2"
					Orientation="Horizontal"
					Visibility="{
						Binding RelativeSource={RelativeSource
                        Mode=FindAncestor,
						AncestorLevel=2,
                        AncestorType={x:Type UserControl }},
						Path=DataContext.ShowSource,
						Converter={StaticResource cvtItemValue}}">
			<Image	x:Name="imgLineState"
				Source="images/BlankIcon.png"
				VerticalAlignment="Top"
				Width="15px"
				Margin="0,2,0,0"/>
			<TextBlock	x:Name="txtLine"
			Visibility="Visible"
				VerticalAlignment="Top"
				Margin="2,2,0,0"
				Text="{Binding LineText}"
				Foreground="#FF6CCC6C"
				HorizontalAlignment="Center"/>
		</StackPanel>
		<ListBox	x:Name="lstAsmLines"
			ItemContainerStyle="{DynamicResource IDB_ListBoxItemStyle}"
			Visibility="Visible"
			Grid.Column="0"
			Grid.Row="1"
			Grid.ColumnSpan="2"
			
			BorderBrush="{x:Null}" 
			ScrollViewer.VerticalScrollBarVisibility="Hidden"
			ScrollViewer.HorizontalScrollBarVisibility="Hidden" Margin="0,0,0,0"/>
	</Grid>
</UserControl>

Open in new window


Note that the CodeLine control now has a StackFrame that needs its Visibility property bound to "ShowSource". "ShowSource" is a property of the class "CodeDisplay".

CodeDisplay has the ListView that has its ItemSource bound to a collection of CodeLine objects (in code).

The Output windows in VS 2010 has so much data and I have to admit it doesn't make much sense to me. I see bindings being reported as not working when the data is in fact being displayed in other parts of the application. I have attached the fiull output window code from the last test I made.

Thanks,
Sid.
Output.txt
Sid,

Is FullLicense a property in CodeDisplayControl View model?
Yes that is correct and that Binding works well.
Sid.
Something could be wrong with your visibility converter. Could u try to use built-in BooleanToVisibility converter and see
The converter is NOT getting called and it works very well in other parts of the application.
Sid.
where is the data template for list box?
    <Style x:Key="IDB_ListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, 
            RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment,
             RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="Padding" Value="2,0,0,0"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" 
                            BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" 
                            Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter Property="Background" TargetName="Bd" Value="WhiteSmoke"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Open in new window

what about data?
As I said earlier, the ListView "ItemsSource" is bound to an observable collection of CodeLine objects in code.

lstCode.ItemsSource = Lines

Open in new window


The "Lines" property is a collectkion of CodeLine objects:

    Private mLines As New ObservableCollection(Of CodeLine)
    ''' <summary>
    ''' The collection of line objects to be displayed
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Lines As ObservableCollection(Of CodeLine)
        Get
            Return mLines
        End Get
        Set(value As ObservableCollection(Of CodeLine))
            mLines = value
        End Set
    End Property

Open in new window

SOLUTION
Avatar of Ravi Vaddadi
Ravi Vaddadi
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
OK thanks. The first change I made to my code from your list was #1. This stopped the binding of the CheckBox to the property "ShowSource" working. Here is what you suggested:
<CheckBox	x:Name="chkShowSource"
					IsChecked="{Binding ShowSource, Mode=TwoWay}"
					Grid.Column="1"
					Grid.Row="0">Show Source</CheckBox>

Open in new window


Here is my code and it works:
<CheckBox	x:Name="chkShowSource"
			IsChecked="{Binding ShowSource, ElementName=CodeDisplayControl}"
			Grid.Column="1"
			Grid.Row="0">Show Source</CheckBox>

Open in new window

This is the property "ShowSource" code:
    Public Property ShowSource As Boolean
        Get
            Return mShowSource
        End Get
        Set(value As Boolean)
            If mShowSource <> value Then
                OnPropertyChanged("ShowSource")
                Debug.WriteLine("ShowSource Changed")
            End If
            mShowSource = value
        End Set
    End Property

Open in new window


This suggests that I have something different about the DataContext of my code,

Any ideas?
Sid.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The "magic" appears to be the explicit setting of the DataContext for the CodeDisplay class. After looking at your code and also from your comment "I set it at once place at the top of the hierarchy ..." I added the following line to the CodeDisplay "Initialized" event:
Me.DataContext = Me

Open in new window

Now the visibility binding is working. I was also able to remove the "ElementName" elements and those bindings also work.

I want to thank you so much for your persistence and patience.
Sid.