Coming from my original post, I was trying to create a custom control and build a control template for it much like in WPF, but it failed on triggers. I was provided a link that indicated that Control Templates are not allowed on all controls, but only certain ones via another Stack Overflow link provided by Raimo on my other question.
So now on to part 2. I have changed the control from an "Entry" type to a "ContentView" type so a control template can be applied. So that was the only thing changed
namespace MyAndroidApp
{
public class MyTextboxEntry : ContentView
{
public MyTextboxEntry()
{}
public static readonly BindableProperty IsRequiredProperty
= BindableProperty.Create(nameof(IsRequired), typeof(bool),
typeof(MyTextboxEntry), false, BindingMode.TwoWay);
public bool IsRequired
{
get { return (bool)GetValue(IsRequiredProperty); }
set { SetValue(IsRequiredProperty, value); }
}
}
}
Namespace references..
xmlns:myCtrls="clr-namespace:MyAndroidApp"
Now, on to the control template. I am actually defining multiple parts in the control but have abbreviated here for simple context.
<ControlTemplate x:Key="CT_MyTextboxEntry" >
<!-- Having a grid as an outer wrapper for multiple internal controls... -->
<Grid HorizontalOptions="StartAndExpand" x:Name="UpdateThisGridControl" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150*" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="16" />
</Grid.ColumnDefinitions>
<Entry Text="Sample" Grid.Column="0" />
<Label Text="junk" Grid.Column="1" />
<Image Source="Sample.jpg" Grid.Column="2" />
<Grid.Triggers>
<Trigger TargetType="myCtrls:MyTextboxEntry" Property="IsRequired" Value="True">
<Setter Property="BackgroundColor" Value="Red"/>
</Trigger>
</Grid.Triggers>
</Grid>
</ControlTemplate>
And the final instance of the control in the page to be displayed via
<myCtrls:MyTextboxEntry ControlTemplate="{StaticResource CT_MyTextboxEntry}" />
So, my failure is within the Grid.Triggers section. The intent here is to use the bindable property of the new entry control (based on ContentView) with proper BindableProperty declaration for "IsRequired", but to set the background color of the GRID.
In WPF, you can say use the trigger from this thing here, but update this other control within the control template as referenced by an "x:Name" reference which I have in-place as "UpdateThisGridControl".
If I disable the [Grid.Triggers] part, the control and template show as expected. As soon as I enable the trigger, it fails with
"bindable" not an instance of AssociatedType
But, if you take into consideration that an entry ContentView has a background color property and so too does the Grid control, why is it choking when trying to set via trigger.
How can I do this type of binding for the property from the custom control to update a property within the specific control template?
If you used Grid.Triggers, you should set the TargetType="Grid" in the Trigger Tab like following code.
<ControlTemplate x:Key="CT_MyTextboxEntry" >
<!-- Having a grid as an outer wrapper for multiple internal controls... -->
<Grid HorizontalOptions="StartAndExpand" x:Name="UpdateThisGridControl" IsEnabled="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150*" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="16" />
</Grid.ColumnDefinitions>
<Entry Text="Sample" Grid.Column="0" />
<Label Text="junk" Grid.Column="1" />
<Image Source="Sample.jpg" Grid.Column="2" />
<!-- For testing, I set the value of `Property` is `IsEnabled`, it worked as normal -->
<Grid.Triggers>
<Trigger TargetType="Grid" Property="IsEnabled" Value="True">
<Setter Property="BackgroundColor" Value="Red"/>
</Trigger>
</Grid.Triggers>
</Grid>
</ControlTemplate>
But you want to set the TargetType="myCtrls:MyTextboxEntry" for Trigger, you can try to use Style to achieve it for myCtrls:MyTextboxEntry.
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="myCtrls:MyTextboxEntry">
<Style.Triggers>
<Trigger TargetType="myCtrls:MyTextboxEntry"
Property="IsRequired" Value="True">
<Setter Property="BackgroundColor" Value="Red" />
<!-- Multiple setters elements are allowed -->
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
<ControlTemplate x:Key="CT_MyTextboxEntry" >
<!-- Having a grid as an outer wrapper for multiple internal controls... -->
<Grid HorizontalOptions="StartAndExpand" x:Name="UpdateThisGridControl" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150*" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="16" />
</Grid.ColumnDefinitions>
<Entry Text="Sample" Grid.Column="0" />
<Label Text="junk" Grid.Column="1" />
<Image Source="Sample.jpg" Grid.Column="2" />
<!-- <Grid.Triggers>
<Trigger TargetType="Grid" Property="IsEnabled" Value="True">
<Setter Property="BackgroundColor" Value="Red"/>
</Trigger>
</Grid.Triggers> -->
</Grid>
</ControlTemplate>
</ContentPage.Resources>
<StackLayout>
<myCtrls:MyTextboxEntry ControlTemplate="{StaticResource CT_MyTextboxEntry}" IsRequired="True" />
</StackLayout>
Here is a running screenshot.
Related
I am currently working with a UWP project in Xamarin Forms.
When i use the default SearchBar, it comes with a border that i wish to remove as well as add a rounded background.
I have setup the renderer and some code, but the border is still intact.
public class SearchBar_UWP : SearchBarRenderer
{
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
Control.Background = null;
Control.BorderBrush = null;
}
}
What am I missing in my UWP custom renderer code in order to remove the border and add a rounded background?
Remove border
You could copy the default style and modify BorderThickness property (in test i found we need to change the property on TextBox inside AutoSuggestBox ), then place the new style into Application.Resources , at last apply the style in custom renderer .
custom style in App
<Application.Resources>
<Style TargetType="AutoSuggestBox" x:Key="myStyle">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="TextBoxStyle" Value="{StaticResource AutoSuggestBoxTextBoxStyle}" />
<Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="AutoSuggestBox">
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Orientation">
<VisualState x:Name="Landscape" />
<VisualState x:Name="Portrait" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox x:Name="TextBox"
Style="{TemplateBinding TextBoxStyle}"
PlaceholderText="{TemplateBinding PlaceholderText}"
Header="{TemplateBinding Header}"
Width="{TemplateBinding Width}"
BorderThickness="0"
ScrollViewer.BringIntoViewOnFocusChange="False"
Canvas.ZIndex="0"
Margin="0"
DesiredCandidateWindowAlignment="BottomEdge"
UseSystemFocusVisuals="{TemplateBinding UseSystemFocusVisuals}" />
<Popup x:Name="SuggestionsPopup">
<Border x:Name="SuggestionsContainer">
<ListView x:Name="SuggestionsList"
Background="{ThemeResource AutoSuggestBoxSuggestionsListBackground}"
BorderThickness="{ThemeResource AutoSuggestListBorderThemeThickness}"
BorderBrush="{ThemeResource AutoSuggestBoxSuggestionsListBorderBrush}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
IsItemClickEnabled="True"
ItemTemplate="{TemplateBinding ItemTemplate}"
ItemTemplateSelector="{TemplateBinding ItemTemplateSelector}"
ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
MaxHeight="{ThemeResource AutoSuggestListMaxHeight}"
Margin="{ThemeResource AutoSuggestListMargin}"
Padding="{ThemeResource AutoSuggestListPadding}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
custom renderer
[assembly: ExportRenderer(typeof(Xamarin.Forms.SearchBar), typeof(MyRenderer))]
namespace App1.UWP
{
class MyRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.Style = App.Current.Resources["myStyle"] as Windows.UI.Xaml.Style;
}
}
}
}
Add Rounded background
Warp SearchBar into Frame , and set CornerRadius on Frame .
<Frame HeightRequest="100" WidthRequest="100" VerticalOptions="Center" HorizontalOptions="Center" BorderColor="Red" CornerRadius="50">
<SearchBar />
</Frame>
I have a Frame with a StackLayout inside of it:
<Frame CornerRadius="1" HasShadow="false" Margin="10"
BackgroundColor="White" BorderColor="Silver" Padding="0" >
<StackLayout Orientation="Vertical" Spacing="0" Padding="0" >
<xaml:PtiXaml />
<template:LineTemplate />
<xaml:AtiXaml />
<template:LineTemplate />
<xaml:StiXaml />
</StackLayout>
</Frame>
Can I create a new object called NewFrame that is the same as the Frame with the StackLayout inside?
<template:NewFrame>
<xaml:PtiXaml />
<template:LineTemplate />
<xaml:AtiXaml />
<template:LineTemplate />
<xaml:StiXaml />
</template:NewFrame>
or
<template:NewFrame>
<xaml:ABCXaml />
</template:NewFrame>
or
<template:NewFrame>
<Label Text="X" />
</template:NewFrame>
It was suggested I use a Custom View but I have looked and can not find an example of this where it contains other elements inside.
Right-Click at the desired position in your Shared Project (or PCL) in your Solution Explorer (I would recommend adding a folder named "Views" or "CustomViews" and creating the item inside that folder), select "Add new item" and choose "Content View" (without (C#) behind it. The filename should be something like "View1.xaml", you can change that due to your liking, however the important thing is that the xaml extension is there.
This will create a new ContentView with a xaml and xaml.cs file.
Inside the xaml file you can declare your xaml code posted above and write any code necessary into the xaml.cs file.
Now you can add a namespace declaration to the page you want to put your view into:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
...
xmlns:customs="clr-namespace:YourNamespace.Views;assembly=YourNamespace"
and declare the element in that Page's or any layout's content:
<customs:CustomViewName ... />
If you want to be able to control the element's behaviour you can add BindableProperties in the codebehind.
For more in-depth information on that, you might want to take a look into this article: https://visualstudiomagazine.com/articles/2017/10/01/add-custom-controls.aspx
Use a ContentView along with a ControlTemplate to create a Custom Control. This way you can create a new control called NewFrame, write the XAML for your control and then use the <ContentPresenter> tag inside your <ControlTemplate> to assign where you'd like your content to be.
Like so:
.
└── NewFrame
├── NewFrame.cs
└── NewFrame.xaml -> Is a ResourceDictionary
NewFrame.cs:
namespace TestApp.Controls
{
public partial class NewFrame : ContentView
{
}
}
NewFrame.xaml:
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:newFrame="clr-namespace:TestApp.Controls"
x:Class="Namespace.For.A.ResourceDictionary">
<Style TargetType="newFrame:NewFrame">
<Setter Property="ControlTemplate">
<Setter.Value>
<ControlTemplate>
<ContentView BackgroundColor="Transparent">
<Frame CornerRadius="1" HasShadow="false" Margin="10" BackgroundColor="White" BorderColor="Silver" Padding="0" >
<StackLayout Orientation="Vertical" Spacing="0" Padding="0">
<ContentPresenter/>
</StackLayout>
</Frame>
</ContentView>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
ConsumingYourControl.xaml:
<template:NewFrame>
<template:NewFrame.Content>
<xaml:PtiXaml />
<template:LineTemplate />
<xaml:AtiXaml />
<template:LineTemplate />
<xaml:StiXaml />
</template:NewFrame.Content>
</template:NewFrame>
<template:NewFrame>
<template:NewFrame.Content>
<xaml:ABCXaml />
</template:NewFrame.Content>
</template:NewFrame>
<template:NewFrame>
<template:NewFrame.Content>
<Label Text=""/>
</template:NewFrame.Content>
</template:NewFrame>
If I have this code:
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:HOV Grid.Row="0" />
<local:HST Grid.Row="1" />
<local:HAC Grid.Row="2" />
<local:HAI Grid.Row="3" />
</Grid>
and I want to set the color of the text in everything inside the grid can I just specify:
TextColor="Gray"
in the and then everything inside there will be Gray color?
Grid doesn't support the TextColor property. You can use an implicit style and color all the labels on a content page like this:
<ContentPage.Resources>
<Style TargetType="Label">
<Setter Property="TextColor" Value="Gray"></Setter>
</Style>
</ContentPage.Resources>
You can create a named style like this:
<ContentPage.Resources>
<Style x:Key="lblStyle" TargetType="Label">
<Setter Property="TextColor" Value="Red"></Setter>
</Style>
</ContentPage.Resources>
And apply it to a label like this:
<Label Style="{StaticResource lblStyle}" Text="Test" Grid.Row="0" />
No, this is not possible. You will either have to create an implicit style or set the color to gray as default on your inherited controls.
I have an application that consists of a TabbedPage that holds custom ContentViews.
In the main ContentView I have a custom ListView, that has a ListViewRenderer in Xamarin UWP.
The ListView items have a template:
<DataTemplate x:Key="ListViewItemTemplate">
<Grid x:Name="grid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- TEXT -->
<TextBlock Grid.Column="0"
Margin="5"
VerticalAlignment="Center"
Foreground="Black"
Text="{Binding ActualText}"
TextWrapping="WrapWholeWords" />
<!-- CC -->
<my:CustomControl Grid.Column="1"
ActualCValue="{Binding ActualValue, Mode=TwoWay}" />
<!-- PICKER -->
<ComboBox x:Name="cboxPicker"
Grid.Column="2"
Width="90"
Margin="3"
VerticalAlignment="Center"
ItemsSource="{StaticResource dataSource}"
SelectedIndex="{Binding ActualValue,
Mode=TwoWay}" />
<!-- SEP -->
<Border Grid.Row="1"
Grid.ColumnSpan="3"
BorderBrush="Gray"
BorderThickness="1" />
</Grid>
</DataTemplate>
Unfortunately, when I try to use swipe, the tabs are not changing.
If I use the built-in Xamarin.Forms ListView with items consisting of an image and text, it works normally.
It might be related to the Button's capturing of the focus (assumption).
What should I change to make my custom ListViewRenderer work with swipe?
As I was using a ListViewRenderer, there was no ListView in the XAML code (it is only provided by the renderer).
The problem however was with the ListView configuration I used in my custom renderer.
I had this:
(Control as SemanticZoom)?.ManipulationMode = Windows.UI.Xaml.Input.ManipulationModes.None;
Now I use this, and the swipe works normally:
(Control as SemanticZoom)?.ManipulationMode = Windows.UI.Xaml.Input.ManipulationModes.System;
I have a master/details page with a list view and a content presenter. My problem is that the content presenter does not fill the entire remaining space of the parent grid when the content of the text block is smaller the current window.
I have tried adding both HorizontalAlign='Stretch' and VerticalAlign='Stretch' as well but nothing works.
Here's the code for the MasterDetailPage.xaml
<Page.Resources>
<DataTemplate x:Key="MasterListViewItemTemplate" x:DataType="data:List">
<Grid Margin="0,11,0,13">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind Title}" Style="{ThemeResource BaseTextBlockStyle}" />
<TextBlock Style="{ThemeResource CaptionTextBlockStyle}"
Text="{x:Bind Date}"
Grid.Column="1"
Margin="12,1,0,0" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="DetailContentTemplate" x:DataType="data:List">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel
Orientation="Vertical"
Margin="0,9,12,9">
<TextBlock
Margin="0,8"
Style="{ThemeResource TitleTextBlockStyle}"
TextWrapping="WrapWholeWords"
Text="{x:Bind Title}"/>
<RichTextBlock
x:Name="textContent"
IsTextSelectionEnabled="True"
TextWrapping="WrapWholeWords"
common:Html2TextParser.Html="{x:Bind Content}"/>
</StackPanel>
</ScrollViewer>
</DataTemplate>
<DataTemplate x:Key="comboListTemplate" x:DataType="data:Combo">
<TextBlock Text="{x:Bind Title}"/>
</DataTemplate>
</Page.Resources>
<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded" UseLayoutRounding="True">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="AdaptiveStates" CurrentStateChanged="AdaptiveStates_CurrentStateChanged">
<VisualState x:Name="DefaultState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="NarrowState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MasterColumn.Width" Value="*" />
<Setter Target="DetailColumn.Width" Value="0" />
<Setter Target="MasterListView.SelectionMode" Value="None" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="MasterColumn" Width="320" />
<ColumnDefinition x:Name="DetailColumn" Width="*" />
</Grid.ColumnDefinitions>
<ComboBox
Name="comboList"
Margin="2,2,2,2"
ItemsSource="{x:Bind comboSource.combos}"
LayoutUpdated="comboList_LayoutUpdated"
SelectionChanged="comboList_SelectionChanged"
ItemTemplate="{StaticResource comboListTemplate}"
HorizontalAlignment="Stretch" />
<ListView
x:Name="MasterListView" ItemsSource="{x:Bind listSource.lists}"
Grid.Row="1"
ItemContainerTransitions="{x:Null}"
ItemTemplate="{StaticResource MasterListViewItemTemplate}"
IsItemClickEnabled="True"
ItemClick="MasterListView_ItemClick">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Column="1"
Grid.RowSpan="2"
BorderThickness="1,0,0,0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}"
ContentTemplate="{StaticResource DetailContentTemplate}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="12,0,0,0">
<ContentPresenter.ContentTransitions>
<!-- Empty by default. See MasterListView_ItemClick -->
<TransitionCollection />
</ContentPresenter.ContentTransitions>
</ContentPresenter>
</Grid>
Can anyone solve this issue? Here's a screenshot my problem.
You could try to put your contentpresenter inside a ViewBox. The ViewBox is designed to stretch and scale a single child to fill the avialable space.
Documentation here
I hope that helps you :)
I tested your xaml and I can not reproduce your problem Please see the screenshot
I the blue space is your contentPresenter, the orange space is the ScrollViewer and finally your textblocks with dummy text.
After setting different colors to the ContentPresenter and the main grid itself, I found that even my main grid "LayoutRoot" was not expanding to full width!
The reason being that I was using a SplitView and I was navigating my MasterDetailPage.xaml inside the SpiltView's Content element. I simply set HorizontalAlign='Stretch' for the SplitView and it solved my issue!!