using a datatemplate for a list of listviewitems - wpf

I've created a listview. The listview's source is set to a list of "listviewitem"s. The listviewItem's content is set to the specific class that I need data from.
Somehow, the datatemplate from my xaml document is not recognised which results in a default template instead of the one I defined.
My template looks like this:
<DataTemplate x:Key="Tiled">
<StackPanel Height="100" Width="90">
<Grid Width="70" Height="70" HorizontalAlignment="Center">
<Image Source="{Binding Path=Content.Icon}" Margin="6,6,6,9"/>
</Grid>
<TextBlock Style="{Binding Path=Content.Name}" FontSize="13"
HorizontalAlignment="Center" Margin="0,0,0,1" />
</StackPanel>
</DataTemplate>
I know that I can also set the source of the list to a collection of objects instead of listviewitems, but I need the contextmenu from the listviewitems.

I know that I can also set the source of the list to a collection of objects instead of listviewitems, but I need the contextmenu from the listviewitems.
No you don't, that's what the ItemContainerStyle is for.
What you by the way should see in the output window:
System.Windows.Data Error: 26 : ItemTemplate and ItemTemplateSelector are ignored for items already of the ItemsControl's container type; Type='ListBoxItem'

Related

ComboBox/ListBox - an undocumented feature?

Today, thanks to a typing mistake, I've managed to produce the following xaml:
<ListView>
first line<Button>second line</Button>the third<system:String>the fourth</system:String>
</ListView>
This will compile and produce a four-line list:
This also works for ListBox, ItemsControl, and ComboBox.
My question: do you know if that is documented anywhere by Microsoft? I've never seen such xaml working before.
All the items you are describing inherit from ItemsControl. The ItemsControl MSDN documentation has an example with multiple types of controls added to a ListBox very similarly to how you have your items added to your ListView.
Relevant XAML from MSDN:
<!--Create a ListBox that contains a string, a Rectangle,
a Panel, and a DateTime object. These items can be accessed
via the Items property.-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
Name="simpleListBox">
<!-- The <ListBox.Items> element is implicitly used.-->
This is a string in a ListBox
<sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
<Rectangle Height="40" Width="40" Fill="Blue"/>
<StackPanel Name="itemToSelect">
<Ellipse Height="40" Fill="Blue"/>
<TextBlock>Text below an Ellipse</TextBlock>
</StackPanel>
<TextBlock>String in a TextBlock</TextBlock>
<!--</ListBox.Items>-->
</ListBox>

WPF hyperlink in datatemplate listboxitem

I have a data template in WPF that is attached to a ResultsViewModel class and renders the VM in a table format. A bunch of these make up ListBoxItems in a ListBox. What I want is in each individual table to have a little X at the top right of the border where if you click it, it calls a function that removes that item from the listbox.
I have tried with a Hyperlink and event OnClick but then I have to have the DataTemplate in the main XAML and not in a resource dictionary as it needs a x:Class tag to use events, but then the event gets fired in the MainViewModel, which isn't the worst thing in the world as the observable list is held in the MainViewModel and needs to be removed at that point anyway, but I can't figure out how to get a reference to the ResultsViewModel of the list box item that contained the data template that was clicked
<DataTemplate x:Key="ErroredResultsTemplate" DataType="x:Type vm:ResultsViewModel" >
<Border x:Name="Border" BorderBrush="{StaticResource ResultProcessedBorder}" Background="{StaticResource ResultFill}" BorderThickness="4" CornerRadius="10" Margin="6" Padding="5" Width="110" Height="110">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="83" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" HorizontalAlignment="Right" VerticalAlignment="Top">
<Hyperlink Click="Close_Results">X</Hyperlink>
</TextBlock>
<TextBlock Width="90" Text="An error occurred calculating results" TextWrapping="Wrap" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top" TextAlignment="Center" />
</Grid>
</Border>
</DataTemplate>
You can achieve this in two ways:
Create a property of type ResultsViewModel in your parent view model (that contains your collection of ResultsViewModel objects) and bind that to the SelectedItem property of your ListBox. Add some kind of RelayCommand to the parent view model to handle the delete action, add a Button to your DataTemplate and bind its Command property to the new command. Then, when any delete button is clicked, you can just remove the item found in the SelectedItem property from your collection and the UI should update accordingly (assuming that you've implemented the INotifyPropertyChange interface).
You can simply bind from the DataTemplate of each item in the ListBox to the parent view model directly. This assumes that you have a Command in your parent view model named Delete and that the parent view model is bound to the DataContext property of the Window or UserControl that the ListBox appears in. Also note the important CommandParameter="{Binding}" part which passes the data object from each item in the collection to the object parameter in the Command when a Command is called.
Example:
<Button Content="X" Command="{Binding DataContext.Delete,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type
XmlNameSpace:WindowOrUserControlName}}, Mode=OneWay}"
CommandParameter="{Binding}" />

How to override the ItemsSource defined for an ItemTemplate?

I use an Item Template to define how the rows of my grid must be displayed. The grid definition (simplified) shows that the item template source is GridRows (a collection of rows) :
<grid ...>
(...)
<ScrollViewer
ItemTemplate="{StaticResource GridRowItemDataTemplate}"
ItemsSource="{Binding GridRows}" />
</ScrollViewer>
</grid>
So far, so good.
In the item template, the textbox is bound to ImportZoneName, which resolved of course to GridRows[i].ImportZoneName, and this is exactly what I want :
<DataTemplate x:Key="GridRowItemDataTemplate">
<Grid>
<TextBlock {Binding ImportZoneName}" />
<ComboBox
SelectedItem="{Binding SelectedModelingTypeValue}"
ItemsSource="{Binding ModelingTypes}" />
</Grid>
</DataTemplate>
Now the problem : I also want to bind the combo box to an other property (ModelingTypes) of my view model. This property is not linked in any way to GridRows. How can I tell WPF to override (or forget) the item template source?
Many, many thanks !
BTW, I did not find yet a simple guide for these simple binding cases... If anyone have a link to such a guide, I will bless him/her forever :)
You can get the DataContext of the parent list like this:
<DataTemplate x:Key="GridRowItemDataTemplate">
<Grid>
<TextBlock {Binding ImportZoneName}" />
<ComboBox
SelectedItem="{Binding SelectedModelingTypeValue}"
ItemsSource="{Binding DataContext.ModelingTypes,
RelativeSource={RelativeSource FindAncestor,
AncestorType=Grid}}" />
</Grid>
</DataTemplate>
Replace Grid with the type of the grid you are using (not sure which it is, not evident from the question), as long as its DataContext has the property ModelingTypes

Binding the content of a ListBoxItem to something not related to

I'm new to WPF, but am pretty familiar with binding list box controls to observable collections in the view model.
In my current project we have a ListBox that is used for navigating to different pages in a frame box. I want to add some display information to the first ListBoxItem to show which object (in this case, the Scenario) is being worked on (it is selected in a previous frame that visible in the subsequent frames). The ListBox itself is using a static list defined in the xaml, so it isn't bound to anything in the ViewModel. The CurrentScenario is a property on the ViewModel. I was able to add a Label to the same window that contains this ListBox and successfully bind CurrentScenario.Id to its content, and it updated correctly, so I know that the path in the Binding statement should resolve correctly.
<ListBox
Style="{StaticResource FunctionBackground}"
IsSynchronizedWithCurrentItem="True"
>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}">
<ListBoxItem.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Scenario" />
<TextBlock Grid.Row="1" Text="{Binding Path=CurrentScenario.Id}"/>
</Grid>
</DataTemplate>
</ListBoxItem.ContentTemplate>
</ListBoxItem>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}" >Parameter</ListBoxItem>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}" >Run</ListBoxItem>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}" >Results</ListBoxItem>
</ListBox>
When I try to add this extra information to the listbox item, from what I can tell, the list box item has an empty text block below the text block with the word "Scenario." I can't figure out why the empty text box content is not showing the value of the bound property. When I put a normal string in the Text property of the second text block, it shows up correctly.
I imagine that either ListBoxItem content is only set up be bound to properties related to the ItemSource, and so it ignores attempts to bind to other things, or maybe there is something fundamental in WPF that I am missing. Or both...
Thanks if anyone has any ideas!
So if property CurrentScenario is in ViewModel you can use RelativeSource to binding to this property.
...
<TextBlock Grid.Row="0" Text="Scenario" />
<TextBlock Grid.Row="1" Text="{Binding Path=DataContext.CurrentScenario.Id, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
...

How to modify silverlight combobox data display

I have a combobox defined as follows:
<ComboBox x:Name="cboDept" Grid.Row="0" Margin="8,8,8,8" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Source={StaticResource cvsCategories}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Width="Auto" Height="Auto">
<sdk:Label Content="{Binding CategoryID}" Height="20" />
<sdk:Label Content="{Binding CategoryName}" Height="20" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
It works fine. However, once I select an item in the list, I want a different template to be applied to the combobox selected item being shown to the user (the item shown after the disappearance of popup). In the above case, I want only CategoryName to be displayed in the ComboBox once I select the respective item.
Can anyone let me know on how to achieve this?
thanks
What you need to do is create a ResourceDictionary containing a few defined templates yourself. In the below, ComboBoxTemplateOne and ComboBoxTeplateTwo are user controls that are set out to display the combobox in the manor you want.
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="TemplateOne">
<local:ComboBoxTemplateOne />
</DataTemplate>
<DataTemplate x:Key="TemplateTwo">
<local:ComboBoxTemplateTwo />
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
You will then need to create your own class that inherits from ContentControl "DataTemplateSelector", overriding OnContentChanged
Protected Overrides Sub OnContentChanged(ByVal oldContent As Object, ByVal newContent As Object)
MyBase.OnContentChanged(oldContent, newContent)
Me.ContentTemplate = SelectTemplate(newContent, Me)
End Sub
You will then need to create another class that inherits from the above DataTemplateSelector which overrides SelectTemplate ("TemplateSelectorClass"), which will return the DataTemplate defined above ("TemplateOne" or "TemplateTwo").
Also in this derived class, you will need to define a property for each of the templates you have
Public Property ComboboxTemplateOne As DataTemplate
Then head back to your XAML and n the blow XAML
<local:TemplateSelectorClass ComboboxTemplateOne="{StaticResource TemplateOne}" Content="{Binding Path=ActiveWorkspace}>
This should work, as it is effectively doing the same work as setting the "DataTemplate" property in WPF (which doesn't exist in SilverLight)
I realise there are a fair few steps here and its quite fiddly, but hopefully this will get you there. Any questions just shout.

Resources