WPF Simple table layout - c#

I'm looking to create a simple layout for a WPF project I'm working on.
I tried styling Datagrid and GridView's but none of them work as I want, plus I don't want items to be editable / selectable, or columns to be sorted or anything like that. Basically I just want a simple dynamic table layout with no bells and whistles.
Any advice on how to recreate this would be greatly appreciated.
Update: I need the number of rows to be dynamic based on an ObservableCollection

Use HeaderedItemsControl, XAML
<!-- templates -->
<DataTemplate x:Key="itemWithDeleteButton">
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=DocumentName, Mode=OneWay}" />
<Button Grid.Column="1" Command="{Binding DeleteCommand}"/>
</Grid>
</DataTemplate>
<Style TargetType="{x:Type HeaderedItemsControl}" x:Key="DeletedGrid">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedItemsControl}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Document Name"
VerticalAlignment="Center"
FontWeight="Bold"/>
<TextBlock Grid.Column="1" Grid.Row="0" Text="Actions"
VerticalAlignment="Center"
FontWeight="Bold"/>
<Grid Grid.Row="1" Grid.ColumnSpan="2" Width="Auto" Height="Auto" Background="White">
<ItemsPresenter/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<!-- control -->
<HeaderedItemsControl Style="{StaticResource DeletedGrid}" Margin="0,0,0,10"
Grid.Row="4" Grid.ColumnSpan="2" ItemTemplate="{StaticResource itemWithDeleteButton}"
ItemsSource="{Binding GridData}">
ViewModel
public class GridItem
{
public string DocumentName { get; set; }
public ICommand DeleteCommand { get; set; }
}
public class MyViewModel
{
public ObservableCollection<GridItem> GridData { get; set; }
}

This is just something similar. For the second column you would probably use a button for Delete of maybe just click event on a TextBlock. To get that exact formatting is going to take some tweaking.
<ListView.View>
<GridView AllowsColumnReorder="False" x:Name="gvCurDocFields">
<GridViewColumn Width="120">
<GridViewColumnHeader Content="Field" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Text="{Binding Path=FieldDefApplied.FieldDef.DispName, Mode=OneWay}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumnHeader Content="Value" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Left" Margin="0" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Text="{Binding Path=DispValue, Mode=OneWay}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>

You can use the Grid layout for this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" /> <!-- Header row -->
<RowDefinition Height="Auto" /> <!-- One for each row of data -->
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- Document Name column -->
<ColumnDefinition Width="200" /> <!-- Actions column -->
</Grid.ColumnDefinitions>
</Grid>

Related

How to create binding for ListBox with TextBox in ListItemTemplate

I have a simple View Model class (I'm omitting the INPC implementation for brevity):
public class MyViewModel : INotifyPropertyChanged
{
public int ID { get; set; }
public ObservableCollection<int> Items { get; }
public int SelectedItemIndex { get; set; }
}
My problem is with my XAML. The above class is contained in another ObservableCollection in the parent object, so it's coded as a DataTemplate:
<DataTemplate x:Key="MyDataTemplate" DataType="x:MyViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Target="{Binding ElementName=ID}">
<Bold>ID:</Bold>
</Label>
<TextBox Grid.Column="1"
x:Name="ID"
Height="25"
Text="{Binding Path=ID}"
VerticalAlignment="Top" />
<Label Grid.Column="2"
Target="Items:">
<Bold>Rooms:</Bold>
</Label>
<Grid Grid.Column="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
ItemsSource="{Binding Path=Items}"
SelectedIndex="{Binding Path=SelectedItemIndex, Mode=TwoWay}"
x:Name="Items">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type system:Int32}">
<TextBox Text="{Binding Path=., Mode=TwoWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Column="1"
Grid.Row="0"
Content="{Binding Path=AddButtonCaption}"
Command="local:EditorCommands.AddElement"
CommandParameter="{Binding}"
Height="25"
VerticalAlignment="Top" />
<Button Grid.Column="1"
Grid.Row="1"
Content="{Binding Path=DeleteButtonCaption}"
Command="local:EditorCommands.DeleteElement"
CommandParameter="{Binding}"
Height="25"
VerticalAlignment="Top" />
</Grid>
</Grid>
</DataTemplate>
There are a few problems with this XAML:
The TextBox does not occupy the full width of the ListBox.
The binding on the same TextBox isn't working.
How do I fix these problems?
1) Give your ListBox ItemContainerStyle property a style and set its HorizontalContentAlignment property:
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
2) Are you sure? Seems to be working fine for me. If you want it to update as you type then set the binding's UpdateSourceTrigger property to PropertyChanged:
<TextBox Grid.Column="1"
x:Name="_ID"
Height="25"
Text="{Binding Path=ID, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top" />
Note that I've changed the TextBox's x:Name to "_ID" here because it was causing a conflict with the way I'm testing it (ID is also a property in the Window. Could also be confusing whatever framework is handling your bindings, so if the above doesn't work then try that as well.

How to prevent TextBlock taking all the available space

Here is my problem. I've built the following ListView
<ListView Grid.Row="1" x:Name="messageList" BorderThickness="0"
ItemsSource="{Binding MySource}"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<WrapPanel Grid.Row="0" Grid.Column="0">
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Received.SenderId}"
/>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Received.DeliverDate}"
/>
</WrapPanel>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Received.MsgText}"
Grid.Row="1" Grid.Column="0"
TextWrapping="WrapWithOverflow">
</TextBlock>
<WrapPanel Grid.Row="0" Grid.Column="1">
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Sent.SenderId}"
/>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Sent.DeliverDate}"
/>
</WrapPanel>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Sent.MsgText}"
Grid.Row="1" Grid.Column="1"
TextWrapping="WrapWithOverflow">
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
For each element of MySource only 1 between Received and Sent will be not null. The ListViewis working as i expect, placing the element on the left or the right of the screen, but the TextBlock that contain the MsgText get all the available space (so the whole row) if the message is too long. How can i limit it to stay only in one half of the parent Grid and make the text overflow eventually?
EDIT: I added an image showing my problem. The 4th message should overflow, but it doesn't
It took me few minutes to produce mcve, but the missing part of the puzzle is this:
<Grid MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}">
That would limit maximum width of all Grids (each item has one) and let wrapping happens. Otherwise Grid is asking for a width to fit whole item in one line and ListView is ok with it, but then you will see horizontal scrollbar (the most hated control by users including me).
MCVE:
public partial class MainWindow : Window
{
public class Item
{
public string Received { get; set; }
public string Sent { get; set; }
}
public List<Item> Items { get; }
public MainWindow()
{
InitializeComponent();
Items = new List<Item>
{
new Item { Received = "1111 111 11 111 11 1" },
new Item { Received = "2222 2 22 2 2 222222222 2 222222 22222222 222222222222 2" },
new Item { Sent = "333333333 3333333 333 33333 3 3 33 333333333 3333" },
new Item { Received = "444444444444444 444 44444444444444 44 4 44444444444444444 4 4 4444444444 4 444 444444444444" },
};
DataContext = this;
}
}
Screenshot:
Xaml:
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Received}"
TextWrapping="WrapWithOverflow" />
<TextBlock Grid.Column="1"
Text="{Binding Sent}"
TextWrapping="WrapWithOverflow" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
The problem is that your grid (and the width of the column) is calculated for each element of your list, so it takes the most space possible. I propose a backup solution, I did not find better but the result is there.
This is to place 2 ListView in 2 columns of a grid and then have the Received element in one and the Sent in the other.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListView Grid.Column="0" x:Name="messageListReceived" BorderThickness="0"
ItemsSource="{Binding MySource}"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<WrapPanel Grid.Row="0">
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Received.SenderId}"
/>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Received.DeliverDate}"
/>
</WrapPanel>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Received.MsgText}"
Grid.Row="1"
TextWrapping="WrapWithOverflow">
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Grid.Column="1" x:Name="messageListSent" BorderThickness="0"
ItemsSource="{Binding MySource}"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<WrapPanel Grid.Row="0">
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Sent.SenderId}"
/>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Sent.DeliverDate}"
/>
</WrapPanel>
<TextBlock Margin="2"
VerticalAlignment="Center"
Text="{Binding Sent.MsgText}"
Grid.Row="1"
TextWrapping="WrapWithOverflow">
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

ItemsControl different template based on Content

Could some identify why the following is not working please, I am trying to implement a Chat Message window where each Message will render a different style dependent on the MessageDirection. For this I am using a ItemsControl which is bound to the Messages property
In the ChatWindow class I have the following
public static readonly DependencyProperty MessagesProperty = DependencyProperty.Register(
"Messages",
typeof(ObservableCollection<Message>),
typeof(ChatWindow),
new PropertyMetadata(null));
public ObservableCollection<Message> Messages
{
get
{
return (ObservableCollection<Message>)this.GetValue(MessagesProperty);
}
set
{
this.SetValue(MessagesProperty, value);
}
}
I have the following defined in a ResourceDictionary
<ScrollViewer x:Name="srcMessages" Margin="0,0,0,0" VerticalScrollBarVisibility="Visible">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=Messages, RelativeSource={RelativeSource AncestorType={x:Type chat:ChatWindow}}}" x:Name="Messages">
<ItemsControl.ItemTemplate>
<DataTemplate>
<chat:MessageContentPresenter Content="{Binding}">
<chat:MessageContentPresenter.InboundTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="94" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="Username One" FontSize="10" Foreground="#adadad"></TextBlock>
<TextBlock Text=" - " FontSize="10" Foreground="#adadad"></TextBlock>
<TextBlock Text="11:45 AM" FontSize="10" Foreground="#adadad"></TextBlock>
</StackPanel>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18" />
<ColumnDefinition Width="65" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="230" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="1" Height="60" Width="60" Source="pack://siteoforigin:,,,/Resources/noavatar.png" StretchDirection="Both" Stretch="Fill">
<Image.Clip>
<EllipseGeometry Center="30,30" RadiusX="30" RadiusY="30" />
</Image.Clip>
</Image>
<Polygon Grid.Column="3" Points="0,0 -4,3 0,6 0,0" StrokeThickness="2" HorizontalAlignment="Right" VerticalAlignment="Center" Fill="#d8d7dc" Stroke="#d8d7dc"></Polygon>
<Border Grid.Column="4" BorderBrush="#d8d7dc" BorderThickness="1" Background="#d8d7dc" Padding="5">
<TextBlock TextWrapping="Wrap" Text="This is a message in a chat window..." VerticalAlignment="Top" FontSize="12" Foreground="#000000"></TextBlock>
</Border>
</Grid>
</Grid>
</DataTemplate>
</chat:MessageContentPresenter.InboundTemplate>
<chat:MessageContentPresenter.OutboundTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="94" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock Text="Username One" FontSize="10" Foreground="#adadad"></TextBlock>
<TextBlock Text=" - " FontSize="10" Foreground="#adadad"></TextBlock>
<TextBlock Text="11:45 AM" FontSize="10" Foreground="#adadad"></TextBlock>
</StackPanel>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="230" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="65" />
<ColumnDefinition Width="18" />
</Grid.ColumnDefinitions>
<Image Grid.Column="4" Height="60" Width="60" Source="pack://siteoforigin:,,,/Resources/noavatar.png" StretchDirection="Both" Stretch="Fill">
<Image.Clip>
<EllipseGeometry Center="30,30" RadiusX="30" RadiusY="30" />
</Image.Clip>
</Image>
<Polygon Grid.Column="2" Points="0,0 4,3 0,6 0,0" StrokeThickness="2" HorizontalAlignment="Right" VerticalAlignment="Center" Fill="#4fcd00" Stroke="#4fcd00"></Polygon>
<Border Grid.Column="1" BorderBrush="#4fcd00" BorderThickness="1" Background="#4fcd00" Padding="5">
<TextBlock TextWrapping="Wrap" Text="This is a message in a chat window..." VerticalAlignment="Top" FontSize="12" Foreground="#000000"></TextBlock>
</Border>
</Grid>
</Grid>
</DataTemplate>
</chat:MessageContentPresenter.OutboundTemplate>
</chat:MessageContentPresenter>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
And the MessageContentPresenter is as below
public class MessageContentPresenter : ContentControl
{
#region Public Properties
public DataTemplate InboundTemplate { get; set; }
public DataTemplate OutboundTemplate { get; set; }
#endregion
#region Methods
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
var message = newContent as Message;
if (message.Direction == MessageDirection.Inbound)
{
this.ContentTemplate = this.InboundTemplate;
}
else
{
this.ContentTemplate = this.OutboundTemplate;
}
}
#endregion
}
When I run this code all works fine and the OnContentChanged method is executed once for each item in the Messages collection. The issue is that both the InboundTemplate and the OutboundTemplate are null.
I really cannot see what the problem is so and why the two templates are both null, any help greatly appreciated.
Indeed, you can use DataTemplateSelector.
But another option is to use the DataTemplate + ContentControl and Style:
First, drop your MessageContentPresenter class as we do not need it. Consequently, you need to remove your ItemsControl.ItemTemplate from your XAML.
Secondly, add DataTemplate + ContentControl and Style to your Window.Resources.
Try this to give you some lead:
<DataTemplate DataType="{x:Type local:Message}">
<ContentControl Content="{Binding Direction}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Direction}" Value="{x:Static local:MessageDirection.Inbound}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Background="Green">Inbound</Button>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Direction}" Value="{x:Static local:MessageDirection.Outbound}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Background="Red">Outbound</Button>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>

Bind list of models to items in ScrollViewer

<ScrollViewer Width="600"
Grid.Row="1"
PanningMode="Both"
extensions:TouchScrolling.IsEnabled="True"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Disabled">
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Width="175"
Grid.Row="0"
Grid.Column="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="170" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" >
<Grid MinWidth="{Binding ElementName=backgroundImage, Path=ActualWidth}" >
<StackPanel HorizontalAlignment="Center"
Orientation="Horizontal" >
<Image Height="160"
Source="man.png" />
</StackPanel>
</Grid>
</Grid>
<TextBlock TextWrapping="Wrap"
Grid.Row="1"
FontSize="10"
Text="Bomber jacket..." />
</Grid>
</Border>
<Border Width="175"
Grid.Row="0"
Grid.Column="1">...
</Border>
<Border Width="175"
Grid.Row="0"
Grid.Column="2">...
</Border>
<Border Width="175"
Grid.Row="0"
Grid.Column="3">...
</Border>
</Grid>
</ScrollViewer>
I have above xaml. In scrollViewer i have some items. I want to initialize it with data from database. How to do this?
I need to screate some ItemControl for common XAML and bind to properties. But how can i manage to bind every item in list to be binded to that ItemControl ?
I found this topic where i understand how to build same xaml for list items, but one problem is there - How to set Grid.Column for each item?
An ItemsControl is used to draw a UI for a collection of items.
So to start with, you need your collection of items from the database.
public ObservableCollection<MyItem> MyCollection { get; set; }
To draw this using an ItemsControl, you would use XAML like this :
<ScrollViewer Height="100">
<ItemsControl ItemsSource="{Binding MyCollection}" />
</ScrollViewer>
This will use the default UI for an ItemsControl, which is to loop through each item in the collection, and for each one render a TextBlock with the Text property displaying the .ToString() of the item.
So what actually renders is something like
<ScrollViewer>
<StackPanel>
<TextBlock /> <!-- DataContext is MyCollection[0] -->
<TextBlock /> <!-- DataContext is MyCollection[1] -->
<TextBlock /> <!-- DataContext is MyCollection[2] -->
</StackPanel>
</ScrollViewer>
The ItemsControl lets you modify different parts of the template though.
For example, to change the <StackPanel> in the XAML above, you would set the ItemsPanel property to use a Grid instead.
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Or to change the TextBlock to something else, you could change the ItemTemplate
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="175" ...>
<TextBlock Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
This would make your output render something like this
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Width="175" ...> <!-- DataContext is MyCollection[0] -->
<TextBlock Text="{Binding Name}" />
</Border>
<Border Width="175" ...> <!-- DataContext is MyCollection[1] -->
<TextBlock Text="{Binding Name}" />
</Border>
<Border Width="175" ...> <!-- DataContext is MyCollection[2] -->
<TextBlock Text="{Binding Name}" />
</Border>
</Grid>
</ScrollViewer>
To set Grid.Column, it's actually a bit trickier. An ItemsControl actually wraps each item in a <ContentPresenter> so each item in the XAML above would actually render more like this :
<ContentPresenter>
<Border Width="175" ...> <!-- DataContext is MyCollection[0] -->
<TextBlock Text="{Binding Name}" />
</Border>
</ContentPresenter>
To set a property like Grid.Column, it needs to be set on the ContentPresenter that wraps each item. And that's what the ItemContainerStyle is used for.
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column"
Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row"
Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
So assuming you were binding to
public ObservableCollection<MyItem> MyCollection { get; set; }
and MyItem looked like this
public class MyItem
{
public int RowIndex { get; set; }
public int ColumnIndex { get; set; }
public string Name { get; set; }
}
Your final XAML might look something like this
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column"
Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row"
Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<!-- ItemTemplate -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="175" ...>
<TextBlock Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And it would render something like this :
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- DataContext is MyCollection[0] -->
<ContentPresenter Grid.Row="{Binding RowIndex}" Grid.Column="{Binding ColumnIndex}">
<Border Width="175" ...>
<TextBlock Text="{Binding Name}" />
</Border>
</ContentPresenter>
<!-- DataContext is MyCollection[1] -->
<ContentPresenter Grid.Row="{Binding RowIndex}" Grid.Column="{Binding ColumnIndex}">
<Border Width="175" ...>
<TextBlock Text="{Binding Name}" />
</Border>
</ContentPresenter>
<!-- DataContext is MyCollection[2] -->
<ContentPresenter Grid.Row="{Binding RowIndex}" Grid.Column="{Binding ColumnIndex}">
<Border Width="175" ...>
<TextBlock Text="{Binding Name}" />
</Border>
</ContentPresenter>
</Grid>
</ScrollViewer>
If i'm right what you mean is that you want to bind a list of items to your xaml. To do this you can use a GridView.
In a Gridview you bind a list of items and define a template for each of these items.
<GridView ItemsSource="{Binding MyList}">
<Gridview.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding foo}"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid MaximumRowsOrColumns="4" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
As you can see. the gridview items template defines how each item in the list is going to be displayed. When you overwrite the itemspanel you can set MaximumRowsOrColumns and Orientation to define how many items each row will have and which way the rows are facing.

wpf Listbox giving columns a header

I have the following markup (xaml):
<ListBox Name="lbEurInsuredType" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition><ColumnDefinition Width="2"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}"></TextBlock>
<TextBox Text="{Binding Uw}" Grid.Column="1"></TextBox>
<TextBox Text="{Binding Partner}" Grid.Column="3"></TextBox>
</Grid>
</DataTemplate></ListBox.ItemTemplate>
</ListBox>
This all looks ok, but now above column 1 and 3 I want to place a header. Can anyone show me how I add headers to my two columns.
Listview is surely the best option, but, if you need to use a listbox you could modify the template of the listbox in this way.
<ListBox Name="lbEurInsuredType" HorizontalContentAlignment="Stretch">
<ListBox.Template>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Grid DockPanel.Dock="Top" Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
<ColumnDefinition Width="2"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Column="0">Column 1</Label>
<Label Grid.Column="1">Column 2</Label>
<Label Grid.Column="2">Column 3</Label>
<Label Grid.Column="3">Column 4</Label>
</Grid>
<ItemsPresenter></ItemsPresenter>
</DockPanel>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
<ColumnDefinition Width="2"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}"></TextBlock>
<TextBox Text="{Binding Uw}" Grid.Column="1"></TextBox>
<TextBox Text="{Binding Partner}" Grid.Column="3"></TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I recommend that you use a ListView instead which more appropriate to you case, you could use a GridView inside and define the columns that you need then restyle them much more easily
<ListView Name="lbEurInsuredType" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<TextBlock Text="Column 1"></TextBlock>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Uw}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Partner}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<TextBlock Text="Column 3"></TextBlock>
</GridViewColumn.Header>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
I haven't run this so there might be some issue with the code but this will give you the idea to add headers to the ListBox
<ListBox Name="lbEurInsuredType" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
<ColumnDefinition Width="2"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Title" ></TextBlock>
<TextBlock Text="Uw" Grid.Column="1" ></TextBox>
<TextBlock Text="Partner" Grid.Column="3" ></TextBox>
<TextBlock Text="{Binding Title}" Grid.Row="1"></TextBlock>
<TextBox Text="{Binding Uw}" Grid.Column="1" Grid.Row="1"></TextBox>
<TextBox Text="{Binding Partner}" Grid.Column="3" Grid.Row="1"></TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Even Listbox can be inside a user control with dynamic data template
<Grid Style="{StaticResource TableHeader}">
<Grid.Resources>
<Style TargetType="{x:Type Border}" BasedOn="{StaticResource TableBorder}"/>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextTableHeader}">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1">
<TextBlock Text="Card"/>
</Border>
<Border Grid.Column="1" BorderThickness="0 1 1 1">
<TextBlock Text="Type"/>
</Border>
<Border Grid.Column="2" BorderThickness="0 1 1 1">
<TextBlock Text="Limit"/>
</Border>
<Border Grid.Column="3" BorderThickness="0 1 1 1">
<TextBlock Text="Amount"/>
</Border>
<Border Grid.Column="4" BorderThickness="0 1 1 1">
<TextBlock Text="Payment Due"/>
</Border>
</Grid>
<ListBox Style="{StaticResource ListBoxStyle}"
ItemsSource="{Binding Source}"
SelectedItem="{Binding SelectedItem}"
IsHitTestVisible="{Binding IsSelectionActive}"
ItemTemplate="{Binding ItemTemplate}" />
</Grid>
ListBox has no HeaderTemplate. ListView is not the best option because it does not support Width="*" . If you get desperate and go to DataGrid you might be getting a lot more than you need. The solution is to use a HeaderedItemsControl
Just change the name of the Collection you are binding to and change the binding properties.
<!-- actual XAML -->
<HeaderedItemsControl
ItemTemplate="{DynamicResource CorrugationItemTemplate}"
ItemsSource="{Binding CorrugationItemCollection}"
Style="{DynamicResource CorrugationStyle}"/>
<!-- this goes in the resource dict -->
<DataTemplate x:Key="CorrugationItemTemplate">
<Grid Width="Auto" Background="#e1e1e1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="Lime" >
<Label Content="{Binding RangeLabel}" />
</Border>
<Border Grid.Column="1" Background="Orange" >
<Label Content="{Binding LeftPreGrind}" />
</Border>
<Border Grid.Column="2" Background="Lime" >
<Label Content="{Binding RightPreGrind}" />
</Border>
<Border Grid.Column="3" Background="Orange" >
<Label Content="{Binding LeftPostGrind}" />
</Border>
<Border Grid.Column="4" Background="Lime" >
<Label Content="{Binding RightPostGrind}" />
</Border>
</Grid>
</DataTemplate>
<Style TargetType="{x:Type HeaderedItemsControl}" x:Key="CorrugationStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedItemsControl}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20pt"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<Border Grid.Column="0" Background="Orange" >
<Label Content="Range" />
</Border>
<Border Grid.Column="1" Background="Lime" >
<Label Content="LeftPreGrind" />
</Border>
<Border Grid.Column="2" Background="Orange" >
<Label Content="RightPreGrind" />
</Border>
<Border Grid.Column="3" Background="Lime" >
<Label Content="LeftPostGrind" />
</Border>
<Border Grid.Column="4" Background="Orange" >
<Label Content="RightPostGrind" />
</Border>
<Grid Grid.Row="1" Grid.ColumnSpan="5" Width="Auto" Height="Auto" Background="White">
<ItemsPresenter/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Categories