WPF DataTemplate for Collection - wpf

I must now hang my head, for I have scoured the Google for hours and still have no clue what I'm doing wrong.
<DataTemplate DataType="{x:Type local:Controllers}">
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=Port}" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
What I'm trying to do is to display an arbitrary number of controller objects in this list. "Controllers" is just an alias for "List<Controller>". "Port" is a property of each "Controller" object, but of course that's not showing up on the list. Items are being correctly added to the collection the list is based on (the collection is actually stored as the Content property of the ContentControl that is displaying this collection of objects), but no item in the collection itself is being displayed.
I thought at first that it might be an update issue--that the collection was being correctly displayed in its initial, empty state, but that isn't the case; if I start with the collection already populated, I still get no items.
Help me, Obi Wan. :(

You need to bind something to the ListBox. That something is just {Binding}, which refers to the instance of the DataTemplates DataType that is passed in at run-time.
<DataTemplate DataType="{x:Type local:Controllers}">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=Port}" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>

Related

Bound String List displays containing class name not the string

My Problem is the following:
I have a class which has a ObservableCollection of strings, which gets populated by the selected children.
Those are supposed to be listed in a ListBox.
But the strings aren't listed, rather the object type in FilterList is shown.
<ListBox Grid.Row="2" Grid.Column="0"
ItemsSource="{Binding FilterList}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"
Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ListOfActiveFilters}">
<!--<ContentPresenter Content="{Binding ListOfActiveFilters.}"/>-->
<TextBlock Text="{Binding}" />
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Filter List contains the classes which have ListOfActiveFilters as a Property.
I thought that it would work the way I showed it, but it doesn't.
Since that is also the way I saw others do it.
If I were to use a Class with a single string Property as the type of the Collection instead of the Collection of strings I have right now, I think it would work.
I just don't really see the point in creating a class which holds a string property just so that I can bind the ContentPresenter or TextBox to that property.
What am I doing wrong? I am rather new to the topic.
One single ListBox will only be able to display the filters in one single ListOfActiveFilters collection. You could use nested ItemsControls to display them all:
<ItemsControl ItemsSource="{Binding FilterList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding ListOfActiveFilters}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The other option is to modify your data model and put all strings in a single collection. You could then bind the ItemsSource property of the ListBox directly to this one. But a ListBox itself has no concept of hierarhical data.

Binding ListBox Items using Templates

I am going crazy trying to figure this out without success.
I have a DependencyObject, ("UserObject"). It has a "DataItems" DependecyProperty that is an ObservableCollection. "UserDefiniton" is a DependencyObject with a DependencyProperty of "Data". Data has two properties: DataType (an enumeration) and Value (a string).
I am trying to define a ListBox in XAML that uses the "DataItems" property as its ItemsSource. In the ItemTemplate, I have several different controls. For simplicity of this issue, I am using a CheckBox and a TextBox. I want CheckBox to be available and visible when DataType is 0, while I want the TextBox to be available and visible when the DataType is 1. Only one control can be available and visible at a time.
This works:
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=0}"
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
<TextBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}"
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<Listbox.ItemTemplate>
</ListBox>
The problem is that even though only one is visible, both are fighting over the Data.Value property (the boolean of the checkbox will show in the textbox, even though the checkbox is hidden).
Basically, though, the binding in this case is working--but the implementation is incorrect.
So, I switched to using Templates. The problem I am having is that I can't get the binding to work.
This is the code that I have for the Template. The Template selector is working correctly, but the Text property of the TextBox and the IsChecked property of the checkbox are not binding to Data.Value:
<DataTemplate x:Key="TextBoxItem">
<TextBox
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</DataTemplate>
<DataTemplate x:Key="CheckBoxItem">
<CheckBox
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
</DataTemplate>
...
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl
Content="{Binding Path=Data.DataType, Mode=OneWay}"
ContentTemplateSelector="{DynamicResource UserDefinitionTemplateSelector}"/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
So how do I fix the binding?
Content should be set to {Binding}, since the Content will be the DataContext of the data-templates, hence you should just pass on the current DataContext. If you need to pass specific data to the template selector you can just drill down in the whole object.
There also is a template selector on the level of the ListBox, so you do not really need the internal ContentControl.
(You might also be interested in generic methods of debugging data bindings.)

Access property of DataContext inside ItemTemplate

I have a really nasty problem with bindings. I know that there are other topics regarding binding itmes inside itemtemplate to datacontext of an object outside the template. However, this just won't work, i.e. the first textblock display 'Test' as desired whereas the same textbox inside the itemtemplate shows nothing.
<TextBlock Text="{Binding DataContext.Test, ElementName=myList}"/>
<ItemsControl x:Name="myList" ItemsSource="{Binding AllItems}"
Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal"
ItemHeight="170" ItemWidth="140"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image x:Name="{Binding KeyName}"
Source="{Binding ImagePath}"
Width="128"
Height="128">
</Image>
<TextBlock Text="{Binding DataContext.Test, ElementName=myList}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I would appreciate some help here folks as this is really a problem for me.
Inside the itemtemplate, the binding is initialized to the context of the current item in AllItems.
Update
Outside of the ItemTemplateyour bindings are relative to the DataContext of the page.**
Once inside an ItemTemplate then bindings are limited to the scope of the item specifically being evaluated at that time.
So, if we assume the following (based on the code in your question):
<ItemsControl x:Name="myList" ItemsSource="{Binding AllItems}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="tb1"
Text="{Binding DataContext.Test, ElementName=myList}"/>
<TextBlock x:Name="tb2" Text="{Binding KeyName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
tb1 cannot access the DataContext object directly.
tb2 cann access KeyName - assuming that whatever object AllItems is an IEnumerable of contains a property with that name.
As I understand it, inside an itemtemplate, the item past from the enumeration controls the binding source and this can't be overridden (by setting ElementName or otherwise).
If you need the value from Test in every object in your enumeration then you'll need to add it as a property of the object in the enumeration.
I'm sure someone more knowledgeable than me could explain why this is or give a better explanation but that's the gist of it.
** Assuming no other nesting of ItemsControls (or equivalent)

WPF Multiple master/details, same grid

I have a TreeView which has three levels.
Lets say its a league, division and team TreeView.
Now, when I select each of the items in the tree, I would like to see detailed information about it.
Whats the best way to achieve this?
Since Grid doesn't items (like a ListBox), I can't just set its ItemsSource and make a DataTemplate...
I thought about using a ListBox which will contain only the selected item but this seems to be very bad...
Thanks.
You first define the 3 DataTemplates for your league, division and team classes. After, you bind the TreeView to the root of your objects. Your League and Division classes should have a Children property that returns the children. All your classes should have a Name property.
Then when you want to show a single object, use the ContentPresenter, and the bind its content to the SelectedItem if the TreeView.
For example:
<StackPanel>
<StackPanel.Resources>
<DataTemplate DataType="{x:Type your_namespace:League}">
<StackPanel Orientation="Vertical">
<TextBlock Text={Binding Name}/>
<.../>
<StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type your_namespace:Division}">
<StackPanel Orientation="Vertical">
<TextBlock Text={Binding Name}/>
<.../>
<StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type your_namespace:Team}">
<StackPanel Orientation="Vertical">
<TextBlock Text={Binding Name}/>
<.../>
<StackPanel>
</DataTemplate>
</StackPanel.Resources>
<TreeView x:Name="_tree" ItemsSource="{Binding RootOfYourItems}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text={Binding Name}/>
</HierarchicalDataTemplate>
</TreeView>
<ContentPresenter Content="{Binding Path=SelectedItem, ElementName=_tree}" />
</StackPanel>
This code was not tested or compiled, it just provided as an example.
I would create a viewmodel with properties for the tree structur, the current selection and the details to the current selection.
The tree struct is one-way bound to the treeview, the SelectedItem from the treeview is OneWayToSource bound to the current selection property (due to the limitations of the property). This property changes the list of child items once the current selection changes, and the child items are bound to the listbox displaying them.

WPF: Problem with ItemsControl datatemplate as a user control

This works fine:
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But, I want to make use of a User Control in place of the stack panel with two TextBlocks using something like:
<DataTemplate>
<jasControls:NameView/>
</DataTemplate>
But, with this and a million other syntaxes, the NameView shows nothing, yet it works fine in a separate test, outside the ItemsControl, with explicitly set (dependency) property - e.g.
<jasControls:NameView FName="{Binding Path=Person.FirstName}" />
I can find hardly any examples of this anywhere, and need to know how to specify the properties of the user control - or how can the user control 'receive' the individual item type (a Person)? What syntax to use? Do I need to specify the datatype? Is ItemsControl causing a problem or should any similar control do e.g. ListBox? Can I design it so that the user control gets an entire Person object? What dependency properties do I need in the user control? In short, how to get data into the user control?! The actual business types involved will be a bit more complicated involving some logic in the user control, hence my desire to use a user control.
ANY direction on this will be very gratefully received - TIA
Assuming your Persons collection has many Person objects in it, the DataContext property of your NameView control will automatically be set to the Person object.
You wouldn't need any dependency properties to achieve this feat. Your NameView usercontrol would simply be:
<UserControl ...>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</UserControl>
You should have no codebehind to get this information to display.
From there, you should be able to access the Person object from the DataContext property:
Person person = this.DataContext as Person;

Resources