I have a custom Expander that I've created as a template:
<local:MultiplyConverter x:Key="multiplyConverter" />
<ControlTemplate x:Key="AnimatedExpander" TargetType="{x:Type Expander}">
<DockPanel>
<ToggleButton x:Name="ExpanderButton" DockPanel.Dock="Top" Template="{StaticResource ExpanderButton}" Content="{TemplateBinding Header}" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" OverridesDefaultStyle="True">
</ToggleButton>
<ScrollViewer x:Name="ExpanderContentScrollView" DockPanel.Dock="Bottom" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Bottom">
<ScrollViewer.Tag>
<sys:Double>0.0</sys:Double>
</ScrollViewer.Tag>
<ScrollViewer.Height>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</ScrollViewer.Height>
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content" />
</ScrollViewer>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView" Storyboard.TargetProperty="Tag" To="1" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView" Storyboard.TargetProperty="Tag" To="0" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
When I actually go to instantiate the Expander inside a grid, the code looks like this:
<Expander Template="{StaticResource AnimatedExpander}" ExpandDirection="Down" OverridesDefaultStyle="True" FontSize="11.0" Foreground="#CC000000" Header="Export Options" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Column="0" CornerRadius="0" Background="Transparent" Margin="10,0,10,0" BorderBrush="#FFAAAAAA" BorderThickness="0,0,0,0.5">
<ComboBox Grid.Column="0" ItemsSource="{Binding CurrentSlicerManager.Exporters}" DisplayMemberPath="Name" SelectedItem="{Binding CurrentSlicerManager.SelectedExporter, Mode=TwoWay}" Grid.Row="0" Margin="0,0,0,10"></ComboBox>
</Border>
<ContentControl Content="{Binding CurrentSlicerManager}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"></ContentControl>
</Grid>
</Expander>
As you can see, I've bound the content inside the Expander to a ContentControl. The ContentControl uses a DataTemplate to determine which .xaml I should load into the Expander. So, I have two DataTemplates that look like this:
<DataTemplate DataType="{x:Type MLC:MLRasterSlicerManager}">
<local:MLRasterSlicerExportSettings></local:MLRasterSlicerExportSettings>
</DataTemplate>
<DataTemplate DataType="{x:Type MLC:MLVectorSlicerManager}">
<local:MLVectorSlicerExportSettings></local:MLVectorSlicerExportSettings>
</DataTemplate>
So, I have two .xaml files (MLRasterSlicerExportSettings and MLVectorSlicerExportSettings) which get loaded... and this works fine. The issue is that the height of these two interfaces are different. When, I change the combobox which controls which file to load into the expander, I would expect the expander to automatically resize itself. And, this does happen when it first loads the expander with the smaller height and then I switch it to load the one with the larger height value... the expander does resize itself to accommodate the larger file... but if I switch it back to the smaller one, then it never resizes to the appropriate height. It just remains the same height as the largest file. Can anyone recommend how to get this expander to automatically resize to the size of the content?
Related
I have a grid inside a CollectionView and I need to change the layout based on a certain condition, changing dynamically the size of the columns and rows and hiding/showing some specific columns.
The datasource of the collectionView is always the same.
Basically, I want to achieve something like this:
Default layout:
Alternative layout:
I saw that maybe I could use the DataTemplateSelectors, but is that the best way or is there another method?
You can use the Grid in the Collectionview and set the Grid format.
First, you can set the CollectionView ItemsLayout. Here is the example.
<CollectionView ItemsSource="{Binding Items}"
ItemsLayout="VerticalGrid, 3">
it will divide the CollectionView list into three vertical parts.
Second, you can make a Grid to preserve the data.
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Row="1"
Text="{Binding Location}" />
</Grid>
I work on the front end of our app, configuring the UI. Our app originally had a ListView using Xamarin Community Toolkit's EventToCommandBehavior with custom ViewCells to facilitate the search. We set up custom renderers to change the ViewCell's background color. However, when I moved over to the Mac, we discovered that none of that worked with iOS; the cell background would not change.
At this point, I switched to a CollectionView with a TapGestureRecognizer on the grid using VisualStateManager to change the background color. Since then, I can either get the grid selection to highlight without the command enabling our buttons or the command to fire which enables the buttons, but the grid selection will not highlight. Simply put, I either get the command or the event but have been unable to get them working together. This is also a problem on our store page; the IAP's never highlighted before the purchase confirmation popped up. We have been experiencing this issue from the start and never noticed.
Here is the relevant XAML:
<!--Faction Search Results-->
<Frame Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="6" BackgroundColor="Black" BorderColor="White" CornerRadius="5">
<CollectionView ItemsSource="{Binding FactionItems}"
BackgroundColor="Transparent">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid x:DataType="Data:Faction" BackgroundColor="Black">
<Grid.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:FactionTopPageViewModel}}, Path=ItemTapped}"
CommandParameter="{Binding .}">
</TapGestureRecognizer>
</Grid.GestureRecognizers>
<Grid.RowDefinitions>
<RowDefinition Height="{markups:OnScreenSize DefaultSize='12', iPod='20', iPhoneSE='20', iPhoneXR='20', iPhoneX='20', iPhone13='25', iPhone7p='25', iPhone11pm='25', iPhone13pm='16',
iPadMini='35', iPad9p7='35', iPad='35', iPadAir='35', iPad11='35', iPad12p9='40', Nexus1='20', NexusR='25', Nexus7R='30', Pixel2R='25', Pixel3R='25', GalaxyS8='25', Nexus6P='25', Pixel3XL='25', PixelC='40'}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Padding="10,5" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" Text="{Binding FactionName}" HorizontalOptions="Start" VerticalTextAlignment="End" FontAttributes="Bold"
FontSize="{markups:OnScreenSize DefaultSize='12', iPod='15', iPhoneSE='15', iPhoneXR='18', iPhoneX='19', iPhone13='19', iPhone7p='19', iPhone11pm='19', iPhone13pm='16',
iPadMini='30', iPad9p7='30', iPad='32', iPadAir='32', iPad11='34', iPad12p9='34', Nexus1='14', NexusR='14', Nexus7R='22', Pixel2R='20', Pixel3R='20', GalaxyS8='20', Nexus6P='18', Pixel3XL='18', PixelC='34'}" />
<Label Padding="0,5" Grid.Row="0" Grid.Column="4" Grid.ColumnSpan="2" Text="{Binding NumberMembersString}" HorizontalOptions="Center" VerticalTextAlignment="End" FontAttributes="Bold"
FontSize="{markups:OnScreenSize DefaultSize='12', iPod='15', iPhoneSE='15', iPhoneXR='18', iPhoneX='19', iPhone13='19', iPhone7p='19', iPhone11pm='19', iPhone13pm='16',
iPadMini='30', iPad9p7='30', iPad='32', iPadAir='32', iPad11='34', iPad12p9='34', Nexus1='14', NexusR='14', Nexus7R='22', Pixel2R='20', Pixel3R='20', GalaxyS8='20', Nexus6P='18', Pixel3XL='18', PixelC='34'}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal"></VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#ae00ff"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Relevant code from the ViewModel:
public Command<Faction> ItemTapped { get; }
public FactionTopPageViewModel()
{
ItemTapped = new Command<Faction>(OnItemSelected);
}
private void OnItemSelected(Faction faction)
{
if (faction == null)
{
SelectedItem = null;
DisableFactionButtons();
return;
}
EnableFactionButtons();
SelectedItem = faction;
}
This results in our buttons enabled at the bottom of the page, but the faction (essentially a team) is not being highlighted in the CollectionView, so the user does not receive confirmation of selecting the proper faction.
If I remove the tapgesture and work through the CollectionView commands setting the SelectionMode="Single", SelectionChangedCommand={Binding IsTapped}, and SelectionChangedCommandParameter={Binding .} then the selected Faction highlights without enabling any buttons. These issues are not platform-specific, like the ListView ViewCell background.
I have been working on this longer than I care to mention, as it seems like I have not wrapped my head around some relatively simple concept. Any help would be greatly appreciated, this issue is holding up the next release of our app.
I have added a Style to the ResourceDictionary. The ControlTemplate contains a Grid with one Slider and two TextBlocks.
My problem is that i dont know how i can bind the textblock text. Is there any easy way to do it?
This is the code, how i would like to use the template in my view.xaml
<Slider Style="{StaticResource ComboSliderTheme}"
Minimum="0"
Maximum="100"
*Textbox myTitle ="Time"
*Textbox myValue ="23"/>
And this is the code i have added to the resource dictionary so far.
How do i have to modify the Textblock text= to get the wished result?
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Slider}"
x:Key="ComboSliderTheme">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Grid VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Height="50"
Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock *Text="My Title" (Value to Bind)
Grid.Column="0"
Grid.Row="0"
/>
<TextBlock *Text="My Value" (Value to Bind)
Grid.Column="1"
Grid.Row="0"
/>
<Slider Grid.Row="1"
Grid.ColumnSpan="2"
Minimum="{TemplateBinding Property=Minimum}"
Maximum="{TemplateBinding Property=Maximum}"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>
Thanks
I have the following XAML in my Xamarin Forms project
<StackLayout x:Name="slFullPage" Orientation="Vertical" HorizontalOptions="Fill" VerticalOptions="FillAndExpand" Margin="0,0,0,0" BackgroundColor="AliceBlue">
<localapp:PageHeader x:Name="pgHeader" VerticalOptions="Start" HorizontalOptions="Fill" />
<combobox:SfComboBox x:Name="cmbLanguage" DataSource="{Binding LangaugesByAppLanguage}" TextSize="Micro"
TextColor="#283655" SelectionChanged="cmbLanguage_SelectionChanged"
Watermark="{localapp:Translate SelectValue}" DropDownItemHeight="25" WidthRequest="250" HeightRequest="30"
DropDownTextSize="Micro" BackgroundColor="White" HorizontalOptions="Center" VerticalOptions="Start"
DisplayMemberPath="LanguageName" SelectedValuePath="ISO_639_1_Code">
</combobox:SfComboBox>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="End" BackgroundColor="Aqua">
<buttons:SfButton x:Name="btnCancel" Text="{localapp:Translate Cancel}" HorizontalOptions="Start" VerticalOptions="End" Margin="8,0,4,0"/>
<buttons:SfButton x:Name="btnDone" Text="{localapp:Translate Done}" HorizontalOptions="End" VerticalOptions="End" Margin="4,0,8,0"/>
</StackLayout>
<localapp:AppFooter x:Name="pgFooter" VerticalOptions="End" HorizontalOptions="Fill" />
</StackLayout>
Based on my understanding of the horizontal options that can be specified on an object, I would expect the Cancel button to be at the start of my horizontal stack layout and my Done button to be at the end of the same stack layout.
Additionally, based on the vertical options that can be used, I would expect the stacklayout with the buttons in it as well as my appfoot to appear at the bottom of my slFullPage stack layout. However, neither of those things are happening.
Instead what I get can be seen below:
You can see that the entire page stacklayout is going all the way to the bottom of the screen but my buttons and footer are not at the bottom.
Additinally, you can see the horizontal stack layout (aqua) goes all the way across the screen but the buttons are not positioned based on their horizontal options.
Any ideas?? FYI - I am running the project as a UWP app.
Based on my understanding of the horizontal options that can be specified on an object, I would expect the Cancel button to be at the start of my horizontal stack layout and my Done button to be at the end of the same stack layout.
You could use a AbsoluteLayout to make the buttons positioned anywhere within a view.
<AbsoluteLayout BackgroundColor="LightBlue" HeightRequest="60" VerticalOptions="EndAndExpand">
<Button Text="Cancel" Margin="10,10" BackgroundColor="DarkBlue" TextColor="White"/>
<Button Text="Done" Margin="10,10" AbsoluteLayout.LayoutBounds="1, 0, AutoSize, AutoSize" AbsoluteLayout.LayoutFlags="PositionProportional" BackgroundColor="DarkBlue" TextColor="White"/>
</AbsoluteLayout>
And use the VerticalOptions="EndAndExpand" to make it appears at the end of its parent and expands.
Updated:
I do not know what it was before, but for now, it doesn’t actually fill up the space in the StackLayout. It’s only when the fill option is used, that it will expand to that space.
When you expect the Cancel button to be at the start and the Done button to be at the end of the same stack layout, fill option and expend need to be setted. I use simple example to display.
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand" BackgroundColor="Aqua">
<Button x:Name="btnCancel" Text="Cancel" HorizontalOptions="StartAndExpand" Margin="8,0,4,0"/>
<Button x:Name="btnDone" Text="Done" HorizontalOptions="EndAndExpand" Margin="4,0,8,0"/>
</StackLayout>
And if you do not want space between last two lines in the end, you need a nested stacklayout to do that without space.
Simple code:
<StackLayout BackgroundColor="LightGray">
<StackLayout>
<Entry x:Name="pgHeader" Text="pgHeader" HorizontalOptions="FillAndExpand" />
<Entry x:Name="cmbLanguage" Text="cmbLanguage" WidthRequest="250" HeightRequest="30"
BackgroundColor="Red" HorizontalOptions="CenterAndExpand" ></Entry>
</StackLayout>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand">
<StackLayout Orientation="Horizontal" BackgroundColor="Aqua">
<Button x:Name="btnCancel" Text="Cancel" HorizontalOptions="StartAndExpand" Margin="8,0,4,0"/>
<Button x:Name="btnDone" Text="Done" HorizontalOptions="EndAndExpand" Margin="4,0,8,0"/>
</StackLayout>
<Entry x:Name="pgFooter" Text="pgFooter"/>
</StackLayout>
</StackLayout>
You don't want to use a StackLayout (well 2 StackLayout), but a Grid:
It's more convenient for your layout, and it is way more performant.
<Grid ColumnSpacing="0" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinition>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinition>
<localapp:PageHeader Grid.Row="0" x:Name="pgHeader" />
<combobox:SfComboBox Grid.Row="1" x:Name="cmbLanguage" DataSource="{Binding LangaugesByAppLanguage}" TextSize="Micro"
TextColor="#283655" SelectionChanged="cmbLanguage_SelectionChanged"
Watermark="{localapp:Translate SelectValue}" DropDownItemHeight="25" WidthRequest="250"
DropDownTextSize="Micro" BackgroundColor="White" HorizontalOptions="Center"
DisplayMemberPath="LanguageName" SelectedValuePath="ISO_639_1_Code">
</combobox:SfComboBox>
<buttons:SfButton Grid.Row="2" Grid.Column="0"
x:Name="btnCancel" Text="{localapp:Translate Cancel}" Margin="8,0,4,0"/>
<buttons:SfButton Grid.Row="2" Grid.Column="2"
x:Name="btnDone" Text="{localapp:Translate Done}" Margin="4,0,8,0"/>
</Grid>
I tried to style my listview template, which contains a gridview. I tried to do it using an ItemsPresenter and it works fine but my gridview's header disappears.
What should i use to preserve my header?
<Style TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Border x:Name="Bd"
SnapsToDevicePixels="true">
<ScrollViewer Focusable="false">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit : My Listview
<ListView Name="findReplaceView" Margin="10" Grid.Row="1" Grid.Column="0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
ItemsSource="{Binding FindAndReplaceItems}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Find" DisplayMemberBinding="{Binding Find}"
Width="{Binding ElementName=helperField1, Path=ActualWidth}"/>
<GridViewColumn Header="Replace" DisplayMemberBinding="{Binding Replace}"
Width="{Binding ElementName=helperField2, Path=ActualWidth}"/>
</GridView>
</ListView.View>
</ListView>
It's because you didn't style the header part. This is how you should do it:
<Style TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- This is how your headers are presented -->
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=ListView},Path=View.Columns}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="LightGray" Background="MidnightBlue">
<TextBlock Foreground="WhiteSmoke"
TextAlignment="Center"
FontWeight="Bold"
Text="{Binding Header}"
Width="{Binding ActualWidth}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!-- This is how your items are presented -->
<Border x:Name="Bd" Grid.Row="1"
SnapsToDevicePixels="true">
<ScrollViewer Focusable="false">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In the above code there are 2 Grid.RowDefinitions with height="Auto" and height="*" the first one is the area (i.e. the top row) that represents the header part and the second one is the remaining area that contains your ItemsPresenter and determines how your items are presented.
Edit
Since this is suppose to be a general style for a ListView that it's columns are already defined in some ListView.View, I thought using an ItemsControl for the headers part would make your style quite flexible. I modified my code sample accordingly in which the headers part is an ItemsControl with horizontal orientation that it's ItemsSource is bound to ListView.View.Columns. This way columns count in the style will depend on columns count in your ListView. Also the ItemsControl.ItemTemplate is a TextBlock that it's text is bound to the header of the respective column, therefore columns header in the style will be the same as columns headers in your ListView.