I've created a custom renderer for Frame to feel like CardView in Android it works fine on Android P but i've tested on API 21,22,23 it doesn't have any kind of effect. Here is my Android Renderer.
public class ShadowFrameRenderer : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
{
public ShadowFrameRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && e.NewElement is ShadowFrame)
{
Elevation = 30.0f;
TranslationZ = 0.0f;
SetZ(30f);
//this.SetBackgroundResource(Resource.Drawable.shadow);
//GradientDrawable drawable = (GradientDrawable)this.Background;
//drawable.SetColor(Android.Graphics.Color.ParseColor("#F0F0F0"));
}
UpdateElevation();
}
private void UpdateElevation()
{
//var materialFrame = (ShadowFrame)Element;
// we need to reset the StateListAnimator to override the setting of Elevation on touch down and release.
if(Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
Control.StateListAnimator = new Android.Animation.StateListAnimator();
// set the elevation manually
ViewCompat.SetElevation(this, 10);
ViewCompat.SetElevation(Control, 10);
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
{
Control.Elevation = 10;
Control.CardElevation = 10;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
UpdateElevation();
//if (e.PropertyName == "Elevation")
//{
// UpdateElevation();
//}
}
}
Here is XAML.
<ContentPage.Content>
<controls:ShadowFrame Padding="10" Margin="10">
<Grid VerticalOptions="Start" HeightRequest="57" Margin="0,0,0,10" BackgroundColor="White">
<Grid ColumnSpacing="0" VerticalOptions="FillAndExpand">
<StackLayout BackgroundColor="{DynamicResource PMedium}"
x:Name="ListingLayoutB" VerticalOptions="FillAndExpand">
<Label Text="LISTING" HorizontalOptions="CenterAndExpand" TextColor="White" x:Name="ListingTxt"
FontSize="15" FontFamily="{StaticResource SBold}" VerticalOptions="CenterAndExpand"/>
</StackLayout>
<StackLayout Grid.Column="1" BackgroundColor="White" x:Name="DealsLayoutB" VerticalOptions="FillAndExpand">
<Label Text="DEALS" HorizontalOptions="CenterAndExpand" TextColor="{DynamicResource PMedium}" x:Name="DealsTxt"
FontSize="15" VerticalOptions="CenterAndExpand" FontFamily="{StaticResource SBold}"/>
</StackLayout>
</Grid>
</Grid>
</controls:ShadowFrame>
</ContentPage.Content>
And here is the result of above code. Screen shot taken from Android Emulator API level 23.
After setting BorderColor="White" the shadow is showing as expected.
Source: https://forums.xamarin.com/discussion/comment/416769#Comment_416769
Related
In my xamarin.forms app, I have a listview with checkboxes for the selection of the individual cell. What I am trying to do is multi select the checkboxes inside the listview by providing a "select all" checkbox outside the listview.The Multiselection works fine. For the "select all" checkbox click and individual checkbox click, I am performing some actions like an API Call. The Problem I am facing is Whenever I Click on the "select all" checkbox, the checkbox changed event of individual checkbox gets triggered.I know its natural But is there any way to prevent it like subscribe or unsubscribe the changed event of individual checkbox or something?
Xaml
<Grid >
<Grid.RowDefinitions>
<RowDefinitions Height="Auto"/>
<RowDefinitions Height="Auto"/>
</Grid.RowDefinitions>
<StackLayout Grid.Row="0" Orientation="Horizontal">
<Label Text="Select All" FontSize="Micro" TextColor="LawnGreen" HorizontalOptions="Start" VerticalOptions="Center" >
</Label>
<CheckBox x:Name="MultiselectCheckbox" ScaleX="0.8" ScaleY="0.8" CheckedChanged="MultiSelectCheckBox_CheckedChanged" IsChecked="False" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Color="LawnGreen"></CheckBox>
</StackLayout>
<ListView
x:Name="Listview"
HorizontalOptions="FillAndExpand"
ItemTapped="DistrictList_ItemTapped"
VerticalOptions="FillAndExpand" >
<ListView.ItemTemplate >
<DataTemplate >
<ViewCell >
<ViewCell.View>
<Frame HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="{Binding name}" FontSize="Micro" HorizontalOptions="StartAndExpand" VerticalOptions="Center" TextColor="Snow" Margin="5,0,0,0">
</Label>
<CheckBox CheckedChanged="Single_CheckedChanged" IsChecked="{Binding Selected}" Color="LightBlue" HorizontalOptions="End" >
</CheckBox>
</StackLayout>
</Frame>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Multiselect Checkbox checked event
private void MultiSelectCheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
if (!e.Value)
{
foreach (MyData TS in MyObject)
{
TS.Selected = false;
}
}
else{
foreach (MyData TS in MyObject)
{
TS.Selected = true;
}
PerformSomeAction();
}
}
Single selection Checkbox changed event
private void Single_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
if (!e.Value)
{
}
else{
PerformSomeAction();
}
}
Data Model
public class MyData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string name { get; set; }
private bool selected;
public bool Selected
{
get
{
return selected;
}
set
{
if (value != null)
{
selected = value;
NotifyPropertyChanged("Selected");
}
}
}
}
Agree with # Nikhileshwar , you could define some properties to get the different condition .And since you had used MVVM, you would better put all logic handling in your viewmodel .
in xaml
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackLayout Grid.Row="0" Orientation="Horizontal" HeightRequest="40" BackgroundColor="LightPink">
<Label Text="Select All" FontSize="Micro" TextColor="Red" HorizontalOptions="Start" VerticalOptions="Center" >
</Label>
<CheckBox x:Name="MultiselectCheckbox" ScaleX="0.8" ScaleY="0.8" IsChecked="{Binding MultiselectCheck}" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Color="Red"></CheckBox>
</StackLayout>
<ListView Grid.Row="1"
x:Name="Listview"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding MyItems}"
VerticalOptions="FillAndExpand" >
<ListView.ItemTemplate >
<DataTemplate >
<ViewCell >
<Frame Padding="0" HeightRequest="40" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="{Binding name}" FontSize="Micro" HorizontalOptions="StartAndExpand" VerticalOptions="Center" TextColor="Red" Margin="5,0,0,0">
</Label>
<CheckBox IsChecked="{Binding Selected}" Color="Red" HorizontalOptions="End" >
</CheckBox>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
in ViewModel
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
bool isMultiselect;
bool isSingleSelect;
public ObservableCollection<MyData> MyItems { get; set; }
private bool multiselectCheck;
public bool MultiselectCheck
{
get
{
return multiselectCheck;
}
set
{
if (multiselectCheck != value)
{
multiselectCheck = value;
if(!isSingleSelect)
{
isMultiselect = true;
foreach (MyData data in MyItems)
{
data.Selected = value;
}
isMultiselect = false;
}
NotifyPropertyChanged("MultiselectCheck");
}
}
}
public MyViewModel()
{
MyItems = new ObservableCollection<MyData>() {
new MyData(){name="Selection1" },
new MyData(){name="Selection2" },
new MyData(){name="Selection3" },
};
foreach(MyData data in MyItems)
{
data.PropertyChanged += Data_PropertyChanged;
}
}
I have a group of check boxes and i need them to change value.. if one is checked the other should be unchecked. The reason why dont want to implement MVVM is that this is just new frontend for already existing application, i just need these to work before we merge projects. This what i have and it doesnt work,i get null reference / System.NullReferenceException: 'Object reference not set to an instance of an object.'
I have this in my code
private void AdvancedCheckBox_IsCheckedChanged(object sender, IsCheckedChangedEventArgs e)
{
if (AdvancedCheckBox.IsChecked == true) //System.NullReferenceException: 'Object reference not set to an instance of an object.'
{
AllCheckBox.IsChecked = false;
UpperIntermediateCheckBox.IsChecked = false;
AdvancedCheckBox.IsChecked = false;
}
else
AdvancedCheckBox.IsChecked = true;
}
private void UpperIntermediateCheckBox_IsCheckedChanged(object sender, IsCheckedChangedEventArgs e)
{
if (UpperIntermediateCheckBox.IsChecked == true)
{
AllCheckBox.IsChecked = false;
UpperIntermediateCheckBox.IsChecked = false;
AdvancedCheckBox.IsChecked = false;
}
else
UpperIntermediateCheckBox.IsChecked = true;
}
private void IntermediateCheckBox_IsCheckedChanged(object sender, IsCheckedChangedEventArgs e)
{
CheckBox IntermediateCheckBox = sender as CheckBox;
if (IntermediateCheckBox.IsChecked == false)
{
AllCheckBox.IsChecked = false;
UpperIntermediateCheckBox.IsChecked = false;
AdvancedCheckBox.IsChecked = false ;
}
else
IntermediateCheckBox.IsChecked = true ;
}
private void AllCheckBox_IsCheckedChanged(object sender, IsCheckedChangedEventArgs e)
{
CheckBox AllCheckBox = sender as CheckBox;
if (AllCheckBox.IsChecked == true) System.NullReferenceException: 'Object reference not set to an instance of an object.'
{
UpperIntermediateCheckBox.IsChecked = false;
AdvancedCheckBox.IsChecked = false;
IntermediateCheckBox.IsChecked = false;
}
else
AllCheckBox.IsChecked = true;
}
and my xaml
<StackLayout Grid.Row="0"
Orientation="Horizontal"
Spacing="10"
HorizontalOptions="Start">
<grial:Checkbox x:Name="AllCheckBox"
IsCheckedChanged ="AllCheckBox_IsCheckedChanged"
HorizontalOptions="Start">
<Label
Margin="10,0"
FontSize="19"
Text="{ grial:Translate A_LabelAllArticles}"
VerticalOptions="Center"
TextColor="{ DynamicResource AccentColor }" />
</grial:Checkbox>
</StackLayout>
<StackLayout Grid.Row="1"
Orientation="Horizontal"
Spacing="10"
HorizontalOptions="Start">
<grial:Checkbox x:Name="BeginnerCheckBox"
IsChecked="false"
HorizontalOptions="Start">
<Label
FontSize="19"
Margin="10,0"
Text="{ grial:Translate A_LabelBeginnerArticles}"
VerticalOptions="Center"
TextColor="{ DynamicResource AccentColor }" />
</grial:Checkbox>
</StackLayout>
<StackLayout Grid.Row="2"
Orientation="Horizontal"
Spacing="10"
HorizontalOptions="Start">
<grial:Checkbox x:Name="IntermediateCheckBox"
IsCheckedChanged="IntermediateCheckBox_IsCheckedChanged"
HorizontalOptions="Start">
<Label
FontSize="19"
Margin="10,0"
Text="{ grial:Translate A_LabelIntermediateArticles}"
VerticalOptions="Center"
TextColor="{ DynamicResource AccentColor }" />
</grial:Checkbox>
</StackLayout>
<StackLayout Grid.Row="3"
Orientation="Horizontal"
Spacing="10"
HorizontalOptions="Start">
<grial:Checkbox x:Name="UpperIntermediateCheckBox"
IsCheckedChanged="UpperIntermediateCheckBox_IsCheckedChanged"
HorizontalOptions="Start">
<Label
FontSize="19"
Margin="10,0"
Text="{ grial:Translate A_LabelUpperIntermediateArticles}"
VerticalOptions="Center"
TextColor="{ DynamicResource AccentColor }" />
</grial:Checkbox>
</StackLayout>
<StackLayout Grid.Row="4"
Orientation="Horizontal"
Spacing="10"
HorizontalOptions="Start">
<grial:Checkbox x:Name="AdvancedCheckBox"
IsCheckedChanged="AdvancedCheckBox_IsCheckedChanged"
HorizontalOptions="Start">
<Label
FontSize="19"
Margin="10,0"
Text="{ grial:Translate A_LabelAdvancedArticles}"
VerticalOptions="Center"
TextColor="{ DynamicResource AccentColor }" />
</grial:Checkbox>
You are using a Checkbox from the Grial UIKit, but are defining the sender as the Native Xamarin.Forms CheckBox, use the Grial CheckBox as the Casting Class of the sender
//This will make every checkbox to checked or unchecked depending of the state of the clicked one
private void AllCheckBox_IsCheckedChanged(object sender, IsCheckedChangedEventArgs e)
{
CheckBox ClickedCheckbox = sender as UXDivers.Grial.Checkbox;
ChangeAllCheckboxesTo(ClickedCheckbox.IsChecked != null ? ClickedCheckbox.IsChecked : false);
}
//This will make every checkbox to false if you select one
private void AnyCheckBox_IsCheckedChanged(object sender, IsCheckedChangedEventArgs e)
{
CheckBox ClickedCheckbox = sender as UXDivers.Grial.Checkbox;
bool WasSelected = ClickedCheckbox.IsChecked != null ? ClickedCheckbox.IsChecked != null : false;
ChangeAllCheckboxesTo(false);
if(!WasSelected)
ClickedCheckbox.IsChecked = true;
}
public void ChangeAllCheckboxesTo(bool value)
{
try
{
UpperIntermediateCheckBox.IsChecked = value;
AdvancedCheckBox.IsChecked = value;
IntermediateCheckBox.IsChecked = value;
}
catch(Exception ex)
{
}
}
I have an Xamarin application. One of the pages I want to display an Image in a circle, rather than square. To do this I have created a custom rendered for each of the platforms following some online guidance. The classes are below; first in the (portable) project I have
public class CircleImage : Image
{
public static readonly BindableProperty BorderThicknessProperty =
BindableProperty.Create(propertyName: nameof(BorderThickness),
returnType: typeof(float),
declaringType: typeof(CircleImage),
defaultValue: 0F);
public float BorderThickness
{
get { return (float)GetValue(BorderThicknessProperty); }
set { SetValue(BorderThicknessProperty, value); }
}
public static readonly BindableProperty BorderColorProperty =
BindableProperty.Create(propertyName: nameof(BorderColor),
returnType: typeof(Color),
declaringType: typeof(CircleImage),
defaultValue: Color.White);
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, value); }
}
public static readonly BindableProperty FillColorProperty =
BindableProperty.Create(propertyName: nameof(FillColor),
returnType: typeof(Color),
declaringType: typeof(CircleImage),
defaultValue: Color.Transparent);
public Color FillColor
{
get { return (Color)GetValue(FillColorProperty); }
set { SetValue(FillColorProperty, value); }
}
}
Then for Android, I have the renderer
[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
namespace GL.Droid.Renderer
{
[Preserve(AllMembers = true)]
public class CircleImageRenderer : ImageRenderer
{
#pragma warning disable CS0618 // Type or member is obsolete.
public CircleImageRenderer() : base()
#pragma warning restore CS0618 // Type or member is obsolete.
{
}
public CircleImageRenderer(Context context) : base(context) { }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously.
public async static void Init()
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
var temp = DateTime.Now;
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
// Only enable hardware accelleration on lollipop.
if ((int)Build.VERSION.SdkInt < 21)
{
SetLayerType(LayerType.Software, null);
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == CircleImage.BorderColorProperty.PropertyName ||
e.PropertyName == CircleImage.BorderThicknessProperty.PropertyName ||
e.PropertyName == CircleImage.FillColorProperty.PropertyName)
{
Invalidate();
}
}
protected override bool DrawChild(Canvas canvas, Android.Views.View child, long drawingTime)
{
try
{
var radius = (float)Math.Min(Width, Height) / 2f;
var borderThickness = ((CircleImage)Element).BorderThickness;
var strokeWidth = 0f;
if (borderThickness > 0)
{
var logicalDensity = Android.App.Application.Context.Resources.DisplayMetrics.Density;
strokeWidth = (float)Math.Ceiling(borderThickness * logicalDensity + .5f);
}
radius -= strokeWidth / 2f;
var path = new Path();
path.AddCircle(Width / 2.0f, Height / 2.0f, radius, Path.Direction.Ccw);
canvas.Save();
canvas.ClipPath(path);
var paint = new Paint
{
AntiAlias = true
};
paint.SetStyle(Paint.Style.Fill);
paint.Color = ((CircleImage)Element).FillColor.ToAndroid();
canvas.DrawPath(path, paint);
paint.Dispose();
var result = base.DrawChild(canvas, child, drawingTime);
path.Dispose();
canvas.Restore();
path = new Path();
path.AddCircle(Width / 2f, Height / 2f, radius, Path.Direction.Ccw);
if (strokeWidth > 0.0f)
{
paint = new Paint
{
AntiAlias = true,
StrokeWidth = strokeWidth
};
paint.SetStyle(Paint.Style.Stroke);
paint.Color = ((CircleImage)Element).BorderColor.ToAndroid();
canvas.DrawPath(path, paint);
paint.Dispose();
}
path.Dispose();
return result;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Unable to create circle image: " + ex);
}
return base.DrawChild(canvas, child, drawingTime);
}
}
}
This works great and gives me the following looking layout
Now for the iOS and where the problem lies, we have the following, this (as far as I can tell), matches the Android implementation which is below
[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
namespace GL.iOS.Renderer
{
[Preserve(AllMembers = true)]
public class CircleImageRenderer : ImageRenderer
{
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async static void Init()
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
{
var temp = DateTime.Now;
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
CreateCircle();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == VisualElement.HeightProperty.PropertyName ||
e.PropertyName == VisualElement.WidthProperty.PropertyName ||
e.PropertyName == CircleImage.BorderColorProperty.PropertyName ||
e.PropertyName == CircleImage.BorderThicknessProperty.PropertyName ||
e.PropertyName == CircleImage.FillColorProperty.PropertyName)
{
CreateCircle();
}
}
private void CreateCircle()
{
try
{
var min = Math.Min(Element.Width, Element.Height);
Control.Layer.CornerRadius = (nfloat)(min / 2.0);
Control.Layer.MasksToBounds = false;
Control.BackgroundColor = ((CircleImage)Element).FillColor.ToUIColor();
Control.ClipsToBounds = true;
var borderThickness = ((CircleImage)Element).BorderThickness;
// Remove previously added layers.
var tempLayer = Control.Layer.Sublayers?
.Where(p => p.Name == borderName)
.FirstOrDefault();
tempLayer?.RemoveFromSuperLayer();
var externalBorder = new CALayer();
externalBorder.Name = borderName;
externalBorder.CornerRadius = Control.Layer.CornerRadius;
externalBorder.Frame = new CGRect(-.5, -.5, min + 1, min + 1);
externalBorder.BorderColor = ((CircleImage)Element).BorderColor.ToCGColor();
externalBorder.BorderWidth = ((CircleImage)Element).BorderThickness;
Control.Layer.AddSublayer(externalBorder);
}
catch (Exception ex)
{
Debug.WriteLine("Unable to create circle image: " + ex);
}
}
const string borderName = "borderLayerName";
}
}
But this gives me the rendered output of
My XAML is
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage x:Class="GL.ProfilePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="Generation London"
xmlns:local="clr-namespace:GL;assembly=GL"
xmlns:Controls="clr-namespace:GL.Controls"
xmlns:Converters="clr-namespace:GL.Converters"
BackgroundColor="White">
<ContentPage.Resources>
<ResourceDictionary>
<Converters:ResizingImageConverter x:Key="ResizingImageConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<Grid ColumnSpacing="0" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Aspect="AspectFill"
Source="login_background.jpg" />
<Image Aspect="Fill"
Margin="0,-1,0,-1"
Source="curved_mask.png"
VerticalOptions="End" />
<Controls:CircleImage BorderThickness="2"
BorderColor="{x:Static local:Settings.LightPurple}"
WidthRequest="100"
HeightRequest="100"
TranslationY="50"
HorizontalOptions="FillAndExpand"
VerticalOptions="End"
Source="{Binding ProfilePicture, Converter={StaticResource ResizingImageConverter}}">
<!--<Image.Source>
<UriImageSource Uri="{Binding ProfilePicture}" CacheValidity="90"/>
</Image.Source>-->
</Controls:CircleImage>
<StackLayout Grid.Row="1" Padding="0,50,0,00" HorizontalOptions="Center">
<Label x:Name="fullName" Style="{StaticResource MainLabel}"/>
<Label Margin="0,-5" Style="{StaticResource SubLabel}" Text="{Binding Occupation}" />
</StackLayout>
<Grid Grid.Row="2" Margin="0,30" ColumnSpacing="0" RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout>
<Label Style="{StaticResource ValueLabel}" Text="{Binding DateOfBirth, StringFormat='{0:dd/MM/yyyy}'}"/>
<Label Style="{StaticResource CaptionLabel}" Text="DOB"/>
</StackLayout>
<StackLayout Grid.Column="1">
<Label x:Name="workTubeStation" Style="{StaticResource ValueLabel}"/>
<Label Style="{StaticResource CaptionLabel}" Text="Nearest Tube"/>
</StackLayout>
<StackLayout Grid.Column="2">
<Label x:Name="gender" Style="{StaticResource ValueLabel}"/>
<Label Style="{StaticResource CaptionLabel}" Text="Gender"/>
</StackLayout>
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="1"
Margin="0,-5"
Text="Interests"
Style="{StaticResource MainLabel}"/>
</Grid>
<ContentView Grid.Row="4" Padding="5">
<ListView x:Name="userInterests"
RowHeight="35"
ItemsSource="{Binding Interests}"
ItemTapped="NoOpInterestSelected"
HorizontalOptions="Center"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label Text="{Binding .}"
Style="{StaticResource ValueLabel}"
HorizontalTextAlignment="Center"
YAlign="Center" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
<Button Grid.Row="5"
Margin="20"
Style="{StaticResource EditButton}"
Clicked="OnEditProfile"
Text="Edit"/>
</Grid>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Q. Why is the circle container not being rendered correctly?
Thanks for your time.
You haven't shown your XAML, but based on your renderer and your output it seems that the Image is covering not just the photo part, but rather the whole screen width, which makes your code work (corner radius and drawing the ellipse) to appear on the unexpected parts and eventually results in what you have shown. The renderer code expects that the Image control has no transparent parts (e.g. that it uses AspectFill)
If you want to set the Rounded Corner for the control ,Refer the following code
...
Control.Layer.MasksToBounds = true;
Control.Layer.CornerRadius = (nfloat)(min / 2.0);
Control.Layer.BorderColor = ((CircleImage)Element).BorderColor.ToCGColor();
Control.Layer.BorderWidth = ((CircleImage)Element).BorderThickness;;
...
You don't need to add a new sublayer on layer.If you do want to do it.Refer to this similar issue.
I have chat page. Page uses StackLayout as wrapper of ListView, Entry and Button.
ChatPage.xaml
<ContentPage.Resources>
<ResourceDictionary>
<local:MessageTemplateSelector x:Key="MessageTemplateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView x:Name="MessagesListView"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages}"
HasUnevenRows="True"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsRefreshing}"
RefreshCommand="{Binding RefreshCommand}"
SeparatorVisibility="None"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
/>
<Grid x:Name="MessageControls" RowSpacing="1" ColumnSpacing="2" Padding="5"
BackgroundColor="#EFEFF4"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Entry x:Name="txtMessage" Grid.Column="0" HeightRequest="40" Placeholder="Message" Text="{Binding OutGoingText}" TextChanged="EnableSend"/>
<Button x:Name="sendButton" Grid.Column="1" Text="Send" Command="{Binding SendCommand}"/>
</Grid>
</StackLayout>
</ContentPage.Content>
Issue: When I click on Entry, keyboard covers Entry. So, I have handled keyboard appear/disappear event to manage entry visibility as following code. It is working fine except this case. Whenever user long press on entry(most probable to cop/paste), entry goes down/behind the keyboard.
Can anybody please suggest me?
[assembly: ExportRenderer (typeof (ChatPage), typeof (KeyboardAdaptedPageRenderer))]
namespace Project.iOS
{
public class KeyboardAdaptedPageRenderer : ContentPageWithCustomBackButtonRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
Keyboard.WillShow += OnKeyboardWillShow;
Keyboard.WillHide += OnKeyboardWillHide;
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
Keyboard.WillShow -= OnKeyboardWillShow;
Keyboard.WillHide -= OnKeyboardWillHide;
OnKeyboardWillHide(new KeyboardInfo()
{
AnimationDuration = 0,
AnimatonOptions = UIViewAnimationOptions.TransitionNone
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Keyboard.WillShow -= OnKeyboardWillShow;
Keyboard.WillHide -= OnKeyboardWillHide;
}
private void OnKeyboardWillShow(KeyboardInfo info)
{
if (info.SoftwareKeyboardIsVisible)
{
UIView.Animate(info.AnimationDuration, 0, info.AnimatonOptions, () =>
{
var bounds = View.Bounds;
bounds.Y = info.BeginRect.Top - info.EndRect.Top; // iphone 4 and others
View.Bounds = bounds;
}, null);
}
}
private void OnKeyboardWillHide(KeyboardInfo info)
{
UIView.Animate(info.AnimationDuration, 0, info.AnimatonOptions, () =>
{
var bounds = View.Bounds;
bounds.Y = 0;
View.Bounds = bounds;
}, null);
}
}
}
I have this XAML code:
<TableView x:Name="tableView" Intent="Settings" HasUnevenRows="True">
<TableSection>
<TableSection.Title>
Card Selection
</TableSection.Title>
<ViewCell Height="50">
<Grid>
<Grid x:Name="deselectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0">
<Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
</Grid>
<Grid x:Name="deselectGridLabel" VerticalOptions="CenterAndExpand" Padding="20, 0">
<Label TextColor="Silver" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLabel" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
</Grid>
</Grid>
</ViewCell>
<ViewCell Height="50">
<Grid x:Name="selectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0">
<Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="selectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Select All" />
</Grid>
</ViewCell>
</TableSection>
</TableView>
When other parts of my code call: SetPageDetails() then the label in the grid is changed to a link or the link is changed to a label. So for this when it is a label I would like to have no background flash event and no action called.
I attach a tap gesture recognizer like this. Note it's all on one line but covers two lines so it's more visible here in the SO question:
deselectGridLink.GestureRecognizers
.Add(NewTapGestureForUpdateCategories(false));
private TapGestureRecognizer NewTapGestureForUpdateCategories(bool val)
{
return new TapGestureRecognizer()
{
Command = new Command(() =>
{
App.DB.UpdateAllCategoryGroups(val);
App.DB.UpdateAllCategories(val);
GetPageData();
RemoveTableViewClickSection();
tableView.Root.Add(CreateTableSection());
})
};
}
When the user clicks the row when deselectGridLink grid is visible then:
The deselectGridLink visibility is set to false
The deselectGridLabel visibility is set to true
private void SetPageDetails()
{
Title = App.cardCountForSelectedCategories + (App.cardCountForSelectedCategories == 1 ? " Card Selected" : " Cards Selected");
if (App.cardCountForSelectedCategories == 0)
{
deselectGridLink.IsVisible = false;
deselectGridLabel.IsVisible = true;
}
else
{
deselectGridLink.IsVisible = true;
deselectGridLabel.IsVisible = false;
}
}
The effect of this is that the grid link text will change to silver and the link becomes a label.
However even though it's a gray color label when the label is clicked there is still a brief background row color change from white to a dark color when the label is clicked. I assume it's just the way a view cell works.
Is there a way to suppress this from happening?
EDIT 1 - Updated answer as per updates to question. i.e. add support for switching between highlight enabled/disabled mode.
EDIT 2 - Restructure answer and add more details.
Option-1: Enable/disable view-cell through IsEnabled
The simplest option would be to use the IsEnabled property, which in turn enables/disables the background flash behavior. The only downside to this approach is that it will also disable the taps on child controls, i.e. tap events/gesture recognizer(s) will not be triggered if parent view-cell's IsEnabled is false.
For example:
XAML
<!-- Add name attribute to view-cell -->
<ViewCell x:Name="deselectCell" ..>
<Grid>
<Grid x:Name="deselectGridLink" ..
....
</ViewCell>
Code-behind
private void SetPageDetails()
{
if (App.cardCountForSelectedCategories == 0)
{
deselectCell.IsEnabled = false; //disable background flash
...
}
else
{
deselectCell.IsEnabled = true;
...
}
}
Recommendation 1 - Use data-binding and triggers
Instead of controlling visibility for each label in code-behind, you can use triggers and data-binding as follows (view-model will have a IsDeselectEnabled property):
<ViewCell IsEnabled="{Binding IsDeselectEnabled}" Height="50">
<Label Margin="20,0,20,0" Style="{DynamicResource ListItemTextStyle}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All">
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding IsDeselectEnabled}" Value="true">
<Setter Property="TextColor" Value="Blue" />
</DataTrigger>
<DataTrigger TargetType="Label" Binding="{Binding IsDeselectEnabled}" Value="false">
<Setter Property="TextColor" Value="Silver" />
</DataTrigger>
</Label.Triggers>
</Label>
</ViewCell>
Recommendation 2 - Use triggers with view as source
<ViewCell x:Name="deselectCell" Height="50">
<Label Margin="20,0,20,0" Style="{DynamicResource ListItemTextStyle}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All">
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding IsEnabled, Source={x:Reference deselectCell}}" Value="true">
<Setter Property="TextColor" Value="Blue" />
</DataTrigger>
<DataTrigger TargetType="Label" Binding="{Binding IsEnabled, Source={x:Reference deselectCell}}" Value="false">
<Setter Property="TextColor" Value="Silver" />
</DataTrigger>
</Label.Triggers>
</Label>
</ViewCell>
Option-2: Enable/disable highlight, but allow taps
To allow taps while toggling ViewCell's background-highlight behavior, we will need to implement platform-renderer(s).
In case of iOS, we can use SelectionStyle to toggle this behavior, while in case of android, we can use Clickable property.
Shared control:
public class CustomViewCell : ViewCell
{
public static readonly BindableProperty AllowHighlightProperty =
BindableProperty.Create(
"AllowHighlight", typeof(bool), typeof(CustomViewCell),
defaultValue: true);
public bool AllowHighlight
{
get { return (bool)GetValue(AllowHighlightProperty); }
set { SetValue(AllowHighlightProperty, value); }
}
}
iOS renderer:
[assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
namespace SampleApp.iOS
{
public class CustomViewCellRenderer : ViewCellRenderer
{
UITableViewCell _nativeCell;
//get access to the associated forms-element and subscribe to property-changed
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
_nativeCell = base.GetCell(item, reusableCell, tv);
var formsCell = item as CustomViewCell;
if (formsCell != null)
{
formsCell.PropertyChanged -= OnPropertyChanged;
formsCell.PropertyChanged += OnPropertyChanged;
}
//and, update the style
SetStyle(formsCell);
return _nativeCell;
}
void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var formsCell = sender as CustomViewCell;
if (formsCell == null)
return;
//TODO: Trying to find a nicer and more robust way to dispose and unsubscribe :(
if (_nativeCell == null)
formsCell.PropertyChanged -= OnPropertyChanged;
if (e.PropertyName == CustomViewCell.AllowHighlightProperty.PropertyName)
{
SetStyle(formsCell);
}
}
private void SetStyle(CustomViewCell formsCell)
{
//added this code as sometimes on tap, the separator disappears, if style is updated before tap animation finishes
//https://stackoverflow.com/questions/25613117/how-do-you-prevent-uitableviewcellselectionstylenone-from-removing-cell-separato
Device.StartTimer(TimeSpan.FromMilliseconds(50), () => {
Device.BeginInvokeOnMainThread(() =>
{
if (formsCell.AllowHighlight)
_nativeCell.SelectionStyle = UITableViewCellSelectionStyle.Default;
else
_nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None;
});
return false;
});
}
}
}
Android renderer:
[assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
namespace SampleApp.Droid
{
public class CustomViewCellRenderer : ViewCellRenderer
{
Android.Views.View _nativeCell;
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context)
{
_nativeCell = base.GetCellCore(item, convertView, parent, context);
SetStyle();
return _nativeCell;
}
// this one is simpler as the base class has a nice override-able method for our purpose - so we don't need to subscribe
protected override void OnCellPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnCellPropertyChanged(sender, e);
if(e.PropertyName == CustomViewCell.AllowHighlightProperty.PropertyName)
{
SetStyle();
}
}
private void SetStyle()
{
var formsCell = Cell as CustomViewCell;
if (formsCell == null)
return;
_nativeCell.Clickable = !formsCell.AllowHighlight;
}
}
}
Sample usage 1 - Through data-binding
<local:CustomViewCell AllowHighlight="{Binding IsHighlightEnabled}" ..>
<Grid>
<Grid x:Name="deselectGridLink" ..
...
</local:CustomViewCell>
Sample usage 2 - Through code-behind
XAML
<!-- Add name attribute to view-cell -->
<local:CustomViewCell x:Name="deselectCell" ..>
<Grid>
<Grid x:Name="deselectGridLink" ..
...
</local:CustomViewCell>
Code-behind
private void SetPageDetails()
{
if (App.cardCountForSelectedCategories == 0)
{
deselectCell.AllowHighlight= false; //disable background flash
...
}
else
{
deselectCell.AllowHighlight= true;
...
}
}
Option-3: Disable highlight, selection for all items
This particularly applies to ListView. The updated question now specifies that the cells are part of TableView, so this option is no longer valid in current question context.
You will need to implement platform renderers to disable highlight colors, and add ItemTapped handler to ListView to disable selection by setting SelectedItem as null always. References used:
Disable highlight item
Disable selection
Code
To get started, create a custom view-cell:
public class NoSelectViewCell : ViewCell { }
Implement iOS renderer as:
[assembly: ExportRenderer(typeof(NoSelectViewCell), typeof(NoSelectViewCellRenderer))]
namespace SampleApp.iOS
{
public class NoSelectViewCellRenderer : ViewCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var nativeCell = base.GetCell(item, reusableCell, tv);
nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None;
return nativeCell;
}
}
}
Implement android renderer as:
[assembly: ExportRenderer(typeof(NoSelectViewCell), typeof(NoSelectViewCellRenderer))]
namespace SampleApp.Droid
{
public class NoSelectViewCellRenderer : ViewCellRenderer
{
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context)
{
var cell = base.GetCellCore(item, convertView, parent, context);
cell.Focusable = false;
cell.FocusableInTouchMode = false;
var listView = parent as Android.Widget.ListView;
if (listView != null)
{
listView.SetSelector(Android.Resource.Color.Transparent);
listView.CacheColorHint = Xamarin.Forms.Color.Transparent.ToAndroid();
}
return cell;
}
}
}
Sample Usage:
XAML
<ListView ItemTapped="Handle_ItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<local:NoSelectViewCell Height="50">
<Grid>
<Grid x:Name="deselectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0">
<Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
</Grid>
<Grid x:Name="deselectGridLabel" VerticalOptions="CenterAndExpand" Padding="20, 0">
<Label TextColor="Silver" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLabel" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
</Grid>
</Grid>
</local:NoSelectViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code-behind
void Handle_ItemTapped(object sender, Xamarin.Forms.ItemTappedEventArgs e)
{
// don't do anything if we just de-selected the row
if (e.Item == null) return;
// do something with e.SelectedItem
((ListView)sender).SelectedItem = null; // de-select the row
}
What G.Sharada proposes is very nicely working for iOS, but on Android I still had blinks on click.
Adding this line to the styles file solved the problem.
<item name="android:colorActivatedHighlight">#android:color/transparent</item>