is there any control that can auto resize items inside. I need to contain textblocks inside ItemsControl only in one row, so if total items width larger then container.Width, it will change each item's width. UWP
<ItemsControl Grid.Column="1"
ItemsSource="{Binding Path=NavigationHistory, ElementName=Main, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="BroadComb"
MaxHeight="24"
Margin="10,0,0,0" VerticalAlignment="Center" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<HyperlinkButton Command="{Binding Path=NavigateCommand, ElementName=Main}" CommandParameter="{Binding}" Margin="10,0,0,0" Foreground="{ThemeResource AppAccentForegroundLowBrush}" >
<HyperlinkButton.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
TextTrimming="None"
Text="{Binding Path=DisplayName}"
FontSize="10"
FontWeight="Bold"
Foreground="{ThemeResource AppAccentForegroundLowBrush}">
<i:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding Path=CanBeTrim}" Value="True">
<core:ChangePropertyAction PropertyName="TextTrimming" Value="WordEllipsis"/>
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding Path=CanBeTrim}" Value="False">
<core:ChangePropertyAction PropertyName="TextTrimming" Value="None"/>
</core:DataTriggerBehavior>
</i:Interaction.Behaviors>
</TextBlock>
<FontIcon Margin="10,0,0,0" HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="ms-appx:/Assets/Fonts/MyBook-Regular.ttf#MyBook"
FontSize="10"
Glyph="2" Foreground="{ThemeResource AppAccentForegroundLowBrush}" />
</StackPanel>
</DataTemplate>
</HyperlinkButton.ContentTemplate>
</HyperlinkButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
One possibility is to use a Grid with your desired controls in it's columns set to HorizontalAlignment="Stretch":
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="abc" HorizontalAlignment="Stretch" />
<TextBlock Grid.Column="1" Text="xyz" HorizontalAlignment="Stretch" />
</Grid>
This will stretch both controls over the maximum available width according to the Grid's container and make them smaller or bigger if the available space changes.
Most of the time, you will only want to set one column to fill the available space (Width="*") and set the other columns to a fixed or relative width. You can experiment with those settings.
Else we would need a more exact specification of what you want to achieve.
is there any control that can auto resize items inside. I need to contain textblocks inside ItemsControl only in one row, so if total items width larger then container.Width, it will change each item's width. UWP
What you need is to change the default ItemsPanel of GridView. You can create your own Custom Panel to let the texts be arranged in only one row:
Create your own custom Panel OneRowPanel.cs:
public class OneRowPanel:Panel
{
double itemHeight=0;
protected override Size MeasureOverride(Size availableSize)
{
int count = Children.Count;
foreach (FrameworkElement child in Children)
{
child.Measure(new Size(availableSize.Width/count,availableSize.Height));
if (child.DesiredSize.Height > itemHeight)
{
itemHeight = child.DesiredSize.Height;
};
}
// return the size available to the whole panel
return new Size(availableSize.Width, itemHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
// Get the collection of children
UIElementCollection mychildren = Children;
// Get total number of children
int count = mychildren.Count;
// Arrange children
int i;
for (i = 0; i < count; i++)
{
//get the item Origin Point
Point cellOrigin = new Point(finalSize.Width / count * i,0);
// Arrange child
// Get desired height and width. This will not be larger than 100x100 as set in MeasureOverride.
double dw = mychildren[i].DesiredSize.Width;
double dh = mychildren[i].DesiredSize.Height;
mychildren[i].Arrange(new Rect(cellOrigin.X, cellOrigin.Y, dw, dh));
}
// Return final size of the panel
return new Size(finalSize.Width, itemHeight);
}
}
Use it in your Xaml and set the TextBlock.TextTrimming to CharacterEllipsis:
<Page
x:Class="AutoResizeItemsSample.MainPage"
...
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="TextTemplate">
<TextBlock Text="{Binding Text}" TextTrimming="CharacterEllipsis">
</TextBlock>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Name="rootPanel">
<GridView Name="gridView" ItemTemplate="{StaticResource TextTemplate}" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<local:OneRowPanel ></local:OneRowPanel>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</StackPanel>
</Grid>
</Page>
Here is the Result:
Here is the complete demo:AutoResizeItemsSample.
Solve this easily with ListView. The ItemsPanel is already a StackPanel and the individual item will resize as needed. If you need the horizontal width of each item to be different, that's supported out of the box, too. I think the solution is to simply choose the ListView control.
Related
I am currently trying to find/implement a WPF control, which is a combination of a Grid and a StackPanel. That means, I like to have two columns with several items. An item is another control (e.g. single Label, TextBox with label in a panel, ...).
If an item is collapsed, the empty space should be filled with the next item, means I have always as less space used in the control as possible (without gaps between the single items).
I attached two images how it should look like.
Initial:
Item4 is collapsed (notice the shift of following up items):
Does anybody have an idea or experience how to do something like that?
you can use a ListView that uses a StackPanel or a DockPanel as ItemTemplate.
This is an example (simplified) of what I use to display an observable collection of objects in a list with Grid and DockPannel:
<ListView x:Name="lbxDroppedDatas" SelectionMode="Single" AllowDrop="True" BorderThickness="0" Padding="0" Margin="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionChanged="lbxDroppedDatas_SelectionChanged" PreviewKeyDown="lbxDroppedDatas_PreviewKeyDown" PreviewMouseRightButtonDown="lbxDroppedDatas_PreviewMouseRightButtonDown" >
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Grid.Row="0" Width="{Binding Path=ActualWidth, ElementName=Container}" Height="40" Margin="1" LastChildFill="True">
<CheckBox DockPanel.Dock="Left" IsChecked="{Binding IsChecked}" VerticalAlignment="Center" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
<Label Content="{Binding Name}" Foreground="{Binding LineBrush}" FontWeight="Bold" FontSize="12" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="0" MouseEnter="Label_MouseEnter" MouseLeave="Label_MouseLeave"/>
<TextBox DockPanel.Dock="Right" Width="60" MaxLength="14" Text="{Binding CurrentValue, StringFormat=N}" Margin="0,0,33,0" Background="Transparent" Foreground="{Binding LineBrush}" BorderBrush="{Binding LineBrush}" BorderThickness="1" VerticalAlignment="Center" HorizontalAlignment="Right" IsEnabled="False"/>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" VerticalAlignment="Stretch" Margin="0"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
You could bind the visibility of your list element to a collapsed or visible property.
I'd solve this problem in the following way:
start with creating a grid with two stack panels (the grid resides within a UserControl or any other placeholder):
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="LeftPanel" x:FieldModifier="private" Orientation="Vertical" />
<StackPanel x:Name="RightPanel" x:FieldModifier="private" Grid.Column="1" Orientation="Vertical" />
</Grid>
to control your items, create a list of items, preferably within the code of your UserControl:
private List<FrameworkElement> m_items = new List<FrameworkElement>();
for the items you don't want to be visible set the Visibility to Hidden or Collapsed.
you'd need some method that will select the visible items and place them alternating to the left and right panel. I've came up with some quick method that you need to call each time your list changes (an item is added or removed, visibility of an item is changed):
public void SortItems()
{
this.LeftPanel.Children.RemoveRange(0, this.LeftPanel.Children.Count);
this.RightPanel.Children.RemoveRange(0, this.RightPanel.Children.Count);
this.m_items.Where(i => i.Visibility == Visibility.Visible).ToList().ForEach((i) => { (this.LeftPanel.Children.Count == this.RightPanel.Children.Count ? this.LeftPanel : this.RightPanel).Children.Add(i); } );
}
The method simply removes all children of the stack panels and then traverses through the items' list choosing only those which are Visible and adds one to the left panel if both panels have the same amount of items in them and to the right panel otherwise.
If you need some other way of selecting into which panel the following item should be added (e.g. ActualHeight) simply change the condition that is selecting the panels.
If you want to make this method more elegant you can add some events or dependency properties that will call SortItems automatically. Anyway, it's a good start.
Thank you very much for all the different solutions. Finally I solved it with the suggestion from Sinatr. So I created my own panel and override the ArrangeOverride:
protected override Size ArrangeOverride(Size finalSize)
{
List<UIElement> visibleItems = new List<UIElement>();
double xColumn1 = 0;
double xColumn2 = (finalSize.Width / 2) + (WIDTH_COLUMN_SEPERATOR / 2);
double y = 0;
double columnWidth = (finalSize.Width - WIDTH_COLUMN_SEPERATOR) / 2;
for (int i = 0; i < InternalChildren.Count; i++)
{
UIElement child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
{
visibleItems.Add(child);
}
}
for (int i = 0; i < visibleItems.Count; i++)
{
if (i >= (visibleItems.Count - 1))
{
visibleItems[i].Arrange(new Rect(xColumn1, y, columnWidth, visibleItems[i].DesiredSize.Height));
}
else
{
UIElement leftItem = visibleItems[i];
UIElement rightItem = visibleItems[i + 1];
double rowHeight = leftItem.DesiredSize.Height > rightItem.DesiredSize.Height ? leftItem.DesiredSize.Height : rightItem.DesiredSize.Height;
leftItem.Arrange(new Rect(xColumn1, y, columnWidth, rowHeight));
rightItem.Arrange(new Rect(xColumn2, y, columnWidth, rowHeight));
y += rowHeight;
i++;
}
}
return finalSize;
}
I also created my own UserControl for the Items:
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label x:Name="captionLabel" Grid.Row="0" Content="{Binding Caption}"/>
<ContentPresenter x:Name="inputMask" Grid.Row="1" Content="{Binding InputMask, ElementName=userControlBRAIN2AttributePanelItem}" />
</Grid>
So I get te following result (from my test application):
enter image description here
with collapsed item 3:
enter image description here
I'm surprised that no one sugested the use of an UniformGrid.
All you have to do is declar an UniformGrid, set Columns to 2 and add your items. Items that are collapsed should have its Visibility set to Collapsed (duh).
Here's an example:
<UniformGrid Columns="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="Item 1 " />
<TextBlock Text="Item 2 " />
<TextBlock Text="Item 3 " />
<TextBlock Text="Item 4 " />
</UniformGrid>
Will produce the following result:
And when the Visibility of the second TextBlock is set to Collapsed, here's what it looks like:
As discussed already here I've a problem with showing content in a GridView. Based on this article I found out that GridView set the Height property for the items to the height of the first item. The problem for me is that I'd like to have the Height adjusted apdaptive based on the height of the content.
So if the second item in a row is the biggest, all items in this row should get the size of the second item.
I've used this code for showing the GridView:
<GridView ItemsSource="{Binding}" ScrollViewer.VerticalScrollBarVisibility="Disabled" Width="1080">
<GridView.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding ListData, Converter={StaticResource DataBindingDebugConverter}}" x:Name="BirthdayListView" HorizontalAlignment="Center" Margin="0,20,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="🎂"
FontSize="16"
Grid.Column="0"
Margin="0,0,10,10"
FontFamily="Sergoe UI"
Style="{StaticResource BasicTextBlock}"/>
<TextBlock Text="{Binding NameString, Converter={StaticResource DataBindingDebugConverter}}"
Grid.Column="2"
Margin="10,0,0,0"
FontSize="16"
Style="{StaticResource BasicTextBlock}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemContainerStyle>
<Style TargetType="ContentControl">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0,20,0,0"/>
<Setter Property="Width" Value="360" />
<Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
</GridView.ItemContainerStyle>
</GridView>
EDIT:
If the calendar entries are on the first entry (Sonntag) everything is fine.
If the calendar entries are on the second item (Montag) they are hidden and the ItemHeight is not increasing.
Based on this article I found out that GridView set the Height property for the items to the height of the first item.
As you known the row height of GridView depend on the first item not the highest item. So we may need to custom a panel for the GridView's ItemPanel. For example, since the Width of your GridView is fixed value, we can create a CustomPanel as follows:
public class CustomPanel : Panel
{
private double _maxWidth;
private double _maxHeight;
protected override Size ArrangeOverride(Size finalSize)
{
var x = 0.0;
var y = 0.0;
foreach (var child in Children)
{
if ((_maxWidth + x) > finalSize.Width)
{
x = 0;
y += _maxHeight;
}
var newpos = new Rect(x, y, _maxWidth, _maxHeight);
child.Arrange(newpos);
x += _maxWidth;
}
return finalSize;
}
protected override Size MeasureOverride(Size availableSize)
{
foreach (var child in Children)
{
child.Measure(availableSize);
var desirtedwidth = child.DesiredSize.Width;
if (desirtedwidth > _maxWidth)
_maxWidth = desirtedwidth;
var desiredheight = child.DesiredSize.Height;
if (desiredheight > _maxHeight)
_maxHeight = desiredheight;
}
var itemperrow = Math.Floor(availableSize.Width / _maxWidth);
var rows = Math.Ceiling(Children.Count / itemperrow);
return new Size(itemperrow * _maxWidth,_maxHeight * rows );
}
}
Then replace the ItemsPanelTemplate of GridView with the CustomPanelin XAML as follows:
<GridView ItemsSource="{Binding}" Width="1080" >
<GridView.ItemTemplate>
...
</GridView.ItemTemplate>
<GridView.ItemContainerStyle>
...
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<local:CustomPanel />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
As you can see above, I need it to reshuffle itself into alignment. Obviously the items still exist, is there a way to completely ignore them?
I have an ObservableCollection:
public static volatile ObservableCollection<MyVideo> MyVideoModels = new ObservableCollection<MyVideo>();
This gets filled with the MyVideo objects.
Binding it to my GridView like so:
public VideosFoundView()
{
this.InitializeComponent();
this.AddVideoFolderGridView.ItemsSource = VideosFoundView.MyVideoModels;
}
The DataTemplate I am using for the GridView.
<GridView Grid.Row="2" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="AddVideoFolderGridView">
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="5">
<Image Source="{Binding FullImageLocationOnDisk}" MaxHeight="300" MaxWidth="200" DoubleTapped="gridViewItem_DoubleTapped" Loaded="gridViewItem_Loaded" Loading="gridViewItem_Loading">
</Image>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
And I have a static ToggleSwitch above this GridView like so:
<Grid Grid.Row="1">
<Grid>
<TextBlock Text="Ingore Image Not Found"/>
<ToggleSwitch x:Name="ToggleSwitchIgnoreImages" Toggled="ignoreImages_Toggled"/>
</Grid>
</Grid>
Which does:
private void ignoreImages_Toggled(object sender, RoutedEventArgs e)
{
ToggleSwitch tSwitch = (ToggleSwitch)(sender as ToggleSwitch);
if (tSwitch.IsOn)
{
for(int i = 0; i < VideosFoundView.MyVideoModels.Count; i++)
{
if(VideosFoundView.MyVideoModels[i].FullImageLocationOnDisk == "ms-appx:///Assets/image-not-found.gif")
{
var gridViewItem = (GridViewItem)this.AddVideoFolderGridView.ContainerFromIndex(i);
gridViewItem.Visibility = Visibility.Collapsed;
}
}
}
else
{
for (int i = 0; i < VideosFoundView.MyVideoModels.Count; i++)
{
//VideosFoundView.MyVideoModels[i].Visibility = "Auto";
var gridViewItem = (GridViewItem)this.AddVideoFolderGridView.ContainerFromIndex(i);
gridViewItem.Visibility = Visibility.Visible;
}
}
}
However the problem is that the items are still taking up space on my GridView, and the other items do not re-position themselves accordingly.
The items are still taking up space on the GridView, when the GridViewItem is collapsed. It might because the ItemTemplate has not be refreshed, the space is still reserved.
As a workaround, there is a port of the Toolkit's WrapPanel for UWP in the project WinRTXamlToolkit.
You can get it from NuGet.
Then in your Page add this prefix:
xmlns:toolkit="using:WinRTXamlToolkit.Controls"
Now you can use <toolkit:WrapPanel Orientation="Horizontal" ></toolkit:WrapPanel> as it was before.
For example:
<GridView x:Name="AddVideoFolderGridView">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" >
</toolkit:WrapPanel>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="5">
<Image Source="{Binding FullImageLocationOnDisk}" MaxHeight="300" MaxWidth="200" DoubleTapped="gridViewItem_DoubleTapped" Loaded="gridViewItem_Loaded" Loading="gridViewItem_Loading">
</Image>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Solution from here: Not showing items with Visibility=Collapsed in Windows 8.1 GridView
tldr:
Edit the template of your GridView and replace the ItemsWrapGrid in the ItemsPanelTemplate with the WrapPanel you can find here http://codepaste.net/8gr5go
Last year I made an app for Windows Phone 8, containing a LongListSelector with a unique first and last item template, as described here:
LongListSelector different item template for first and last item
I recently updated the app to Windows Phone 8.1 Store and this functionality broke. Here's my class (the only difference from the post being, that I'm using a ListView instead of a LongListSelector for Windows Phone 8.1 Store, since LongListSelector is not a part of the Windows Phone 8.1 Store framework):
public abstract class TemplateSelector : ContentControl
{
public abstract DataTemplate SelectTemplate(object item, int index, int totalCount, DependencyObject container);
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
var parent = GetParentByType<ListView>(this);
var index = (parent.ItemsSource as IList).IndexOf(newContent);
var totalCount = (parent.ItemsSource as IList).Count;
ContentTemplate = SelectTemplate(newContent, index, totalCount, this);
}
private static T GetParentByType<T>(DependencyObject element) where T : FrameworkElement
{
T result = null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
while (parent != null)
{
result = parent as T;
if (result != null)
{
return result;
}
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
}
The problem is that this:
DependencyObject parent = VisualTreeHelper.GetParent(element);
of the GetParentByType function returns null for some reason. Anyone knows why or have an alternative solution?
Below is my XAML code(with a few xmlns's removed). The DataTrigger is there because sometimes a unique first item template is enough (controlled by the LoadMore property of the ViewModel, which is a bool).
<controls:WP81Page
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<controls:WP81Page.Resources>
<DataTemplate x:Key="first">
<Grid HorizontalAlignment="Left" Width="Auto" Height="200">
<Border BorderThickness="1" BorderBrush="Black" Visibility="{Binding NoLargeImage, Converter={StaticResource BoolToVisibilityConverter}}" >
<Image Source="/Images/default-image.png" Stretch="UniformToFill" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Visibility="{Binding NoLargeImage, Converter={StaticResource BoolToVisibilityConverterReverse}}" >
<Image Source="{Binding LargeImageUrl}" Stretch="UniformToFill" />
</Border>
<StackPanel VerticalAlignment="Bottom" Background="#7F000000" >
<TextBlock Text="{Binding Header}" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="White" FontWeight="Bold" Margin="15,0,15,15"/>
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="default">
<StackPanel Orientation="Horizontal" Margin="0,10,0,0" Height="105" Width="Auto">
<Border BorderThickness="1" BorderBrush="Black" Margin="0,2" Visibility="{Binding NoSmallImage, Converter={StaticResource BoolToVisibilityConverter}}" >
<Image Source="/Images/default-image.png" Width="130" Height="100" Stretch="UniformToFill" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Margin="0,2" Visibility="{Binding NoSmallImage, Converter={StaticResource BoolToVisibilityConverterReverse}}">
<Image Source="{Binding SmallImageUrl}" Width="130" Height="100" Stretch="UniformToFill"/>
</Border>
<StackPanel Orientation="Vertical" Width="300" Margin="8,0,0,0">
<TextBlock Text="{Binding Header}" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Margin="0,3,0,0" Text="{Binding DisplayDate}" TextWrapping="Wrap" Foreground="#FFB9B9B9" FontSize="16" />
</StackPanel>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="last">
<TextBlock Text="hent flere nyheder" FontSize="25" Margin="0,20" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" Height="75" />
</DataTemplate>
<DataTemplate x:Key="UniqueFirstTemplateSelector">
<common:UniqueFirstTemplateSelector Content="{Binding}" First="{StaticResource first}" Default="{StaticResource default}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<DataTemplate x:Key="UniqueFirstAndLastTemplateSelector">
<common:UniqueFirstAndLastTemplateSelector Content="{Binding}" First="{StaticResource first}" Default="{StaticResource default}" Last="{StaticResource last}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</controls:WP81Page.Resources>
<controls:WP81Page.DataContext>
<viewModels:NewsViewModel/>
</controls:WP81Page.DataContext>
<Grid x:Name="LayoutRoot" Style="{Binding Source={StaticResource Background}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ProgressBar Grid.Row="0" VerticalAlignment="Top" IsIndeterminate="True" Background="Transparent" Foreground="White" Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibilityConverter}}" />
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="NYHEDER" FontSize="35" FontWeight="Bold" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1">
<controls:WP81ListView x:Name="listSelector" Margin="22,0" Grid.Row="2" ItemsSource="{Binding News}" Command="{Binding NewsEntrySelectedCommand}" >
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding LoadMore}" Value="True">
<core:ChangePropertyAction TargetObject="{Binding ElementName=listSelector}"
Value="{StaticResource UniqueFirstAndLastTemplateSelector}"
PropertyName="ItemTemplate" />
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding LoadMore}" Value="False">
<core:ChangePropertyAction TargetObject="{Binding ElementName=listSelector}"
Value="{StaticResource UniqueFirstTemplateSelector}"
PropertyName="ItemTemplate" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</controls:WP81ListView>
</Grid>
</Grid>
</controls:WP81Page>
Thank you in advance.
EDIT
Alright, so what I want is not for the first and last item to be unique all the time. I'm making a news feed where the last item is only unique when there are more news entries to load (when the last item are clicked, more news entries are added to the ListView). If however, the end of the news entries are reached, the last item must not be unique (hence the combination with interactivity).
ListView has a property called ItemTemplateSelector which accepts objects based on DataTemplateSelector. So you need to change a couple of things to get it to work.
First of all, the definition of your template selector. It needs to be based on DataTemplateSelector and override method called SelectTemplateCore. It takes an object which is a ListViewItem. At that point you can go up the VisualTree to get the ListView and choose the DataTemplate based on index.
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate First { get; set; }
public DataTemplate Default { get; set; }
public DataTemplate Last { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var listViewItem = container as ListViewItem;
var listView = GetParentByType<ListView>(listViewItem);
var index = (listView.ItemsSource as IList).IndexOf(item);
var totalCount = (listView.ItemsSource as IList).Count;
if (index == 0)
return First;
else if (index == totalCount - 1)
return Last;
else
return Default;
}
private T GetParentByType<T>(DependencyObject element) where T : FrameworkElement
{
T result = null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
while (parent != null)
{
result = parent as T;
if (result != null)
{
return result;
}
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
}
Then, you need to create an instance of it in static resources
<local:MyTemplateSelector x:Key="SelectingTemplate"
First="{StaticResource first}"
Default="{StaticResource default}"
Last="{StaticResource last}" />
with the First, Default and Last DataTemplates, just like you did before.
The last step is to apply it to the ListView you're using.
<ListView ItemsSource="{Binding SomeItemsSource}"
ItemTemplateSelector="{StaticResource SelectingTemplate}" />
And that's it!
The standard GridApp template is as follows:
Variable Sized Grouped GridView template is as follows:
How to make a template for your application, so that it looks like this:
For example, this design in all applications Bing for windows 8:
Code for Variable Sized Grouped GridView template:
<UserControl.Resources>
<!-- Collection of grouped items displayed by this page -->
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="true"
ItemsPath="Items"
d:Source="{Binding ItemGroups, Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}"/>
<DataTemplate x:Key="CustomTileItem">
<Grid >
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding Image}" Stretch="UniformToFill"/>
</Border>
<StackPanel VerticalAlignment="Bottom" >
<TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="30" Margin="15,0,15,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</Grid>
</DataTemplate>
</UserControl.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Grid.Column="1" Style="{StaticResource PageHeaderTextStyle}"/>
</Grid>
<!-- Horizontal scrolling grid used in most view states -->
<ScrollViewer
x:Name="itemGridScrollViewer"
AutomationProperties.AutomationId="ItemGridScrollViewer"
Grid.Row="1"
Margin="0,-3,0,0"
Style="{StaticResource HorizontalScrollViewerStyle}">
<local:MyGridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Margin="116,0,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource CustomTileItem}"
SelectionMode="None"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick">
<local:MyGridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</local:MyGridView.ItemsPanel>
<local:MyGridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<Button
AutomationProperties.Name="Group Title"
Content="{Binding Title}"
Click="Header_Click"
Style="{StaticResource TextButtonStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid ItemWidth="75" ItemHeight="150" Orientation="Vertical" Margin="0,0,80,0" MaximumRowsOrColumns="3"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</local:MyGridView.GroupStyle>
</local:MyGridView>
</ScrollViewer>
and:
public class MyGridView : GridView
{
private int rowVal;
private int colVal;
private Random _rand;
private List<Size> _sequence;
public MyGridView()
{
_rand = new Random();
_sequence = new List<Size> {
LayoutSizes.PrimaryItem,
LayoutSizes.SecondarySmallItem, LayoutSizes.SecondarySmallItem,
LayoutSizes.SecondarySmallItem,
LayoutSizes.SecondaryTallItem,
LayoutSizes.OtherSmallItem, LayoutSizes.OtherSmallItem, LayoutSizes.OtherSmallItem
};
}
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
SampleDataItem dataItem = item as SampleDataItem;
int index = -1;
if (dataItem != null)
{
index = dataItem.Group.Items.IndexOf(dataItem);
}
if (index >= 0 && index < _sequence.Count)
{
colVal = (int)_sequence[index].Width;
rowVal = (int)_sequence[index].Height;
}
else
{
colVal = (int)LayoutSizes.OtherSmallItem.Width;
rowVal = (int)LayoutSizes.OtherSmallItem.Height;
}
VariableSizedWrapGrid.SetRowSpan(element as UIElement, rowVal);
VariableSizedWrapGrid.SetColumnSpan(element as UIElement, colVal);
}
}
public static class LayoutSizes
{
public static Size PrimaryItem = new Size(6, 2);
public static Size SecondarySmallItem = new Size(3, 1);
public static Size SecondaryTallItem = new Size(3, 2);
public static Size OtherSmallItem = new Size(2, 1);
}
For example with "Variable Sized Grouped GridView template", we can combine the rows or columns, and how to set the first element height = "auto", and all other elements that have different widths and heights, but grouped as "Variable Sized Grouped GridView template"?
View this post:
http://blogs.msdn.com/b/synergist/archive/2012/09/25/windows-store-app-xaml-gridview-with-variable-templates.aspx
I think this is what you need.