The result I want to achieve is pretty simple, a list with 2 columns, both with equal width. In Windows Phone 7/8 this could easily be achieved using a ListBox with a WrapPanel as ItemsPanel and setting the ItemWidth to 240 (as the screen width was 480).
Now I'm Writing a Universal App, but here the problem is that the screen is not guaranted to have a width of 480 (not even for the Phone it seems) so I can't set the ItemWidth as I want it to fill the width of the screen. I have been able to achieve almost the desired effect using the following XAML:
<GridView ItemsSource="{Binding Results}" Margin="12">
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding SampleImage}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid MaximumRowsOrColumns="2" Orientation="Horizontal" HorizontalChildrenAlignment="Stretch" VerticalChildrenAlignment="Stretch">
</WrapGrid>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
Which gives the following result:
As seen it successfully gives 2 columns with equal width, BUT the Grid in the GridView.ItemTemlate doesn't fill the whole width of each column. I have tried setting HorizontalAlignment="Stretch" on both that Grid and on the GridView itself witout any success. Anyone has any idea of this do this?
My solution is :
<GridView ItemsSource="{Binding Results}" Margin="12"
SizeChanged="GridView_SizeChanged"
x:Name="MyGridView">
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding SampleImage}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>
Code Behind :
private void GridView_SizeChanged(object sender, SizeChangedEventArgs e)
{
var panel = (ItemsWrapGrid)MyGridView.ItemsPanelRoot;
panel.ItemWidth =panel.ItemHeight= e.NewSize.Width / 2;
}
You could try this:
<GridView.ItemContainerStyle>
<Style
TargetType="GridViewItem">
<Setter
Property="HorizontalAlignment"
Value="Stretch" />
<Setter
Property="VerticalAlignment"
Value="Stretch" />
</Style>
</GridView.ItemContainerStyle>
The other thing you could try is to manually set ItemWidth/ItemHeight whenever you get the SizeChanged event on the GridView.
If for some reason the above doesn't work - you could also do what I do below and update the Value of both DoubleViewModel resources on SizeChanged events:
<UserControl.Resources>
<viewModels:DoubleViewModel
x:Key="ItemWidth"
Value="120" />
<viewModels:DoubleViewModel
x:Key="ItemHeight"
Value="120" />
</UserControl.Resources>
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:YourItemTemplateControl
Width="{Binding Value, Source={StaticResource ItemWidth}}"
Height="{Binding Value, Source={StaticResource ItemHeight}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
Where DoubleViewModel is:
public class DoubleViewModel : BindableBase
{
#region Value
/// <summary>
/// Backing field for the Value property.
/// </summary>
private double value;
/// <summary>
/// Gets or sets a value indicating the value.
/// </summary>
public double Value
{
get { return this.value; }
set { this.SetProperty(ref this.value, value); }
}
#endregion
}
The solution I used was based in Filip Skakuns suggestion, but a slight different implementation by making a reusable User Control with this behaviour. The User Control has (among others) Columns and ItemsSource properties. I change the ItemWidth of the ItemsWrapGrid instead of the Width of the ItemTemplate and do this directly in the SizeChanged event handler.
I also needed to use a ItemsWrapGrid instead of a WrapGrid for this to work. The XAML for the final user control:
<UserControl
x:Class="MyProject.CustomControls.ColumnGridView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ControlRoot">
<Grid DataContext="{Binding ElementName=ControlRoot}">
<GridView ItemsSource="{Binding ItemsSource}" ItemTemplate="{Binding ItemTemplate}">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" SizeChanged="ItemsWrapGrid_SizeChanged" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</Grid>
</UserControl>
And for the code-behind:
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace MyProject.CustomControls
{
public sealed partial class ColumnGridView : UserControl
{
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(ColumnGridView), new PropertyMetadata(null));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(ColumnGridView), new PropertyMetadata(null));
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("Columns", typeof(int), typeof(ColumnGridView), new PropertyMetadata(1));
public int Columns
{
get { return (int)GetValue(ColumnsProperty); }
set
{
if (value <= 0) throw new ArgumentOutOfRangeException("Columns must be greater than 0");
SetValue(ColumnsProperty, value);
}
}
public ColumnGridView()
{
this.InitializeComponent();
}
private void ItemsWrapGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
ItemsWrapGrid itemsWrapGrid = sender as ItemsWrapGrid;
if (itemsWrapGrid != null)
{
itemsWrapGrid.ItemWidth = e.NewSize.Width / Columns;
}
}
}
}
I was able to solve something very similar just binding the Item Width to the parent ActualWidth, like this:
<ListView Name="allDevicesListView" d:DataContext="{d:DesignData /SampleData/VeraServerSampleData.xaml}" ItemsSource="{Binding Devices}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="{Binding ElementName=allDevicesListView, Path=ActualWidth}">
<Grid Width="{Binding ElementName=allDevicesListView, Path=ActualWidth}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" FontSize="24" Grid.Column="0" Margin="0,0,14.333,0" />
<local:VeraDeviceControl VeraDeviceCategory="DimmableLight" Width="auto" Grid.Column="1"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Alternatively you could use the Loaded-Event from your WrapGrid to set at least the ItemWidth
XAML
<Grid Background="LightGreen">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0" Name="MyGrid" Background="Red">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" />
<TextBlock Grid.Column="1" Text="I.O" />
<TextBlock Grid.Column="2" Text="N.V" />
<TextBlock Grid.Column="3" Text="n.I.O" />
</Grid>
<Grid Grid.Row="0" Grid.Column="1" Background="Aqua">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" />
<TextBlock Grid.Column="1" Text="I.O" />
<TextBlock Grid.Column="2" Text="N.V" />
<TextBlock Grid.Column="3" Text="n.I.O" />
</Grid>
<GridView Grid.Row="1" Grid.ColumnSpan="2"
Background="LightBlue"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Details}"
ItemContainerStyle="{StaticResource GridViewItemStyleIOhneHover}"
ItemTemplateSelector="{StaticResource MyProtokollElementDataTemplateSelector}">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal"
HorizontalAlignment="Stretch"
MaximumRowsOrColumns="2"
HorizontalChildrenAlignment="Stretch"
VerticalChildrenAlignment="Stretch" Loaded="MyWrapGrid_Loaded">
</WrapGrid>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</Grid>
Codebehind
private void MyWrapGrid_Loaded(object sender, RoutedEventArgs e)
{
var wg = sender as WrapGrid;
wg.ItemWidth = MyGrid.ActualWidth;
}
Example
Related
I'm working with a list of objects that have a Selected property and I'm trying to bind it to the IsSelected property on ListViewItem within a multi-select ListView control in UWP.
I just can't seem to get the binding to work. The checkboxes in the ListView do not render checked if Selected = True and the Set on Selected never gets triggered when checking an item.
SettingsPage.xaml
<Page.Resources>
<DataTemplate x:Key="PreviewColumnTemplate" x:DataType="models:Column">
<TextBlock>
<Run Text="{x:Bind name}"/>
<Run Text=" ("/>
<Run Text="{x:Bind ColumnValidation.column_label}"/>
<Run Text=") "/>
</TextBlock>
</DataTemplate>
<Style x:Key="previewColumnListViewItem" TargetType="ListViewItem">
</Style>
</Page.Resources>
<ListView
x:Name="previewColumnListView"
ItemsSource="{x:Bind ViewModel.CurrentDrillHole.Collar.Columns, Mode=TwoWay}"
ItemTemplate="{StaticResource PreviewColumnTemplate}"
Height="400"
SelectionMode="Multiple"
SelectionChanged="previewColumnListView_SelectionChanged">
<ListView.Resources>
<Style TargetType="ListViewItem" BasedOn="{StaticResource previewColumnListViewItem}">
<Setter Property="IsSelected" Value="{Binding Selected, Mode=TwoWay}"/>
</Style>
</ListView.Resources>
</ListView>
The ViewModel.CurrentDrillHole.Collar object is of type Table and looks like so:
public class Table : BindableBase
{
public string Name { get; set; }
public TableValidation TableValidation { get; set; }
public List<Column> Columns { get; set; }
public List<Row> Rows { get; set; } = new List<Row>();
}
And the Column object looks like so. It is here I want to bind to the Selected property.
public class Column : BindableBase, INotifyPropertyChanged
{
public string name { get; set; }
public ColumnValidation ColumnValidation { get; set; }
public List<RefEntryValue> LookupValues { get; set; } = null;
private bool _selected = false;
public bool Selected {
get => _selected;
set
{
_selected = value;
OnPropertyChanged();
}
}
}
Any ideas of things I can try would be greatly appreciated. Thanks for the help!
When you set SelectionMode="Multiple", ListViewItem uses the default ListViewItemTemplate whose key is "ListViewItemExpanded".
Its style is as follows:
<Style TargetType="ListViewItem" x:Key="ListViewItemExpanded">
......
<ControlTemplate TargetType="ListViewItem">
<Grid x:Name="ContentBorder"
Control.IsTemplateFocusTarget="True"
FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
RenderTransformOrigin="0.5,0.5">
……
<Border x:Name="MultiSelectSquare"
BorderBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
BorderThickness="2"
Width="20"
Height="20"
Margin="12,0,0,0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Visibility="Collapsed">
<Border.Clip>
<RectangleGeometry Rect="0,0,20,20">
<RectangleGeometry.Transform>
<TranslateTransform x:Name="MultiSelectClipTransform" />
</RectangleGeometry.Transform>
</RectangleGeometry>
</Border.Clip>
<Border.RenderTransform>
<TranslateTransform x:Name="MultiSelectCheckBoxTransform" />
</Border.RenderTransform>
<FontIcon x:Name="MultiSelectCheck"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Glyph=""
FontSize="16"
Foreground="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
Visibility="Collapsed"
Opacity="0" />
</Border>
<Border x:Name="MultiArrangeOverlayTextBorder"
Opacity="0"
IsHitTestVisible="False"
Margin="12,0,0,0"
MinWidth="20"
Height="20"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Background="{ThemeResource SystemControlBackgroundAccentBrush}"
BorderThickness="2"
BorderBrush="{ThemeResource SystemControlBackgroundChromeWhiteBrush}">
<TextBlock x:Name="MultiArrangeOverlayText"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.DragItemsCount}"
Style="{ThemeResource CaptionTextBlockStyle}"
IsHitTestVisible="False"
Opacity="0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
AutomationProperties.AccessibilityView="Raw" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Style>
As you can see, there is no CheckBox in its style, it is composed by Border and FontIcon.
If you want to solve this problem, I suggest that you could add CheckBox to DataTemplate. By doing this, we can bind “Selected’ to the “IsChecked” property of the CheckBox.
Please refer to the following code.
<ListView
x:Name="previewColumnListView"
ItemsSource="{x:Bind ViewModel.CurrentDrillHole.Collar.Columns, Mode=TwoWay}"
Height="400"
SelectionChanged="previewColumnListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate x:Key="PreviewColumnTemplate" x:DataType="models:Column">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Selected, Mode=TwoWay}"/>
<TextBlock>
<Run Text="{x:Bind name}"/>
<Run Text=" ("/>
<Run Text="{x:Bind ColumnValidation.column_label}"/>
<Run Text=") "/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is how it ended up looking in the end. Does this seem clunky to anyone else? It would be nice if the ListView control made it easier to deal with collections and SelectionMode="Multiple".
SettingsPage.xaml
<ListView
x:Name="previewColumnListView"
ItemsSource="{x:Bind ViewModel.CurrentDrillHole.Collar.Columns, Mode=TwoWay}"
Height="400"
SelectionChanged="previewColumnListView_SelectionChanged"
IsItemClickEnabled="True"
ItemClick="previewColumnListView_ItemClick">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Column">
<StackPanel Orientation="Horizontal">
<CheckBox Click="previewColumnListView_CheckBox_Click" IsChecked="{Binding Selected, Mode=TwoWay}"/>
<TextBlock>
<Run Text="{x:Bind name}"/>
<Run Text=" ("/>
<Run Text="{x:Bind ColumnValidation.column_label}"/>
<Run Text=") "/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
SettingsPage.xaml.cs
private async void previewColumnListView_CheckBox_Click(object sender, RoutedEventArgs e)
{
// update the list of selected columns
Settings.CollarPreviewFields = ViewModel.CurrentDrillHole.Collar.Columns.Where(x => x.Selected).Select(x => x.name).ToList();
await App.SaveSettings();
}
private void previewColumnListView_ItemClick(object sender, ItemClickEventArgs e)
{
Column selectedColumn = (Column)e.ClickedItem;
selectedColumn.Selected = !selectedColumn.Selected;
// trigger checkbox click event. will update the list and save.
previewColumnListView_CheckBox_Click(null, null);
}
I have this ListBox with custom elements and I want them to stretch to take all the available area. This is my code; currently the items only take the space that they need and I have some unused space on the left and the right side of the screen. Why?
<ListBox x:Name="listBox" Margin="0,6,0,0">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<local:ListTemplateSelector Content="{Binding}">
<local:ListTemplateSelector.bloccato>
<DataTemplate>
<StackPanel Grid.Row="1">
<Grid Background="Beige">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Assets/Images/locked.png" Width="40" Height="40" HorizontalAlignment="Left"/>
<TextBlock Grid.Column="1" Text="{Binding nomePacchetto}" FontFamily="./Assets/neo-normal.ttf#NEOTERIC" FontSize="48" VerticalAlignment="Bottom" Foreground="#FF373737"/>
</Grid>
<Line Stroke="DarkGray" X2="400" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,6,0,6"/>
</StackPanel>
</DataTemplate>
</local:ListTemplateSelector.bloccato>
<local:ListTemplateSelector.sbloccato>
<DataTemplate>
<StackPanel Grid.Row="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="left" Foreground="#FF5B5B5B"><Run FontSize="32" Text="1/"/> <Run FontSize="20" Text="40"/></TextBlock>
<TextBlock Grid.Column="1" Text="{Binding nomePacchetto}" FontFamily="./Assets/neo-normal.ttf#NEOTERIC" FontSize="48" VerticalAlignment="Bottom" Foreground="#FF373737"/>
</Grid>
<Line Stroke="DarkGray" X2="400" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,6,0,6"/>
</StackPanel>
</DataTemplate>
</local:ListTemplateSelector.sbloccato>
</local:ListTemplateSelector>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
There are two ways to solve this problem:
(1)
The easiest way is giving fixed width to your root grid in datatemplate. Though you'll provide fixed width, it will be resolution responsive.
Check this example:
// XAML page
<ListBox x:Name="lbxTest" Margin="12">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Blue" Width="370" >
<TextBlock Text="{Binding}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you write your code behind like this:
List<string> lstString = new List<string>() { "string 1", "string 2", "string 3" };
lbxTest.ItemsSource = lstString;
Then in every resolution (480x800, 720x1280, 768x1280, 1080x1920), the size obtained by ListBox will be same.
check the screenshots for refference.
screenshot in 480x800
screenshot in 768x1280
(2)
The other way to solve this problem is adding one parameter in the ItemSource we'll be assigning to the ListBox.
<ListBox x:Name="lbxTest" Margin="12">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Blue" Width="{Binding width}" >
<!--Width="370"-->
<TextBlock Text="{Binding text}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
// code behind
public class Model
{
public double width { get; set; }
public string text { get; set; }
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Model m1 = new Model()
{
text = "string 1",
width = lbxTest.ActualWidth
};
Model m2 = new Model()
{
text = "string 2",
width = lbxTest.ActualWidth
};
List<Model> lstModel = new List<Model>();
lstModel.Add(m1);
lstModel.Add(m2);
lbxTest.ItemsSource = lstModel;
}
Hope this will help..!!
There are two properties that should work but they do not (HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch")
To solve that this example will help you with the witdh:
<ListBox x:Name="ListBoxInstance" HorizontalAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Green" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="{Binding ActualWidth, ElementName=ListBoxInstance, Mode=OneWay}" >
<Border Background="Red" >
<TextBlock Text="{Binding}"/>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Items>
<x:String>This is a test</x:String>
<x:String>This is the second</x:String>
<x:String>This is the thidr</x:String>
<x:String>s</x:String>
</ListBox.Items>
</ListBox>
As you see with
Width="{Binding ActualWidth, ElementName=ListBoxInstance}"
You have the content to the full width.
In some others cases (depending on the platform)
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
This one is driving me crazy and I am sure there must be a straightforward answer (that I haven't been able to spot).
I have a grouped gridview control which uses a VariableSizedWrapGrid for the grouped panel. The designs approved by my client include a top and bottom border on each group. I thought I could do one of two things:
Specify the border on the VariableSizedWrapGrid; or
Create a line in the GroupStyle.HeaderTemplate and apply the same to a footer.
So it seems I can't do either of those things as VariableSizedWrapGrid inherits from Panel which doesn't support the border property (only adding the border as a child element) and the GridView class doesn't include a grouped footer property. Is there a way of applying a border to the VariableSizedWrapGrid? Xaml is quite new to me as I normally specialise in server side code rather than presentation.
If I've understood you correctly they what you are trying to achieve is something like this:
This is the code for that, and it should work with a variablesizegrid as well. If I've missunderstood please add some more details and the code you already have so we can see how we can best help you.
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="App14.ItemsPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App14"
xmlns:data="using:App14.Data"
xmlns:common="using:App14.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="groups" IsSourceGrouped="true" />
</Page.Resources>
<Grid Style="{StaticResource LayoutRootStyle}">
<Grid.Resources>
<DataTemplate x:Key="groupTemplate">
<Grid>
<Border BorderBrush="White" BorderThickness="0,10" Padding="20">
<StackPanel >
<Border Background="DarkGreen" Padding="10" Margin="10">
<TextBlock Text="{Binding Name}"/>
</Border>
<Border Background="Yellow" Padding="10" Margin="10">
<Image Width="100" Height="100" Stretch="Uniform" Source="{Binding Image}"/>
</Border>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.RowSpan="2"
Padding="116,136,116,46"
ItemsSource="{Binding Source={StaticResource groups}}"
ItemTemplate="{StaticResource groupTemplate}">
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="10">
<TextBlock Text='{Binding Key}' Foreground="White" FontSize="25" Margin="5" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
</Grid>
and the code:
namespace App14
{
public sealed partial class ItemsPage : App14.Common.LayoutAwarePage
{
public ItemsPage()
{
this.InitializeComponent();
groups.Source = GetAllGrouped(LoadCats());
}
public IEnumerable<IGrouping<string, FakeCat>> GetAllGrouped(IEnumerable<FakeCat> cats)
{
return cats.OrderBy(x => x.Name).GroupBy(x => x.Name);
}
IEnumerable<FakeCat> LoadCats()
{
return new List<FakeCat>
{
new FakeCat {Name = "Naomi", Image = "../Assets/cat1.jpg"},
new FakeCat {Name = "Naomi", Image = "../Assets/cat2.jpg"},
new FakeCat {Name = "Peter", Image = "../Assets/cat3.jpg"},
new FakeCat {Name = "Spencer", Image = "../Assets/cat4.jpg"},
};
}
}
public class FakeCat
{
public string Name { get; set; }
public string Image { get; set; }
public int ItemSize { get; set; }
}
}
Problem solved! I guess I was having trouble figuring out what controls the templates for the group rather than the actual items. I would like to take credit for solving this, but the answer came courtesy of a member of a LinkedIn group. The following style works when applied to the GridView's GroupStyle's ContainerStyle:
<Style x:Key="GroupItemStyle1" TargetType="GroupItem">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentControl x:Name="HeaderContent" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}" Content="{TemplateBinding Content}" IsTabStop="False" Margin="{TemplateBinding Padding}" TabIndex="0"/>
<Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="1" Fill="White" Margin="5,0,15,0" />
<ItemsControl x:Name="ItemsControl" IsTabStop="False" ItemsSource="{Binding GroupItems}" Grid.Row="1" TabIndex="1" TabNavigation="Once">
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<AddDeleteThemeTransition/>
<ContentThemeTransition/>
<ReorderThemeTransition/>
<EntranceThemeTransition IsStaggeringEnabled="False"/>
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
</ItemsControl>
<Rectangle Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="1" Fill="White" Margin="5,0,15,0" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And then in the XAML for the GridView:
<GridView.GroupStyle>
<GroupStyle HidesIfEmpty="True" ContainerStyle="{StaticResource GroupItemStyle1}">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<!-- Header Template here -->
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,0,0"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
have in XAML a datagridtemplate column
<sdk:DataGridTemplateColumn x:Name="Urgency">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Source="{Binding UrgencyUri}"
VerticalAlignment="Stretch"
Stretch="UniformToFill"
Height="10" Width="10" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Urgency}"/>
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.HeaderStyle>
<Style TargetType="primitive:DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Urgency, Source={StaticResource MHVWindowResources}}" Grid.Column="0" HorizontalAlignment="Left" />
<filter:DataGridColumnFilter Grid.Column="1" HorizontalAlignment="Right" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</sdk:DataGridTemplateColumn.HeaderStyle>
</sdk:DataGridTemplateColumn>
I used this code below however it returns null
DataGridTemplateColumn templateColumn = dataGrid.Columns[columnIndex] as DataGridTemplateColumn;
if (templateColumn != null)
{
string header = templateColumn.Header as string;
if (header == null)
{
return null;
}
bindingPath = header;
}
Is their anything that I missed out? Thanks
I want to know how to get the column header in code behind.
Add this line:
var header = templateColumn.Header
place a break point here debug and check what the type is in the quick watch.
My bet is the header is probably never a string so it always null when you use:
string header = templateColumn.Header as string;
hope that helps
I have an user control named GraphPanel. It has two dependency properties, one custom, PanelTitle, and the other inherited from the FrameworkElement, Content.
public static readonly DependencyProperty PanelTitleProperty = DependencyProperty.Register(
"PanelTitle",
typeof(string),
typeof(GraphPanel),
new PropertyMetadata("")
);
// ...
public string PanelTitle
{
set { SetValue(PanelTitleProperty, value); }
get { return (string)GetValue(PanelTitleProperty); }
}
The XAML code is as follows:
<UserControl
x:Class="PlaceringsGuiden.Library.Components.GraphPanel"
DataContext="{Binding RelativeSource={RelativeSource self}}">
<UserControl.Resources>
<ResourceDictionary Source="/App;component/Assets/Styles/GraphPanelStyles.xaml" />
</UserControl.Resources>
<Border Style="{StaticResource GraphPanelBorderStyle}">
<Grid Style="{StaticResource GraphPanelGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="8*" />
</Grid.RowDefinitions>
<Grid Grid.Column="0" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.02*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="0.02*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1"
Grid.Row="0"
Text="{Binding Path=PanelTitle}"
Style="{StaticResource GraphPanelHeaderStyle}" />
</Grid>
<Grid Grid.Column="0" Grid.Row="0" x:Name="GraphPanelContentPresenter">
<ContentPresenter Content="{Binding Path=Content}" />
</Grid>
</Grid>
</Border>
</UserControl>
Running this yields an exception:
Value does not fall within the expected range.
at MS.Internal.XcpImports.CheckHResult(UInt32 hr)
What am I doing wrong? What should I do to achieve this binding?
Thanks!
I resolved this issue by removing the binding from the ContentPresenter. That said, this solution is flawed as the custom control is not an element container.
By creating a new class extending ContentControl, styling with ControlTemplate, is this achieved without the need of complicated binding scenarios.
public class GraphPanel : ContentControl
{
#region Properties
public string PanelTitle
{
get { return (string) GetValue(PanelTitleProperty); }
set { SetValue(PanelTitleProperty, value); }
}
#endregion
#region Dependency properties
public static readonly DependencyProperty PanelTitleProperty =
DependencyProperty.Register("PanelTitle", typeof(string), typeof(GraphPanel), new PropertyMetadata(""));
#endregion
public GraphPanel()
: base()
{
DefaultStyleKey = typeof(GraphPanel);
}
}
and XAML code:
<Style TargetType="local:GraphPanel">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:GraphPanel">
<Border Style="{StaticResource GraphPanelBorderStyle}">
<Grid Style="{StaticResource GraphPanelGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="8*" />
</Grid.RowDefinitions>
<Grid Grid.Column="0" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.02*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="0.02*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1"
Grid.Row="0"
Text="{TemplateBinding PanelTitle}"
Style="{StaticResource GraphPanelHeaderStyle}" />
</Grid>
<Grid Grid.Column="0" Grid.Row="1">
<ContentPresenter />
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Sometimes it just helps writing it down, and you'd realise your own mistakes. Thanks for giving this a moment's thought though!