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!
Related
How can I remove the row Comment is on if its null or empty string?
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding EntryDate}" Grid.Column="0" Grid.Row="0"></Label>
<Label Text="{Binding Sleep}" Grid.Column="1" Grid.Row="0"></Label>
<Label Text="{Binding Comment}" Grid.Column="0" Grid.ColumnSpan="4"
Grid.Row="1"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
This is the appropriate case to use a ValueConverter.
All you need to do is create a converter like this:
namespace App.Converters
{
public class TextToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value != null)
if (!(value is string)) return true;
return string.IsNullOrWhiteSpace(value as string) ? false : true;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And use it on the IsVisible property of Label:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:App.Converters"
x:Class="App.Views.SamplePage">
<ContentPage.Resources>
<ResourceDictionary>
<converters:TextToBoolConverter x:Key="TextToBoolConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding EntryDate}" Grid.Column="0" Grid.Row="0"></Label>
<Label Text="{Binding Sleep}" Grid.Column="1" Grid.Row="0"></Label>
<Label Text="{Binding Comment}" Grid.Column="0" Grid.ColumnSpan="4"
Grid.Row="1"
IsVisible={Binding Comment, Converter={StaticResource TextToBoolConverter}}/>
</Grid>
</ContentPage>
Thus you can reuse it wherever you need.
Your model:
public class MyModel
{
//...other properties ...
public string Comment { get; set; }
public bool HasComment => !string.IsNullOrEmpty(Comment);
}
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding EntryDate}" Grid.Column="0" Grid.Row="0"></Label>
<Label Text="{Binding Sleep}" Grid.Column="1" Grid.Row="0"></Label>
<Label Text="{Binding Comment}" Grid.Column="0" Grid.ColumnSpan="4"
Grid.Row="1" IsVisible = {Binding HasComment}></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
In my UWP app which uses MVVMLight, I have a button contained in my ListViewItem and when clicked I want to display a flyout and provide additional actions for the specific ListViewItem but whenever I tap on the button, I get an error i.e.
System.InvalidCastException: Unable to cast object of
type 'Windows.UI.Xaml.Input.TappedRoutedEventArgs' to
type 'MyApp.ViewModels.ItemViewModel'.
at GalaSoft.MvvmLight.Command.RelayCommand`1.Execute(Object parameter)
at Microsoft.Xaml.Interactions.Core.InvokeCommandAction.Execute(Object
sender, Object parameter) at
Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender,
ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTr
I understand to some extend why it's happening but how I can I fix this? The button contained in my ListViewItem data template is obviously being passed to the parent which is the listview and the DataTrigger that's defined in the Listview is capturing it.
All I want to do is display a flyout when this button is clicked to provide additional options.
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="{StaticResource SystemControlBackgroundAccentBrush5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Width="90"
Height="90"
VerticalAlignment="Center"
Margin="5,5,0,5">
<Image Width="90"
Height="90"
Source="{Binding Logo}" />
</Border>
<Grid Grid.Row="0" Grid.Column="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="1" VerticalAlignment="Bottom">
<TextBlock Text="Id:" FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Subject}" Margin="10,0,0,0" Foreground="White" VerticalAlignment="Center"/>
</StackPanel>
<Button Width="30" Height="30"
Command="{Binding AdditionalInfoCommand}"
Grid.RowSpan="4"
Grid.Column="1"
Margin="0,0,12,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Stroke="{ThemeResource SystemControlBackgroundAccentBrush}"
Fill="{ThemeResource SystemControlBackgroundAccentBrush}"
StrokeThickness="2">
</Ellipse>
<Image Source="ms-appx:///Assets/Information.png" Width="30" Height="30" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
and my ListView has the following code:
<ListView ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
.... as defined above
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
Any ideas on how I can resolve this?
UPDATE 1:
I'll re-phrase my question slightly. I've managed to bind the button displayed on the ListViewItem to a specific click event by introducing a RelayCommand (AdditionalInfoCommand) in the item's ViewModel. So now my main problem is the actual error mentioned above which is triggered after AdditionalInfoCommand and I assume it's because it is being captured by the ItemClickCommand. Strange as you would assume that it is using the same ViewModel.
Is there a way to no trigger the itemclick when this button is tapped?
Thanks.
Thierry
I figured it out.
When I tap the button inside my ListView DataTemplate, the button is being passed the Item's ViewModel which is correct but then the tap event is bubbled up back to the parent which is captured by:
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
While there may be a solution to stop the event bubbling, to circumvent the problem, I changed my ItemClickCommand.
It was defined as follows:
private RelayCommand<MyItemViewModel> _itemClickCommand;
public RelayCommand<MyItemViewModel> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<MyItemViewModel>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I simply changed it to:
private RelayCommand<object> _itemClickCommand;
public RelayCommand<object> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<object>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I then changed the ItemClickCommandAction to take in an object instead of MyItemViewModel and now, within the method, I check the type of the ViewModel being passed:
private void ItemClickCommandAction(object currentObject)
{
if (currentObject is MyItemViewModel)
{
//go to next page
}
}
Can you please recommend me some sample code to create framed lines as shown in the picture. As you can see in the image, for example for the first row, M should be in one frame and all the other 3 items in that row should be in another row.
Below please see my code, any help is appreciated.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XYZclass.Views.Exams.ExamsPage"
Title="{Binding PageTitle}">
<StackLayout VerticalOptions="FillAndExpand">
<Image Source="XYZclassHeader.png" Aspect="AspectFit" />
<Grid BackgroundColor="#0075c1">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="1" Text="" TextColor="White" HorizontalOptions="Center"/>
<Label Grid.Row="0" Grid.Column="2" Text="Type:" TextColor="White" HorizontalOptions="Center"/>
<Label Grid.Row="0" Grid.Column="3" Text="Created:" TextColor="White" HorizontalOptions="Center"/>
<Label Grid.Row="0" Grid.Column="4" Text="Finished:" TextColor="White" HorizontalOptions="Center"/>
</Grid>
<ListView Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Exams}" HorizontalOptions="Center">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Frame OutlineColor="Accent" Grid.Row="0" Grid.Column="1" HorizontalOptions="Center">
<Frame.Content>
<Label Text="{Binding DisplayName}" />
</Frame.Content>
</Frame>
<Label Grid.Row="0" Grid.Column="2" Text="{Binding Type}" HorizontalOptions="Center"/>
<Label Grid.Row="0" Grid.Column="3" Text="{Binding CreationDate}" HorizontalOptions="Center"/>
<Label Grid.Row="0" Grid.Column="4" Text="{Binding CompletionDateInfo}" HorizontalOptions="Center"/>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout BackgroundColor="#0075c1" VerticalOptions="FillAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding DeleteSelectedExamsCommand}"/>
</StackLayout.GestureRecognizers>
<Label Text="Delete Selected(3) "
TextColor="White" />
<Label Text=""
TextColor="White"
FontSize="Large" />
</StackLayout>
<StackLayout BackgroundColor="#0075c1" VerticalOptions="FillAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CreateNewExamCommand}"/>
</StackLayout.GestureRecognizers>
<Label Text="Create New Exam "
TextColor="White"/>
<Label Text=""
TextColor="White"
FontSize="Large"
FontFamily="FontAwesome"/>
<!--Note about FontAwesome for iOS: The FontFamily reference is for iOS. On android it will be ignored-->
</StackLayout>
</StackLayout>
</ContentPage>
Thanks for your answers but this is I have implemented it, I mostly used Grid control, I am sharing it here in case you need a similar screen:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XYZclass.Views.Exams.ExamsPage"
BackgroundColor="White"
xmlns:controls="clr-namespace:XYZclass.Controls">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="10, 20, 10, 10"
Android="0,0,0,10"/>
</ContentPage.Padding>
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="TextColor" Value="White"/>
<Setter Property="Font" Value="Medium"/>
<Setter Property="VerticalOptions" Value="Center"/>
<Setter Property="HorizontalOptions" Value="Center"/>
</Style>
<Style x:Key="LabelStyleSmall" TargetType="Label">
<Setter Property="TextColor" Value="#41a4dc"/>
<Setter Property="Font" Value="Small"/>
<Setter Property="VerticalOptions" Value="Center"/>
<Setter Property="HorizontalOptions" Value="Center"/>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<controls:Navigation/>
</Grid>
<Grid RowSpacing="0" Grid.Row="1" Padding="10,10,10,0">
<Grid.RowDefinitions>
<RowDefinition Height="145*" />
<RowDefinition Height="415*" />
<RowDefinition Height="2*" />
<RowDefinition Height="88*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" RowSpacing="10" BackgroundColor="#ed004a">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="#ed004a" Padding="30,0,0,0">
<Label Text="Your personal exam history information"
Style="{StaticResource LabelStyle}"
HorizontalOptions="StartAndExpand"/>
</Grid>
<Grid Grid.Row="1"
BackgroundColor="#0075c1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text=""
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="1"
Text="Type:"
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="2"
Text="Created:"
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="3"
Text="Finished:"
Style="{StaticResource LabelStyle}"/>
</Grid>
</Grid>
<ScrollView Grid.Row="1">
<ListView x:Name="ExamList"
ItemsSource="{Binding Exams}"
HorizontalOptions="Center"
RowHeight="70">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<Frame OutlineColor="#ed004a" Grid.Column="0">
<Frame.Content>
<Label Text="{Binding Name}"
Font="Large"
Style="{StaticResource LabelStyle}"
TextColor="#ed004a"
FontAttributes="Bold"/>
</Frame.Content>
</Frame>
<Frame OutlineColor="#ed004a" Grid.Column="1">
<Frame.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text="{Binding Type}"
Style="{StaticResource LabelStyleSmall}"/>
<Label Grid.Column="1"
Text="{Binding StartDateText}"
Style="{StaticResource LabelStyleSmall}"/>
<Label Grid.Column="2"
Text="{Binding FinishedDateText}"
Style="{StaticResource LabelStyleSmall}"/>
</Grid>
</Frame.Content>
</Frame>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollView>
<BoxView Grid.Row="2" Color="#0075c1" WidthRequest="100" HeightRequest="2"/>
<Grid Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="40*" />
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
BackgroundColor="{Binding DeleteButtonBackgroundColor}"
Padding="30,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
x:Name="LabelDeleteSelectedExamsPartOne"
Text="{Binding DeleteButtonText}"
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="1"
x:Name="LabelDeleteSelectedExamsPartTwo"
Text=""
Style="{StaticResource LabelStyle}"
Font="Large"/>
<Grid.GestureRecognizers IsEnabled="{Binding DeleteButtonIsEnabled}">
<TapGestureRecognizer
Command="{Binding DeleteSelectedExamsCommand}"/>
</Grid.GestureRecognizers>
</Grid>
<Grid Grid.Column="2"
BackgroundColor="#0075c1"
Padding="30,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
x:Name="LabelCreateNewExamPartOne"
Text="Create New Exam "
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="1"
x:Name="LabelCreateNewExamPartTwo"
Text=""
Style="{StaticResource LabelStyle}"
Font="Large"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding CreateNewExamCommand}"/>
</Grid.GestureRecognizers>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</ContentPage>
First of all, grid column indices start at '0'(i.e., if u have 4 columns, their indices will be [0,1,2,3]).
Back to the question:
Your approach seems very strange: you put 4 elements in 4 grid cells 'independently', but 3 of this elements have to be placed within common border. You may try to put this 3 elements in another container(for example, in stack layout), and then add a border to this container. Frame class itself is very poor, but here is some workaround using custom renderers.
Thus, listview cell may look like this:
<StackLayout Orientation="Horizontal">
<local:FrameWithBorder>
<Label Text="{Binding DisplayName}" />
</local:FrameWithBorder>
<local:FrameWithBorder HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Type}" />
<Label Text="{Binding CreationDate}" />
<Label Text="{Binding CompletionDateInfo}" />
</StackLayout>
</local:FrameWithBorder>
</StackLayout>
PS> I'm not much experienced in Xamarin, so, any suggestions and corrections would be greatly appreciated.
EDIT: do not put listview inside scrollview
Please ignore my previous answer, this is a much better answer to my own question: I have improved my code and now I create the items dynamically in the code behind, please use the following code if you need a similar screen:
Please see that I am creating "GridExams" dynamically in the code behind:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XYZProject.Views.Exams.ExamsPage"
BackgroundColor="White"
xmlns:controls="clr-namespace:XYZProject.Controls">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="10, 20, 10, 10"
Android="0,0,0,0"/>
</ContentPage.Padding>
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="TextColor" Value="White"/>
<Setter Property="Font" Value="Medium"/>
<Setter Property="VerticalOptions" Value="Center"/>
<Setter Property="HorizontalOptions" Value="Center"/>
</Style>
<Style x:Key="LabelStyleSmall" TargetType="Label">
<Setter Property="TextColor" Value="#41a4dc"/>
<Setter Property="Font" Value="Small"/>
<Setter Property="VerticalOptions" Value="Center"/>
<Setter Property="HorizontalOptions" Value="Center"/>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<controls:Navigation/>
</Grid>
<Grid RowSpacing="0" Grid.Row="1" Padding="10,10,10,0">
<Grid.RowDefinitions>
<RowDefinition Height="145*" />
<RowDefinition Height="415*" />
<RowDefinition Height="2*" />
<RowDefinition Height="88*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" RowSpacing="10" BackgroundColor="#ed004a">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="#ed004a" Padding="30,0,0,0">
<Label Text="Your personal exam history information"
Style="{StaticResource LabelStyle}"
HorizontalOptions="StartAndExpand"/>
</Grid>
<Grid Grid.Row="1"
BackgroundColor="#0075c1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text=""
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="1"
Text="Type:"
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="2"
Text="Created:"
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="3"
Text="Finished:"
Style="{StaticResource LabelStyle}"/>
</Grid>
</Grid>
<Grid Grid.Row="1">
<ScrollView>
<Grid x:Name="GridExams">
</Grid>
</ScrollView>
</Grid>
<BoxView Grid.Row="2" Color="#0075c1" WidthRequest="100" HeightRequest="2"/>
<Grid Grid.Row="3" Padding="0,0,0,10">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="40*" />
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
BackgroundColor="{Binding DeleteButtonBackgroundColor}"
Padding="30,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
x:Name="LabelDeleteSelectedExamsPartOne"
Text="{Binding DeleteButtonText}"
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="1"
x:Name="LabelDeleteSelectedExamsPartTwo"
Text=""
Style="{StaticResource LabelStyle}"
Font="Large"/>
<Grid.GestureRecognizers IsEnabled="{Binding DeleteButtonIsEnabled}">
<TapGestureRecognizer
Command="{Binding DeleteSelectedExamsCommand}"/>
</Grid.GestureRecognizers>
</Grid>
<Grid Grid.Column="2"
BackgroundColor="#0075c1"
Padding="30,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
x:Name="LabelCreateNewExamPartOne"
Text="Create New Exam "
Style="{StaticResource LabelStyle}"/>
<Label Grid.Column="1"
x:Name="LabelCreateNewExamPartTwo"
Text=""
Style="{StaticResource LabelStyle}"
Font="Large"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding CreateNewExamCommand}"/>
</Grid.GestureRecognizers>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid Grid.Row="1"
IsVisible="{Binding IsLoading}"
BackgroundColor="Black"
Opacity="0.25">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ActivityIndicator Grid.Row="0"
IsVisible="{Binding IsLoading}"
IsRunning="{Binding IsLoading}"
VerticalOptions="End"
HorizontalOptions="Center"/>
<Label Grid.Row="1"
Text="Please wait..."
TextColor="White"
VerticalOptions="Start"
HorizontalOptions="Center"/>
</Grid>
</Grid>
</ContentPage>
Method details that creates the grid dynamically:
private void CrateExamsGridDynamically()
{
GridExams.RowDefinitions = new RowDefinitionCollection();
GridExams.BackgroundColor = Color.White;
GridExams.Padding = new Thickness(0, 5, 0, 5);
Grid childContainer = new Grid();
childContainer.HorizontalOptions = LayoutOptions.CenterAndExpand;
childContainer.VerticalOptions = LayoutOptions.CenterAndExpand;
childContainer.BackgroundColor = Color.White;
childContainer.RowDefinitions = new RowDefinitionCollection();
childContainer.ColumnDefinitions = new ColumnDefinitionCollection()
{
new ColumnDefinition
{
Width =new GridLength(1, GridUnitType.Star)
},
new ColumnDefinition
{
Width=new GridLength(4, GridUnitType.Star)
}
};
GridExams.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
List<Exam> exams = App.ExamService.GetExams();
int top = 0;
foreach (var exam in exams)
{
childContainer.RowDefinitions.Add(new RowDefinition
{
Height = new GridLength(60, GridUnitType.Absolute)
});
exam.StartDateText = exam.StartDate.HasValue ? exam.StartDate.Value.ToString("dd/MM/yy") : string.Empty;
exam.FinishedDateText = exam.FinishedDate.HasValue ? exam.FinishedDate.Value.ToString("dd/MM/yy") : "In Progress >";
string examType = string.Empty;
switch (exam.Type)
{
case ExamTypes.Undefined:
break;
case ExamTypes.Part1:
examType = "Part 1";
break;
case ExamTypes.Part2:
examType = "Part 2";
break;
case ExamTypes.Both:
break;
default:
break;
}
#region [ Left Grandchild Container ]
Grid grandChildContainerLeft = new Grid();
grandChildContainerLeft.BackgroundColor = Constants.CustomColour.RcpPurple;
grandChildContainerLeft.Padding = new Thickness(1, 1, 1, 1);
#region [ Left Great Grandchild Container ]
Grid greatGrandChildContainerLeft = new Grid();
// TapGestureRecognizer for Left Container
var grandChildContainerLeftTapGestureRecognizer = new TapGestureRecognizer();
grandChildContainerLeftTapGestureRecognizer.Tapped += GrandChildContainerLeftTapGestureRecognizer_Tapped;
greatGrandChildContainerLeft.GestureRecognizers.Add(grandChildContainerLeftTapGestureRecognizer);
greatGrandChildContainerLeft.BackgroundColor = Color.White;
greatGrandChildContainerLeft.Children.Add(new Label
{
Text = exam.Name,
TextColor = Constants.CustomColour.RcpPurple,
FontAttributes = FontAttributes.Bold,
BackgroundColor = Color.White,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand
}, 0, 0);
// This is to carry exam id
greatGrandChildContainerLeft.Children.Add(new Label
{
Text = exam.Id.ToString(),
IsVisible = false
}, 0, 0);
#endregion
grandChildContainerLeft.Children.Add(greatGrandChildContainerLeft, 0, 0);
#endregion
#region [ Right Grandchild Container ]
Grid grandChildContainerRight = new Grid();
grandChildContainerRight.BackgroundColor = Constants.CustomColour.RcpBlue;
grandChildContainerRight.Padding = new Thickness(1, 1, 1, 1);
#region [ Right Great Grandchild Container ]
Grid greatGrandChildContainerRight = new Grid();
// TapGestureRecognizer for Right Container
var grandChildContainerRightTapGestureRecognizer = new TapGestureRecognizer();
grandChildContainerRightTapGestureRecognizer.Tapped += GrandChildContainerRightTapGestureRecognizer_Tapped;
greatGrandChildContainerRight.GestureRecognizers.Add(grandChildContainerRightTapGestureRecognizer);
greatGrandChildContainerRight.BackgroundColor = Color.White;
// We need three columns for each child grid
greatGrandChildContainerRight.ColumnDefinitions = new ColumnDefinitionCollection()
{
new ColumnDefinition
{
Width =new GridLength(1, GridUnitType.Star)
},
new ColumnDefinition
{
Width=new GridLength(1, GridUnitType.Star)
},
new ColumnDefinition
{
Width=new GridLength(2, GridUnitType.Star)
}
};
// This is for type
greatGrandChildContainerRight.Children.Add(new Label
{
Text = examType,
TextColor = Constants.CustomColour.RcpBlue,
BackgroundColor = Color.White,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand,
}, 0, 0); // Exam type: the first column
// This is to carry exam id
greatGrandChildContainerRight.Children.Add(new Label
{
Text = exam.Id.ToString(),
IsVisible = false
}, 0, 0);
// This is for created date
greatGrandChildContainerRight.Children.Add(new Label
{
Text = exam.StartDateText,
TextColor = Constants.CustomColour.RcpBlue,
BackgroundColor = Color.White,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand,
}, 1, 0); // Created: the second column
// This is for finished date
greatGrandChildContainerRight.Children.Add(new Label
{
Text = exam.FinishedDateText,
TextColor = Constants.CustomColour.RcpBlue,
BackgroundColor = Color.White,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand,
}, 2, 0); // Finished: the third column
#endregion
grandChildContainerRight.Children.Add(greatGrandChildContainerRight, 0, 0);
#endregion
childContainer.Children.Add(grandChildContainerLeft, 0, top); // First Column for grandChildContainerLeft
childContainer.Children.Add(grandChildContainerRight, 1, top); // Second Column for grandChildContainerRight
top++;
}
GridExams.Children.Add(childContainer, 0, 0);
}
I develop an Universal app using MVVM-Light, and I need to manage comments on a page.
On this page, I show a List of comments, and I want to show a MenuFlyout that allows user to edit or delete its comments:
By following this link Using a Behavior to open Attached Flyouts in Windows 8.1 Store apps, I managed to show the MenuFlyout when an Item of the ListView is tapped.
=> But I would like to display the MenuFlyout only if the user is the author of the selected comment...
Is there a way to do this?
Here is my XAML:
<ListView x:Name="myComments"
ItemsSource="{Binding Comments}"
IsItemClickEnabled="True"
SelectionMode="Single"
SelectedItem="{Binding SelectedComment}"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,19,12"
HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 1. Author -->
<TextBlock Grid.Column="0"
Text="{Binding name}"
HorizontalAlignment="Stretch" VerticalAlignment="Center"
TextAlignment="Left"
Margin="0"
Foreground="{StaticResource ThemeBrush}"
Style="{StaticResource ListViewItemSubheaderTextBlockStyle}" />
<!-- 2. Date -->
<TextBlock Grid.Column="1"
Text="{Binding date, Converter={StaticResource DateToStringConverter}}"
HorizontalAlignment="Stretch" VerticalAlignment="Center"
TextAlignment="Right"
Margin="0"
Foreground="{StaticResource ThemeBrush}"
Style="{StaticResource ListViewItemSubheaderTextBlockStyle}" />
</Grid>
<!-- 3. Content -->
<TextBlock Text="{Binding content}"
TextAlignment="Left"
TextWrapping="Wrap"
Margin="0"
Foreground="Black"
FontSize="20"
Style="{StaticResource GroupHeaderTextBlockStyle}" />
<!-- MenuFlyout - with Commands -->
<FlyoutBase.AttachedFlyout>
<MenuFlyout>
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="Background" Value="{StaticResource ThemeBrush}"/>
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem Text="Edit"
Command="Binding ElementName=MyPage, Path=DataContext.EditCommentCommand}"/>
<MenuFlyoutItem Text="Delete"
Command="{Binding ElementName=MyPage, Path=DataContext.DeleteCommentCommand}"/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<!-- Behavior -->
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<local:OpenFlyoutAction />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
And here is the code-cehind:
public class OpenFlyoutAction : DependencyObject, IAction
{
public object Execute(object sender, object parameter)
{
FlyoutBase.ShowAttachedFlyout((FrameworkElement)sender);
return null;
}
}
=> Is it possible to show the FlyoutBase directly in the ViewModel instead of the code-behind?
I've first created another topic because I encountered a problem on MenuFLyoutItem and Command: XAML - MenuFlyoutItem attached to a ListView doesn't work in WP8.1
Tell me if I have to continue on this another topic and delete this one.
In complement to the answer at Depechie on the DataTemplateSelector, here is what I've done:
I create a DataTemplateSelector class called "CommentsTemplateSelector":
public class CommentsTemplateSelector : DataTemplateSelector
{
public DataTemplate UserComment { get; set; }
public DataTemplate NoUserComment { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
Commentaire comment = (Commentaire)item;
if (comment.isAuthor)
return UserComment;
else
return NoUserComment;
}
}
I declare this DataTemplateSelector and its associated templates in the page's resources:
<Page.Resources>
<!-- TemplateSelectors -->
<ts:CommentsTemplateSelector x:Key="CommentsTemplateSelector" />
<!-- DataTemplates -->
<!-- UserComment : with MenuFlyout -->
<DataTemplate x:Key="UserComment">
<Border Tapped="Border_Tapped">
<StackPanel Margin="0,0,19,12"
HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 1. Author -->
<TextBlock Grid.Column="0"
Text="{Binding name}"
... />
<!-- 2. Date -->
<TextBlock Grid.Column="1"
Text="{Binding date, Converter={StaticResource DateToStringConverter}}"
... />
</Grid>
...
</StackPanel>
<!-- MenuFlyout -->
<FlyoutBase.AttachedFlyout>
<MenuFlyout>
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="Background" Value="{StaticResource ThemeBrush}"/>
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem Text="Edit"
Command="{Binding ElementName=CommentsPage, Path=DataContext.EditCommentCommand}" />
</MenuFlyoutItem>
<MenuFlyoutItem Text="Delete"
Command="{Binding ElementName=CommentsPage, Path=DataContext.DeleteCommentCommand}" />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<local:OpenFlyoutAction />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</Border>
</DataTemplate>
<!-- NoUserComment : without MenuFlyout -->
<DataTemplate x:Key="NoUserComment">
<Border Tapped="Border_Tapped" Background="Red">
<StackPanel Margin="0,0,19,12"
HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 1. Author -->
<TextBlock Grid.Column="0"
Text="{Binding name}"
... />
<!-- 2. Date -->
<TextBlock Grid.Column="1"
Text="{Binding date, Converter={StaticResource DateToStringConverter}}"
... />
</Grid>
...
</StackPanel>
</Border>
</DataTemplate>
...
<Page.Resources>
Finally, I affected the ItemTemplateSelector of my ListView to the DataTemplateSelector:
<ListView x:Name="myCommentaires"
ItemsSource="{Binding Commentaires}"
IsItemClickEnabled="True"
ItemTemplateSelector="{StaticResource CommentsTemplateSelector}"
SelectionMode="Single"
SelectedItem="{Binding SelectedCommentaire, Mode=TwoWay}"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
When I launch the app, I go into the class "CommentsTemplateSelector" and it works fine, but the ListView only shows the model name for each items:
So I think there is a Binding problem in the DataTemplates but what is it?
Finally it worked after monvig the templates from the page's resources into ListView.ItemTemplateSelector:
<ListView.ItemTemplateSelector>
<ts:CommentsTemplateSelector>
<!-- UserComment : with MenuFlyout -->
<ts:CommentsTemplateSelector.UserComment>
<DataTemplate>
<Border Tapped="Border_Tapped">
<StackPanel Margin="0,0,19,12"
HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 1. Author -->
<TextBlock Grid.Column="0"
Text="{Binding name}"
... />
<!-- 2. Date -->
<TextBlock Grid.Column="1"
Text="{Binding date, Converter={StaticResource DateToStringConverter}}"
... />
</Grid>
...
</StackPanel>
<!-- MenuFlyout -->
<FlyoutBase.AttachedFlyout>
<MenuFlyout>
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="Background" Value="{StaticResource ThemeBrush}"/>
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem Text="Edit"
Command="{Binding ElementName=CommentsPage, Path=DataContext.EditCommentCommand}" />
</MenuFlyoutItem>
<MenuFlyoutItem Text="Delete"
Command="{Binding ElementName=CommentsPage, Path=DataContext.DeleteCommentCommand}" />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<local:OpenFlyoutAction />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</Border>
</DataTemplate>
</ts:CommentsTemplateSelector.UserComment>
<!-- NoUserComment : without MenuFlyout -->
<ts:CommentsTemplateSelector.NoUserComment>
<DataTemplate>
<Border Background="Red">
<StackPanel Margin="0,0,19,12"
HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 1. Author -->
<TextBlock Grid.Column="0"
Text="{Binding name}"
... />
<!-- 2. Date -->
<TextBlock Grid.Column="1"
Text="{Binding date, Converter={StaticResource DateToStringConverter}}"
... />
</Grid>
...
</StackPanel>
</Border>
</DataTemplate>
</ts:CommentsTemplateSelector.UserComment>
</ts:CommentsTemplateSelector>
<ListView.ItemTemplateSelector>
I don't know why it hasn't worked into the page's resources as I found some samples like this...
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