I would like to change the content of a ListView either programmatically or something xaml based.
<Page.Resources>
<CollectionViewSource
x:Name="queuesViewSource" Source="{Binding TicketQueues}" d:Source="{Binding Groups[0].TicketQueues, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
<CollectionViewSource
x:Name="ticketsViewSource"
Source="{Binding Tickets}"
d:Source="{Binding Tickets, Source={d:DesignData Source=/DataModel/TicketsList.json, Type=data:TicketsListDataSource}}"/>
<DataTemplate x:Key="queuesViewItemTemplate">
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Grid.Column="1" Margin="10,0,0,0">
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" MaxHeight="40"/>
<TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ticketsListViewTemplate">
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Margin="10,0,0,0">
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" MaxHeight="40"/>
<TextBlock Text="{Binding Due}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</Grid>
</DataTemplate>
<local:ServiceDeskMasterViewDataTemplateSelector
QueuesTemplate="{StaticResource queuesViewItemTemplate}"
TicketListTemplate="{StaticResource ticketsListViewTemplate}"
x:Name="masterViewTemplateSelector" />
</Page.Resources>
...
<ListView Grid.Row="1" x:Name="masterList" ItemTemplateSelector="{StaticResource masterViewTemplateSelector}" />
And in the code-behind:
Constructor of Page:
{
// Initial Data setting...
Binding items = new Binding();
items.Source = this.Resources["queuesViewSource"];
this.masterList.SetBinding(ItemsControl.ItemsSourceProperty, items);
this.InvalidateVisualState();
}
And finally, the list view item selection to "hopefully" load the listview with different content, but it's not working. Please help
if (eArgs.AddedItems.Count == 1 && eArgs.RemovedItems.Count == 1)
{
Binding items = new Binding();
items.Source = this.Resources["ticketsViewSource"];
this.masterList.SetBinding(ItemsControl.ItemsSourceProperty, items);
//this.masterList.SetBinding(ItemsControl.ItemTemplateProperty, )
this.masterList.ItemTemplate = masterViewTemplateSelector.TicketListTemplate;
//this.masterList.ApplyTemplate();
this.InvalidateVisualState();
}
Try adding a new property like "MasterListItems" and bind to this property instead of directly to the "ticketsViewSource" or the "queueViewSource"
MasterListItems = this.Resources["ticketsViewSource"];
Binding items = new Binding();
items.Mode = BindingMode.TwoWay;
items.Source = MasterListItems;
Then instead of creating a new binding when changing the content, just update the ItemsSource:
MasterListItems = this.Resource["ticketsViewSource"]
Related
I have 2 Listboxes with 2 data templates that are almost identical except that one of them contains TextBox instead of ComboBox.
First Template :
<DataTemplate x:Key="OldPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
<Grid Height="60" Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue"
Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex),
Converter={StaticResource IncrementerConverter}}" />
</Border>
<TextBlock Grid.Column="1" Text="{Binding Name}" />
<TextBlock Grid.Column="2" Text="{Binding DistributionSystemName}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
</Grid>
</DataTemplate>
Second Template :
<DataTemplate x:Key="NewPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
<Grid Height="60">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue"
Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex),
Converter={StaticResource IncrementerConverter}}" />
</Border>
<TextBlock Grid.Column="1" Text="{Binding Name}" />
<ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}"
SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"
IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}"
ItemTemplate="{StaticResource DistributionSystemTemplate}" >
</ComboBox>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of available ways: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfAvailableWays}" />
</Grid>
</DataTemplate>
As you can see they are both almost identical except for this part :
<ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}"
SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"
IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}"
ItemTemplate="{StaticResource DistributionSystemTemplate}" >
</ComboBox>
The problem is whenever i change anything in one of them i have to change the same thing in the other also ... Any way that can merge them in some way and make the combobox the only variable that changes according to which listbox is calling the template ?
Here an attempt on how you can achieve that, but I have to admit that it is messy but answers your question blindly! a better approach would be to properly implement a DataTemplateSelector.
The idea is to decouple the changing parts into two separate DataTemplates and put them into the resources, one with the Combobox and one for the TextBlock in your case:
<DataTemplate x:Key="DataTemplateCombobox">
<ComboBox ItemsSource="{Binding ValidDistributionSystemsForPanel}" ...>
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTextblock" >
<TextBlock Text="{Binding DistributionSystemName}" ... />
</DataTemplate>
Now those controls will be replaced with a ContentPresenter in your main (common) DataTemplate. The ContentPresenter uses a ContentTemplateSelector to select which sub-DataTemplate to used based on the name of the ListBox on which this DataTemplate is applied, this is why the Content is bound directly o the ListBox using ancestry binding:
<local:ValueDataTemplateSelector x:Key="TemplateSelector"
DefaultDataTemplate="{StaticResource DataTemplateTextblock}"
ComboboxDataTemplate="{StaticResource DataTemplateCombobox}"
TextBlockDataTemplate="{StaticResource DataTemplateTextblock}" />
<DataTemplate x:Key="OldPanelsTemplate">
<Grid Height="60" Margin="0" Name="OldPanelsTemplateGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" Text="tex" />
</Border>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
<ContentPresenter ContentTemplateSelector="{StaticResource TemplateSelector}" Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Grid.Row="0" Grid.Column="2">
</ContentPresenter>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
</Grid>
</DataTemplate>
And here how your DataTemplateSelector should be implemented (basically):
public class ValueDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultDataTemplate { get; set; }
public DataTemplate ComboboxDataTemplate { get; set; }
public DataTemplate TextBlockDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var lb = item as ListBox;
if (lb is null)
return DefaultDataTemplate;
if (lb.Name == "ListOne")
return ComboboxDataTemplate;
if (lb.Name == "ListTwo")
return TextBlockDataTemplate;
return DefaultDataTemplate;
}
}
Finally, since your sub-DataTemplates lose their DataContexts due to the Content of the ContentPresenter being bound to the ListBox directly, then just hook their DataContexts again using ElementName Binding or something:
<DataTemplate x:Key="DataTemplateCombobox">
<ComboBox DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}" ItemsSource="{Binding ValidDistributionSystemsForPanel}" >
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTextblock" >
<TextBlock Text="{Binding DistributionSystemName}" DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"/>
</DataTemplate>
OldPanelsTemplateGrid is the First Grid in the main DataTemplate that should have the valid ListBoxItem DataContext.
Here is the full Xaml code:
</Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800" >
<Window.Resources>
<DataTemplate x:Key="DataTemplateCombobox">
<ComboBox DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}" ItemsSource="{Binding ValidDistributionSystemsForPanel}" >
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTextblock" >
<TextBlock Text="{Binding DistributionSystemName}" DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"/>
</DataTemplate>
<local:ValueDataTemplateSelector x:Key="TemplateSelector"
DefaultDataTemplate="{StaticResource DataTemplateTextblock}"
ComboboxDataTemplate="{StaticResource DataTemplateCombobox}"
TextBlockDataTemplate="{StaticResource DataTemplateTextblock}" />
<DataTemplate x:Key="OldPanelsTemplate">
<Grid Height="60" Margin="0" Name="OldPanelsTemplateGrid" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" Text="tex" />
</Border>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
<ContentPresenter ContentTemplateSelector="{StaticResource TemplateSelector}" Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Grid.Row="0" Grid.Column="2">
</ContentPresenter>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
</Grid>
</DataTemplate>
</Window.Resources>
<!--DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorLevel=2,AncestorType=DataTemplate}}"-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox x:Name ="ListOne" ItemsSource="{Binding MyCollection}" ItemTemplate="{StaticResource OldPanelsTemplate}"/>
<ListBox x:Name ="LisTwo" ItemsSource="{Binding MyCollection}" ItemTemplate="{StaticResource OldPanelsTemplate}" Grid.Row="1"/>
</Grid>
I managed to solve it by merging them in one template like this:
<DataTemplate x:Key="NewOldPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
<Grid Height="60">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
Margin="0 0 2 0" >
<TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue"
Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex),
Converter={StaticResource IncrementerConverter}}" />
</Border>
<TextBlock Grid.Column="1" Text="{Binding Name}" />
<!-- To have same template for new and old panels we had the two elements (combobox and textblock) for distribution system and toggle visibility by converters according to their groupbox title -->
<ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}"
SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"
IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}"
ItemTemplate="{StaticResource DistributionSystemTemplate}"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=GroupBox, Mode=FindAncestor}, Path=Header,Converter={StaticResource NewPanelsTemplateVisibilityConverter}}">
</ComboBox>
<TextBlock Grid.Column="2" Text="{Binding DistributionSystemName}"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=GroupBox, Mode=FindAncestor}, Path=Header,Converter={StaticResource OldPanelsTemplateVisibilityConverter}}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Number of available ways: "/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfAvailableWays}" />
</Grid>
</DataTemplate>
and control the visibility of variable parts(ComboBox and TextBlock in my case) with converters like this :
public class OldPanelsTemplateVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((string)value == "Old Panels")
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
}
public class NewPanelsTemplateVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((string)value == "Old Panels")
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}}
The solution above is based on that each of my listboxes is surrounded by groupbox with certain header (In other cases you may want to use ListBox Names in the converter as visibility toggle)
Thanks to #PavelAnikhouski for the idea of converters.
Also i tried the solution of DataTemplateSelector proposed by #SamTheDev and it worked also (Thanks #SamTheDev), but i preferred the converter solution because i didn't feel comfortable with the idea of losing datacontext through using content presenter (The bottom line here is both solutions work and no one is more elegant it is just personal preference)
This question already has answers here:
ListViewItem won't stretch to the width of a ListView
(2 answers)
Closed 7 years ago.
I am trying to display some data from an Observable Collection to a ListView.
Binding is working fine. My only problem I cannot fix is the layout of the ListView.I use a Grid with 2 rows to split my View. The ListView is one the second row. I only try to use * for setting Columns and Rows as suggested in most sources. The code for the ListView is this.
<Grid Grid.Row="1" Margin="10,0,10,0" HorizontalAlignment="Stretch">
<ListView ItemsSource="{Binding Collection}" HorizontalAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center" Text="{Binding Period}" Foreground="{StaticResource ColorBrush}" FontFamily="{StaticResource Family}" FontSize="20"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center" Text="{Binding Currency}" Foreground="{StaticResource ColorBrush}" FontFamily="{StaticResource Family}" FontSize="20"/>
<TextBlock Grid.Column="2" HorizontalAlignment="Center" Text="{Binding Date}" Foreground="{StaticResource ColorBrush}" FontSize="20" FontFamily="{StaticResource Family}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
When I use HorizontalAlignment="Stretch" for the Grid that contains the ListView and the ListView itself the data is displayed on the left side without getting the whole width of parent View.
When I explicitly set Width to some value (i.e.380) it displays OK.
<Grid Grid.Row="1" Margin="10,0,10,0">
<ListView ItemsSource="{Binding Collection}" Width="380">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="380">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center" Text="{Binding Period}" Foreground="{StaticResource ColorBrush}" FontFamily="{StaticResource Family}" FontSize="20"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center" Text="{Binding ValueInCurrency}" Foreground="{StaticResource ColorBrush}" FontFamily="{StaticResource Family}" FontSize="20"/>
<TextBlock Grid.Column="2" HorizontalAlignment="Center" Text="{Binding Date}" Foreground="{StaticResource ColorBrush}" FontSize="20" FontFamily="{StaticResource Family}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
But I want to know a better way to not use implicit values, but *.
Try this. Add the following jus after </ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
I have ListBox and dynamic DataTempletes Items.
When TextBlock text wrapping, ListBox size is adding and size changing when unwrapping doesn't return to old value and remains un-changed.
I don't understand what is the problem. TextBlock size changed but not ListBox.
<ListBox x:Name="ctrlFavOdds"
Grid.Row="2"
ItemsSource="{Binding 'FavStakesList'}"
ItemContainerStyle="{StaticResource alternatingStyle}"
AlternationCount="2"
FontWeight="Bold"
HorizontalContentAlignment="Stretch"
IsHitTestVisible="False"
VerticalAlignment="Top"
VirtualizingStackPanel.IsVirtualizing="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
Margin="2,0,5,0"
Source="{Binding 'MakeExpressBetImage', Converter={StaticResource 'imageconverter'}}"/>
<TextBlock Grid.Column="1"
TextWrapping="Wrap"
FontSize="12"
Text="{Binding Stake}"
VerticalAlignment="Center"
HorizontalAlignment="Left"/>
<TextBlock Grid.Column="2"
TextWrapping="Wrap"
Margin="5,0,0,0"
Text="{Binding StakeFactor}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontSize="14"/>
<Image Grid.Column="3"
Margin="5,0,2,0"
Source="{Binding 'UpDounImage', Converter={StaticResource imageconverter}}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When TextBlock text wraping, ListBox size is adding and size changing when unwrapping doesn't return to old value and remains un-changed
I'm trying to create an ItemsControl that uses a grid as its ItemsPanel in such a way that it has two columns, where the first columns width is the width of the widest item in that column, and has as may rows needed to display all the items
Basically, I want the following, but somehow within an ItemsControl so that I can bind to a collection of objects:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="{Binding Items[0].Header}"/>
<TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>
<Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
<TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>
<Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
<TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
</Grid>
Edit : Rachels answer worked great, here is a working example.
(I moved the Grid.IsSharedSizeScope="True" to the ItemsPanel, not sure if Rachel meant to put it in the ItemTemplate (which didn't work))
namespace WpfApplication23
{
public partial class Window1 : Window
{
public List<Item> Items { get; set; }
public Window1()
{
Items = new List<Item>()
{
new Item(){ Header="Item0", Content="someVal" },
new Item(){ Header="Item1", Content="someVal" },
new Item(){ Header="Item267676", Content="someVal" },
new Item(){ Header="a", Content="someVal" },
new Item(){ Header="bbbbbbbbbbbbbbbbbbbbbbbbbb", Content="someVal" },
new Item(){ Header="ccccccc", Content="someVal" }
};
InitializeComponent();
DataContext = this;
}
}
public class Item
{
public string Header { get; set; }
public string Content { get; set; }
}
}
<Window x:Class="WpfApplication23.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Grid.IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
There are multiple problems here for an ItemsControl:
Getting your first column to match the width of the largest item
Generating a dynamic number of rows
Generating more than one item for each iteration of the ItemsControl
The last one is really the biggest problem, because an ItemsControl wraps each ItemTemplate in a ContentPresenter, so there is no default way of creating more than one item in the panel for each Iteration of the ItemsControl. Your end result would look like this:
<Grid>
...
<ContentPresenter>
<Label Content="{Binding Items[0].Header}"/>
<TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>
</ContentPresenter>
<ContentPresenter>
<Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
<TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>
</ContentPresenter>
<ContentPresenter>
<Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
<TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
</ContentPresenter>
</Grid>
My best suggestion would be to create an ItemTemplate that contains a 1x2 Grid, and use Grid.IsSharedSizeScope to make the width of the first column shared. (The ItemsPanelTemplate would remain the default StackPanel.)
This way, the end result would look like this:
<StackPanel>
<ContentPresenter>
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</ContentPresenter>
<ContentPresenter>
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</ContentPresenter>
...
</StackPanel>
You can use a ListView
<ListView ItemsSource="{Binding MyList}">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn
Header=""
Width="Auto"
DisplayMemberBinding="{Binding Header}"/>
<GridViewColumn
Header=""
DisplayMemberBinding="{Binding Value}"/>
</GridView>
</ListView.View>
</ListView>
the ColumnHeaderContainerStyle hides the GridViewHeader
I have an object hierarchy created with xsd2code. The object hierarchy
consist of a root object of type Project which contains an ObservableCollection
of Folder named folder which is a recursive object containing ObservableCollection
of Folder named folder and File named file
I want to bind this object hierarchy to a TreeView thru an ObjectDataProvider
and control the display thru HierarchicalDataTemplate definitions
The ObjectDataProvider and DataTemplates are defined in a resource merged in App.xaml like this
<HierarchicalDataTemplate x:Key="projectTemplate"
DataType="{x:Type model:Project}"
ItemsSource="{Binding Path=folder}"
>
<Grid ToolTip="{Binding Path=Name}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="Images/folder_documents_512.ico" Grid.Column="0"/>
<TextBlock Grid.Column="1" Margin="6,0,0,0"
Text="{Binding Path=Name}"
/>
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type model:Folder}"
ItemsSource="{Binding Path=file}">
<Grid ToolTip="{Binding Path=name}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="Images/folder_vectors_512.ico" Grid.Column="0"/>
<TextBlock Grid.Column="1" Margin="6,0,0,0"
Text="{Binding Path=name}"
/>
</Grid>
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type model:File}">
<Grid ToolTip="{Binding Path=name}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="Images/article-24.png" Grid.Column="0"/>
<TextBlock Grid.Column="1" Margin="6,0,0,0"
Text="{Binding Path=name}"
/>
</Grid>
</DataTemplate>
<ObjectDataProvider
x:Key="project"
>
</ObjectDataProvider>
The TreeView is defined like this
<TreeView x:Name="tvProject"
ItemsSource="{Binding Source={StaticResource project}}"
>
The global processing is : I load the object hierarchy from an Xml file (xsd2code functionality). As I can see in debug my object hierarchy is clean.
Then, I bind the object hierarchy to the ObjectDataProvider in code like this
ObjectDataProvider dp = Application.Current.FindResource("project") as ObjectDataProvider;
dp.ObjectInstance = _project;
Well, nothing appears in the TreeView.
What am I missing ?
I resolved my issue,
First, I had to bound a collection to the ObjectDataProvider and not just a single object. I donĀ“t think this is an ObjectDataProvider requirement but more a requirement for HierarchicalDataTemplate.
ObjectDataProvider dp = Application.Current.FindResource("project") as ObjectDataProvider;
ArrayList ocp = new ArrayList();
ocp.Add(_project);
dp.ObjectInstance = ocp;
Then I had to chain the reference to the different DataTemplates thru the ItemTemplate property like this:
First on the TreeView
<TreeView x:Name="tvProject"
ItemsSource="{Binding Source={StaticResource project}}"
ItemTemplate="{StaticResource projectTemplate}"
>
And then in the diferent DataTemplate
<DataTemplate x:Key="fileTemplate"
DataType="{x:Type model:File}">
<Grid ToolTip="{Binding Path=name}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="Images/article-24.png" Grid.Column="0"/>
<TextBlock Grid.Column="1" Margin="6,0,0,0"
Text="{Binding Path=name}"
/>
</Grid>
</DataTemplate>
<HierarchicalDataTemplate x:Key="folderTemplate"
DataType="{x:Type model:Folder}"
ItemsSource="{Binding Path=file}"
ItemTemplate="{StaticResource fileTemplate}">
<Grid ToolTip="{Binding Path=name}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="Images/folder_vectors_512.ico" Grid.Column="0"/>
<TextBlock Grid.Column="1" Margin="6,0,0,0"
Text="{Binding Path=name}"
/>
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="projectTemplate"
DataType="{x:Type model:Project}"
ItemsSource="{Binding Path=folder}"
ItemTemplate="{StaticResource folderTemplate}"
>
<Grid ToolTip="{Binding Path=Name}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="Images/folder_documents_512.ico" Grid.Column="0"/>
<TextBlock Grid.Column="1" Margin="6,0,0,0"
Text="{Binding Path=Name}"
/>
</Grid>
</HierarchicalDataTemplate>
Now it works
EDIT
Since Collection can give back different types (i.e Folder and File)
you need a template selector
public class FileSystemObjectTemplateSelector : DataTemplateSelector
{
public override DataTemplate
SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null && item is FileSystemObject)
{
if (item is File)
return Application.Current.FindResource("fileTemplate") as DataTemplate;
if (item is Folder)
return Application.Current.FindResource("folderTemplate") as DataTemplate;
}
return null;
}
}
and the call it in the XAML
<local:FileSystemObjectTemplateSelector x:Key="FileSystemObjectTemplateSelector" />
<HierarchicalDataTemplate x:Key="folderTemplate"
DataType="{x:Type model:Folder}"
ItemsSource="{Binding Path=fsObjects, Converter={StaticResource SortFoldersAndFiles}}"
ItemTemplateSelector="{StaticResource FileSystemObjectTemplateSelector}" >
</HierarchicalDataTemplate>