Page 1 of 1

Binding to a TextBox inside a Style for a ListBoxItem

Posted: 30 May 2014, 02:25
by descala
Hi!

I'm having a bit of trouble adding items to a listbox for which I've made an item style.

The idea is that a TextBox contained within the style should be bound to a property of my data items, in this case ModelName. I set the binding to ModelName inside the TextBox, set the ItemsSource property for the ListBox, as well as the item container style, and a DataContext containing items which expose a ModelName property, but this approach is failing. The listbox does get populated as I add items to it, but my textboxes are blank. I tried using a two-way binding, but that did not work either.

To start with, here is my list box item style:
		<Style x:Key="ListBoxItemModelLayerStyle" 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}">
						<Grid x:Name="GridTemplate" HorizontalAlignment="Stretch" Height="60" Margin="0" VerticalAlignment="Top" Width="168" Visibility="Visible">
							<Rectangle x:Name="RectangleTemplate" RadiusY="5" RadiusX="5" Fill="#BF6C87CC"/>
							<TextBox x:Name="ModelNameTextBoxTemplate" Margin="32.5,5,0,0" TextWrapping="Wrap" Text="{Binding ModelName}" FontSize="12" Background="#FF5368A1" Foreground="#FFDADADA" Height="25" VerticalAlignment="Top" Width="130" HorizontalAlignment="Left" BorderBrush="{x:Null}"/>
							<ComboBox x:Name="ModelTypeComboBoxTemplate" Height="24" Margin="32,0,0,5" VerticalAlignment="Bottom" Width="79" HorizontalAlignment="Left" Background="#FF6781C3" Foreground="Black" BorderBrush="{x:Null}">
								<ComboBoxItem x:Name="ModelTypeBoxUpperTemplate" Content="Upper"/>
								<ComboBoxItem x:Name="ModelTypeBoxLowerTemplate" Content="Lower"/>
								<ComboBoxItem x:Name="ModelTypeBoxBuccalTemplate" Content="Buccal"/>
							</ComboBox>
							<ToggleButton x:Name="ModelToggleVisibilityButtonTemplate" Content="ToggleButton" HorizontalAlignment="Left" Margin="3,0,0,0" Style="{DynamicResource ToggleVisibilityEye}" Width="25" IsChecked="True" VerticalAlignment="Center" Height="25"/>
						</Grid>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
... and here is the ListBox to whose items it is applied ...
				<ListBox x:Name="ItemLayersListBox" ItemsSource="{Binding Items}" HorizontalAlignment="Center" Margin="0" Width="200" VerticalAlignment="Center" MaxHeight="720" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden" MinHeight="200">
					<ListBoxItem Content="ModelLayer" Style="{DynamicResource ListBoxItemModelLayerStyle}"/>
				</ListBox>
Here are my data context and items wrapper classes:
    class DataModel : public Noesis::Core::BaseComponent
    {
    public:
        DataModel() : items_(new Noesis::Gui::Collection())
        {    }
        ~DataModel()
        {
            
        }
        
        Noesis::Gui::Collection* GetItems() const { return items_; }
        
        
    private:
        Noesis::Gui::Collection* items_;
        
        NS_IMPLEMENT_INLINE_REFLECTION(DataModel, Noesis::Core::BaseComponent)
        {
            NsMeta<Noesis::Core::TypeId>("DataModel");
            NsProp("Items", &DataModel::GetItems);
        }
    };
    struct DataItem : public Noesis::Core::BaseComponent
    {
        DataItem(const NsChar* n) :
        name_(n)
        {
        }
        
        NsString name_;
        NsString type_;
        
        NS_IMPLEMENT_INLINE_REFLECTION(DataItem, Noesis::Core::BaseComponent)
        {
            NsMeta<Noesis::Core::TypeId>("DataItem");
            NsProp("ModelName", &DataItem::name_);
        }
    };
and here is where I initialize my listbox, set data context and item style:
        auto list_box = NsStaticCast<ListBox*>(gui_root_->FindName(LISTBOX_NAME.c_str()));
        
        data_model_ = new DataModel();
        
        Ptr<IResourceKey> key = ResourceKeyString::Create("ListBoxItemModelLayerStyle");
        Noesis::Gui::Style* style = gui_root_->FindResource<Noesis::Gui::Style>(key.GetPtr());
        list_box->SetDataContext(data_model_);
        list_box->SetItemContainerStyle(style);
When I add items, I do the following:
        data_model_->GetItems()->Add(new DataItem(_name.c_str()));
This creates a new DataItem with a ModelName property, which should be bound to the textbox. However, I cannot get this to work. If I use a DataTemplate rather than a Style, the binding works, but I have other problems.

Any ideas?
Thanks

Re: Binding to a TextBox inside a Style for a ListBoxItem

Posted: 30 May 2014, 11:17
by sfernandez
Hi,

First of all, if you use the ItemsSource property, the ListBox.Items collection is made read only and you cannot add/remove items from there, so you shouldn't specify any ListBoxItem children inside the ListBox. The following xaml is wrong (we will add the corresponding error message in a following version):
<ListBox ItemsSource="{Binding Items}" ...>
    <ListBoxItem .../>  <!-- WRONG -->
</ListBox>
Second, when you use the ItemsSource property, a ListBoxItem container is automatically created for each item, so if you want to specify a Style for these containers, you have to do it using ItemContainerStyle property:
<ListBox
    ItemsSource="{Binding Items}"
    ItemContainerStyle="{StaticResource ContainerStyle}" .../>
Next, to understand how data binding works in a ListBox I made this graphic representing the generated visual tree:
ListBox Visual Tree.png
As you can see, your DataItems are connected through the DataContext property to the root of the DataTemplate you specify in the ItemTemplate property. The ListBoxItem and its template elements know nothing about the DataItem, so they cannot have a Binding to DataItem properties. These bindings only work for elements in the ItemTemplate's DataTemplate.

Which problems do you have when using a DataTemplate?

Re: Binding to a TextBox inside a Style for a ListBoxItem

Posted: 30 May 2014, 16:40
by descala
Thank you for the very detailed explanation!

So it looks as though using a Style will not work for my purposes, as I need the fields bound to item properties.

Regarding DataTemplates, when I was using a DataTemplate containing a styled ToggleButton, the style wasn't being properly applied to the ToggleButton when I dynamically added items to the listbox.

Here is the DataTemplate I was using before (the ToggleButton Style below is ToggleVisibilityEye):

<DataTemplate x:Key="ModelLayerTemplate">
	<Grid HorizontalAlignment="Left" Height="60" Margin="0,0,0,0" VerticalAlignment="Top" Width="190">
		<Rectangle RadiusY="5" RadiusX="5" Fill="#BF95ACE5"/>
		<TextBox x:Name="ModelNameTextBox" Margin="32.5,5,0,0" TextWrapping="Wrap" Text="{Binding ModelName}" FontSize="12" Background="#FF5368A1" Foreground="#FFDADADA" Height="25" VerticalAlignment="Top" Width="130" HorizontalAlignment="Left" BorderBrush="{x:Null}"/>
		<ComboBox x:Name="ModelTypeComboBox" Height="24" Margin="32,0,0,5" VerticalAlignment="Bottom" Width="79" HorizontalAlignment="Left">
			<ComboBoxItem x:Name="ModelTypeBoxUpper" Content="Upper" IsSelected="True"/>
			<ComboBoxItem x:Name="ModelTypeBoxLower" Content="Lower"/>
			<ComboBoxItem x:Name="ModelTypeBoxBuccal" Content="Buccal"/>
		</ComboBox>
		<ToggleButton x:Name="ModelToggleVisibilityButton" Content="ToggleButton" HorizontalAlignment="Left" Margin="3,0,0,0" Style="{DynamicResource ToggleVisibilityEye}" Width="25" IsChecked="True" VerticalAlignment="Center" Height="25"/>
	</Grid>
</DataTemplate>
That Style uses a stroked path graphic to toggle the visibility of items referenced in the listbox. But when applying the template to dynamically added items, I see a default-styled ToggleButton with some text that appears to say "To" (as in ToggleButton maybe?), rather than the properly styled button.

Is there something I'm doing wrong there in using the DataTemplate?

Re: Binding to a TextBox inside a Style for a ListBoxItem

Posted: 30 May 2014, 20:20
by KeldorKatarn
I'm not sure if I remember this right but... can't you declare that DataTemplate as a resource, and then in the style itself reference that datatemplate, assigning it not directly but in the style... would that work?

Re: Binding to a TextBox inside a Style for a ListBoxItem

Posted: 01 Jun 2014, 10:58
by sfernandez
Regarding DataTemplates, when I was using a DataTemplate containing a styled ToggleButton, the style wasn't being properly applied to the ToggleButton when I dynamically added items to the listbox.
Is there something I'm doing wrong there in using the DataTemplate?
I was able to reproduce the problem. There is a bug with DynamicResource look up. We will fix it in the next release. Meanwhile you can use a StaticResource instead:
<DataTemplate x:Key="ModelLayerTemplate">
   <Grid HorizontalAlignment="Left" Height="60" Margin="0,0,0,0" VerticalAlignment="Top" Width="190">
      ...
      <ToggleButton x:Name="ModelToggleVisibilityButton" Content="ToggleButton" HorizontalAlignment="Left" Margin="3,0,0,0" Style="{StaticResource ToggleVisibilityEye}" Width="25" IsChecked="True" VerticalAlignment="Center" Height="25"/>
   </Grid>
</DataTemplate>

Re: Binding to a TextBox inside a Style for a ListBoxItem

Posted: 06 Jun 2014, 16:28
by descala
Thanks! I'll try the StaticResource approach for now.

Re: Binding to a TextBox inside a Style for a ListBoxItem

Posted: 01 Jul 2014, 14:41
by sfernandez
DynamicResource look-up solved in 1.1.9 version.