WPF GridViewRowPresenter in an ItemsControl - wpf

I'm just starting learning WPF and I'm trying to use a GridViewRowPresenter inside of an ItemsControl to essentially duplicate the functionality of a simple table in HTML. The ListView is not appropriate since it is interactive (which I don't want). I am binding to a generic List of objects of an unknown quantity.
I have a List of a custom object that has two string properties: FirstName and LastName. The following code works:
<ItemsControl Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=FirstName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
while this renders nothing:
<ItemsControl Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<GridViewRowPresenter>
<GridViewRowPresenter.Columns>
<GridViewColumnCollection>
<GridViewColumn DisplayMemberBinding="{Binding Path=FirstName}"></GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=LastName}"></GridViewColumn>
</GridViewColumnCollection>
</GridViewRowPresenter.Columns>
</GridViewRowPresenter>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I'm not sure where to go from here and I would greatly appreciate any help! Thanks!

If you want a non-interactive grid of items, you can use an ItemsControl with a Grid that uses shared size scope:
<ItemsControl ItemsSource="{Binding Items}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="FirstName"/>
<ColumnDefinition Width="*" SharedSizeGroup="LastName"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Grid.Column="1" Text="{Binding LastName}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
A more efficient approach would be to write your own Panel subclass that works similarly to Grid (you could probably subclass Grid) but automatically adds rows as necessary. Then use that Panel as the ItemsPanel for the ItemsControl.

Related

Binding to elements in WPF GridViewHeader doesn't work

I'm trying to create a custom header for a ListView / GridView where the user can easily filter items using Text inputs:
I've created DependencyProperties on the UserControl's code-behind and are binding the Xaml datacontext to itself (name=root). The binding i'm having trouble with is the TextBox inside the Header - it simply doesn't work. If i set a 'ModelVariantId' to "Whatever" it doesn't show and likewize i don't get any PropertyChanged events when writing in it..
I've found -some- similar sounding questions on the internet but nothing exactly matching.
<ListView ItemsSource="{Binding ElementName=root, Path=CombinedCarModels}">
<ListView.View>
<GridView>
<GridViewColumn Width="120" DisplayMemberBinding="{Binding ModelVariantId}">
<GridViewColumn.Header>
<GridViewColumnHeader HorizontalContentAlignment="Stretch">
<StackPanel>
<Label>ModelVariantId</Label>
<TextBox Text="{Binding ElementName=root,
Path=CarModelFilter.ModelVariantId, Mode=TwoWay}" />
</StackPanel>
</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
If you define a HeaderTemplate, this binding should work:
Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl},
Path=CarModelFilter.ModelVariantId, Mode=TwoWay}"
XAML:
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" HorizontalAlignment="Stretch">ModelVariantId</Label>
<TextBox Grid.Row="1" HorizontalAlignment="Stretch"
Text="{Binding ElementName=root, Path=CarModelFilter.ModelVariantId, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
There is no element named "root" in the same naming scope as the header, but you should be able to bind to a parent element in the visual tree using the RelativeSource property.
This assumes that the parent UserControl has a CarModelFilter property.

Items Control Binding not displaying anything?

For some reason, my XAML is not displaying anything for my Items Control. I am using a List of Students as my ItemsSource and referencing the properties of that List to create a custom control.
This is my XAML:
<ItemsControl ItemsSource="{Binding Class.Students}" Grid.Row="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="{Binding FavoriteSubject, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Right now, the drop-down is just empty. The bindings are correct and if I pull them outside the ItemsControl, they display fine. Is there something specific about ItemsControls that I am missing?

Listboxes hierarchy

I have 3 embedded listboxes for entities: group, item, subitem.
<ListBox Name="GroupItemsListBox"
ItemSource="{Binding EntityGroups"}>
<ListBox.ItemTemplate>
<DataTemplate>
<ItemsControl Name="ItemsListBox"
ItemSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl Name="SubItemsListBox"
ItemSource="{Binding SubItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name} />
</DataTemplate>
</ItemsControl.ItemTemplate>
</DataTemplate>
</ItemsControl.ItemTemplate />
</ItemsControl>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
Also there is a case when last listbox is empty.
I want to handle SelectedItem from the lowest existing ItemControl. So i will be able to manage when the most specific object is selected.
For example when i click on ItemGroup[1].Items[0].SubItem[2] i want to get this element, but not ItemGroup[1] or ItemGroup[1].Items[0].
How can i achieve it?
Use the LongListSelector instead. This supports grouping in a much nicer way.

Binding nested control using MVVM pattern

I have a problem with binding nested control with my MVVM pattern. This is my XAML code:
<ItemsControl Grid.Column="1" ItemsSource="{Binding NotificationContacts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:Expander>
<toolkit:Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ContactName}" Grid.Column="0" VerticalAlignment="Center"></TextBlock>
<Image Source="Images/answer_ok.png" Grid.Column="1" Margin="15,0,15,0" Width="27" Height="27"></Image>
</Grid>
</toolkit:Expander.Header>
<toolkit:Expander.Content>
<ListBox Margin="30,10,0,10" ItemsSource="{Binding NotificationContacts.Messages">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MessageName}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</toolkit:Expander.Content>
</toolkit:Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is, that the listbox control located in ExpanderControl Data Template is not being data bound.
Listbox control is populated by EntityCollection named 'Messages' which is contained in parent object 'NotificationContacts' that ItemsControl is databound with...
Does anyone know how to resolve this issue ?
Thanks in advance !!!
Did you try this:
<ItemsControl Grid.Column="1" ItemsSource="{Binding NotificationContacts}">
......
<ListBox Margin="30,10,0,10" ItemsSource="{Binding Messages}">
.....
<TextBlock Text="{Binding MessageName}"></TextBlock>
If I remember it right, when you are "inside" ItemContol, binding context is set to NotificationContacts. So use just "{Binding Messages}" could be fine.
And by the way, you are missing curly bracket on the line:
<ListBox Margin="30,10,0,10" ItemsSource="{Binding NotificationContacts.Messages">
Call ItemsControl f.i. "ic" and use next binding in ListBox
<ItemsControl x:Name="ic" Grid.Column="1" ItemsSource="{Binding NotificationContacts}">
...
<ListBox Margin="30,10,0,10" ItemsSource="{Binding ElementName=ic, Path=DataContext.Messages}">

MVVM Binding in Silverlight3 ItemsControl to get the parent controls DataContext

I have the following ItemsControl in Silverlight 3.
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="MyBtn" Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding ElementName=MyBtn, Path=Tag}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Problem is that I have the ItemsControl bound to the Collection in my ViewModel, but I need the button to trigger a command on the ViewModel which is of course not Available in the DataContext of the button since it only contains the collection.
I can make the command fire by setting my ViewModel as a Resource and then binding to it as a StaticResource, but I want to know why the element to element binding won't work in this scenario. I would prefer not to use the StaticResource binding because that requires the default constructor on the ViewModel and so I can't inject my data easily.
UPDATE
I'm working through this slowly... Looking at the suggestions from Peter I realized that I may have more serious binding issues because of my page setup. I have two possible road blocks, but first things first.
My Items control above is wrapped in another items control that is bound to an observable collection. I moved my items control so that its a direct child of the root items control. It was wrapped in another control that I'll get to. So I tried the element binding to the items control ControlItemList, but its a collection so it can't find my ButtonCommand method in that Collection. What I need to do is bind to an item within that collection. How do I bind to a single item within the collection?
<Grid x:Name="LayoutRoot" Background="White"><!-- DataContext="{Binding Path=., Source={StaticResource lvvm}}">-->
<StackPanel Orientation="Vertical">
<ItemsControl x:Name="ControlItemList" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="ControlName" Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center" />
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
So, Assuming I can get the above to work the other road block is that my items control is wrapped in another usercontrol that I'm using to get DataTemplateSelector type functionality. I think this control may be blocking me from getting to the parent DataContext. Is there a limit as to how far up the tree you can go?
<common:DeviceControlTemplateSelector Grid.Column="1" FieldType="{Binding ValueType}" Margin="0,2,0,2">
<common:DeviceControlTemplateSelector.StringTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, Mode=TwoWay}" Width="100"/>
</DataTemplate>
</common:DeviceControlTemplateSelector.StringTemplate>
<common:DeviceControlTemplateSelector.DateTimeTemplate>
<DataTemplate>
<TextBox Text="this is date time binding" Width="100"/>
</DataTemplate>
</common:DeviceControlTemplateSelector.DateTimeTemplate>
<common:DeviceControlTemplateSelector.BooleanTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Value, Mode=TwoWay}" />
</DataTemplate>
</common:DeviceControlTemplateSelector.BooleanTemplate>
<common:DeviceControlTemplateSelector.IntegerTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ValueCollection}" DisplayMemberPath="Value" SelectedIndex="{Binding Value, Mode=TwoWay}" >
</ComboBox>
</DataTemplate>
</common:DeviceControlTemplateSelector.IntegerTemplate>
<common:DeviceControlTemplateSelector.MultiButtonTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="MyBtn"
Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=ControlItemList, Path=DataContext.ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</common:DeviceControlTemplateSelector.MultiButtonTemplate>
<Button x:Name="MyBtn"
Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=ControlItemList, Path=ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks again everyone for your help!
the best solution i have found so far to deal with this situation is by Dan Wahl where he uses a DataContextProxy.
http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
Try to change
from
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=ButtonCommand}"
to
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=DataContext.ButtonCommand}"
OR
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot.DataContext, Path=ButtonCommand}"
In MVVM messaging is a strong concept for cummunication between ViewModels. You could use PRISM Eventaggregator or the Messenger class of MVVM Light Toolkit. On the button command you can publish a message and subscribe to it in the viewmodel.

Resources