Why this line
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Green);
is working in a custom EntryRenderer in Android (xamarin forms) and not in a custom MaterialEntryRenderer?
I want to change only the underline color of the entry in material and not the underline color and the placeholder text color too.
xaml
<?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="Genesys.App.Views.ConfigurationView"
xmlns:local1="clr-namespace:Genesys.App.CustomControls"
Title="Ingrese Configuración"
Visual="Material">
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Padding="15"
Background="white">
<local1:MyEntry x:Name="txtServicio"
Placeholder="SERVICIO"
BackgroundColor="White">
</local1:MyEntry>
<Button Padding="5"
Margin="5"
Text="guardar"
TextTransform="Uppercase"
TextColor="white"
BackgroundColor="{StaticResource colorPrimary}"
HorizontalOptions="End"
Command="{Binding ButtonCommand}"></Button>
</StackLayout>
</ContentPage.Content>
</ContentPage>
//class MyEntry
public class MyEntry : Entry
{
}
CustomEntryRenderer in android
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyRenderer))]
namespace Genesys.App.Droid
{
public class MyRenderer : EntryRenderer//this works
{
public MyRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
this.EditText.FocusChange += (sender, ee) => {
bool hasFocus = ee.HasFocus;
if (hasFocus)
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Green); //changes color
}
else
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Red); //changes color
}
};
}
}
}
}
//don't work
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyRenderer))]
namespace Genesys.App.Droid
{
public class MyRenderer : MaterialEntryRenderer//don't work
{
public MyRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
this.EditText.FocusChange += (sender, ee) => {
bool hasFocus = ee.HasFocus;
if (hasFocus)
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Green);//doesn't change color
}
else
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Red); //doesn't change color
}
};
}
}
}
}
Related
I have a xamarin forms application and I have inserted a webview inside the layout stack, the problem is that I have multiple pages and in each page the length of the content is different from the others.
I wanted to ask if there was a way to have the webview automatically adjust the height.
I have read other similar questions on the site, but I admit they seemed confusing to me.
This is my code xaml
<ContentPage.Content>
<StackLayout Padding="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Spacing="0" BackgroundColor="White">
<WebView
x:Name="wvSite"
VerticalOptions="FillAndExpand"
HeightRequest="2300" />
</StackLayout>
</ContentPage.Content>
You could reset the webview height according to the content height when loading page finished via custom renderer.
Android:
[assembly: ExportRenderer(typeof(WebView), typeof(CustomWebViewRenderer))]
namespace App1.Droid
{
public class CustomWebViewRenderer : WebViewRenderer
{
public CustomWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
Control.SetWebViewClient(new CustomWebViewClient(e.NewElement));
}
}
internal class CustomWebViewClient : Android.Webkit.WebViewClient
{
private WebView webView;
public CustomWebViewClient(WebView webView)
{
this.webView = webView;
}
public async override void OnPageFinished(Android.Webkit.WebView view, string url)
{
if (webView != null)
{
int i = 10;
while (view.ContentHeight == 0 && i-- > 0)
await System.Threading.Tasks.Task.Delay(100);
webView.HeightRequest = view.ContentHeight;
}
base.OnPageFinished(view, url);
}
}
}
iOS:
[assembly: ExportRenderer(typeof(WebView),
typeof(CustomWebviewRenderer))]
namespace App1.iOS
{
public class CustomWebviewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
NavigationDelegate = new CustomWebviewNavigationDelegate();
}
}
internal class CustomWebviewNavigationDelegate : WKNavigationDelegate
{
[Export("webview:didFinishNavigation:")]
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
//base.DidFinishNavigation(webView, navigation);
webView.Frame = new CoreGraphics.CGRect(0, 0, webView.ScrollView.ContentSize.Width, webView.ScrollView.ContentSize.Height);
}
}
}
I'm using
Xamarin.Froms: 5.0.0.24125
Xcode Version: 13.0 (13A233)
Visual Studio for Mac Community Version 8.10.9 (Build 3)
NuGet Version: 5.9.0.7134
Xamarin.iOS Version: 14.20.0.27 (Visual Studio Community)
MainPage.xaml:
<?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="ListViewTopSpace.MainPage"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
Title="Page title"
ios:Page.UseSafeArea="true">
<ListView ItemsSource="{Binding Users}"
BackgroundColor="Blue"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="0,0,0,0.5"
Padding="10,0"
BackgroundColor="AliceBlue">
<Label FontSize="12">
<Label.FormattedText>
<FormattedString>
<Span Text="User Id: "
FontAttributes="Bold"/>
<Span Text="{Binding Id, Mode=OneWay}"/>
</FormattedString>
</Label.FormattedText>
</Label>
<Label FontSize="12">
<Label.FormattedText>
<FormattedString>
<Span Text="User name: "
FontAttributes="Bold"/>
<Span Text="{Binding Name, Mode=OneWay}"/>
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
MainPageViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace ListViewTopSpace
{
public class MainPageViewModel : ObservableProperty
{
public MainPageViewModel()
{
Users = new ObservableCollection<UserListModel>()
{
new UserListModel { Id = 1, Name = "User 1" },
new UserListModel { Id = 2, Name = "Test user 1" },
new UserListModel { Id = 3, Name = "User 2" },
new UserListModel { Id = 4, Name = "Test user 2" },
};
}
private ObservableCollection<UserListModel> _users;
public ObservableCollection<UserListModel> Users
{
get { return _users; }
set
{
_users = value;
OnPropertyChanged(nameof(Users));
}
}
}
public class UserListModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ObservableProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Result:
[1]: https://i.stack.imgur.com/BKn29.png
This highlighted space shouldn’t be there.
#Colex is right, but you have to replace this string of code:
Control.TableHeaderView = new UIKit.UIView(frame: new CoreGraphics.CGRect(0, 0, 0, float.MinValue));
with this:
Control.SectionHeaderTopPadding = new nfloat(0);
Full code of Renderer:
[assembly: ExportRenderer(typeof(ListView), typeof(LVRenderer))]
namespace YourNameSpace.iOS
{
public class LVRenderer : ListViewRenderer
{
public LVRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SectionHeaderTopPadding = new nfloat(0);
}
}
}
}
The problem is related with apple not xamarin side .
Please see this thread : https://developer.apple.com/forums/thread/683980.
To solve it we can add a custom renderer for ListView
Try the following code
[assembly:ExportRenderer(typeof(ListView),typeof(MyRenderer))]
namespace Formssss.iOS
{
public class MyRenderer : ListViewRenderer
{
public MyRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.TableHeaderView = new UIKit.UIView(frame: new CoreGraphics.CGRect(0, 0, 0, float.MinValue));
}
}
}
}
After updating to xamarin.forms version to latest from NuGet manager will solve this issue.
Updating to version 5.0.0.2244 will definitely fix this issue
and extra top padding from list view will be removed on iOS 15.0 or greater
I am trying to get an image in the shape of rectangle with rounded Cornes. I have tried to use nugget fftransformations and frame. So far I am not getting the result I want. Using the nugget turns the image into square no matter what size I give to it. Using frame for some reason doesn't actually round the corners. enter image description here
<StackLayout VerticalOptions="CenterAndExpand">
<ffimageloading:CachedImage VerticalOptions="CenterAndExpand"
WidthRequest="530" HeightRequest="334"
DownsampleToViewSize="true"
Source = "http://loremflickr.com/530/334/nature?filename=simple.jpg">
<ffimageloading:CachedImage.Transformations>
<fftransformations:RoundedTransformation Radius="10"/>
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
<ffimageloading:CachedImage VerticalOptions="CenterAndExpand"
WidthRequest="530" HeightRequest="334"
DownsampleToViewSize="true"
Source = "http://loremflickr.com/530/334/nature?filename=simple.jpg">
<ffimageloading:CachedImage.Transformations>
<fftransformations:CornersTransformation CornersTransformType="AllRounded"/>
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
<Grid VerticalOptions="CenterAndExpand">
<Frame Padding="0" WidthRequest="530" HeightRequest="334"
HorizontalOptions="Center"
VerticalOptions="Center" CornerRadius="20" >
<Image Source = "http://loremflickr.com/530/330/nature?filename=simple.jpg" Aspect="AspectFill" />
</Frame>
</Grid>
</StackLayout>
Some time ago I needed corner effects on other controls. And it applies to image too.
All you need is to create Effect:
public class RoundCornersEffect : RoutingEffect
{
public RoundCornersEffect() : base($"MySuperApp.{nameof(RoundCornersEffect)}")
{
}
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.CreateAttached(
"CornerRadius",
typeof(int),
typeof(RoundCornersEffect),
0,
propertyChanged: OnCornerRadiusChanged);
public static int GetCornerRadius(BindableObject view) =>
(int)view.GetValue(CornerRadiusProperty);
public static void SetCornerRadius(BindableObject view, int value) =>
view.SetValue(CornerRadiusProperty, value);
private static void OnCornerRadiusChanged(BindableObject bindable, object oldValue, object newValue)
{
if (!(bindable is View view))
return;
var cornerRadius = (int)newValue;
var effect = view.Effects.OfType<RoundCornersEffect>().FirstOrDefault();
if (cornerRadius > 0 && effect == null)
view.Effects.Add(new RoundCornersEffect());
if (cornerRadius == 0 && effect != null)
view.Effects.Remove(effect);
}
}
And implement it on both platforms:
IOS:
public class RoundCornersEffectIOS : PlatformEffect
{
protected override void OnAttached()
{
try
{
PrepareContainer();
SetCornerRadius();
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnDetached()
{
try
{
Container.Layer.CornerRadius = new nfloat(0);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == RoundCornersEffect.CornerRadiusProperty.PropertyName)
SetCornerRadius();
}
private void PrepareContainer()
{
Container.ClipsToBounds = true;
Container.Layer.AllowsEdgeAntialiasing = true;
Container.Layer.EdgeAntialiasingMask = CAEdgeAntialiasingMask.All;
}
private void SetCornerRadius()
{
var cornerRadius = RoundCornersEffect.GetCornerRadius(Element);
Container.Layer.CornerRadius = new nfloat(cornerRadius);
}
}
Droid:
public class RoundCornersEffectDroid : PlatformEffect
{
private Android.Views.View View => Control ?? Container;
protected override void OnAttached()
{
try
{
PrepareContainer();
SetCornerRadius();
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnDetached()
{
try
{
View.OutlineProvider = ViewOutlineProvider.Background;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == RoundCornersEffect.CornerRadiusProperty.PropertyName)
SetCornerRadius();
}
private void PrepareContainer()
{
View.ClipToOutline = true;
}
private void SetCornerRadius()
{
var cornerRadius = RoundCornersEffect.GetCornerRadius(Element) * GetDensity();
View.OutlineProvider = new RoundedOutlineProvider(cornerRadius);
}
private static double GetDensity() =>
DeviceDisplay.MainDisplayInfo.Density;
private class RoundedOutlineProvider : ViewOutlineProvider
{
private readonly double _radius;
public RoundedOutlineProvider(double radius)
{
_radius = radius;
}
public override void GetOutline(Android.Views.View view, Outline outline)
{
outline?.SetRoundRect(0, 0, view.Width, view.Height, (float)_radius);
}
}
}
Then you can use it in control:
<Image Source="mylogo.png" VerticalOptions="Center" Aspect="AspectFit" myeffects:RoundCornersEffect.CornerRadius="5"/>
Hopefully this doesn't get dinged, I've looked at other posts and nothing seems to work.
Code:
<controls:SearchPageSearchBar Grid.Column="1" Scale=".8" Margin="0,0,0,0" x:Name="searchBar"
BackgroundColor="White"
SearchCommandParameter="{Binding Text, Source={x:Reference searchBar}}"
Placeholder="Search" >
</controls:SearchPageSearchBar>
public class SearchPageSearchBarDroid : SearchBarRenderer
{
public SearchPageSearchBarDroid(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> args)
{
base.OnElementChanged(args);
var plateId = Resources.GetIdentifier("android:id/search_plate", null, null);
var plate = Control.FindViewById(plateId);
plate.SetBackgroundColor(Android.Graphics.Color.Transparent);
}
}
Tried all solutions here too
https://forums.xamarin.com/discussion/140247/how-can-i-remove-the-underline-in-searbar-on-android
[assembly: ExportRenderer(typeof(SearchPageSearchBar), typeof(SearchPageSearchBarDroid))]
i test your code and it works,have you missed the above line in your SearchPageSearchBarDroid ?
[assembly: ExportRenderer(typeof(SearchPageSearchBar), typeof(SearchPageSearchBarDroid))]
namespace App18.Droid
{
class SearchPageSearchBarDroid:SearchBarRenderer
{
public SearchPageSearchBarDroid(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
var plateId = Resources.GetIdentifier("android:id/search_plate", null, null);
var plate = Control.FindViewById(plateId);
plate.SetBackgroundColor(Android.Graphics.Color.Transparent);
}
}
}
I'm experiencing a refresh problem with my databound ComboBox.
The selection is not refreshed when I change the SelectedValue from the code-behind.
Here is a sample I made which illustrates the problem. Am I doing something wrong ?
Thanks !
<navigation:Page x:Class="Views.TestCB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="TestCB Page">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding AllItems, Mode=TwoWay}" Grid.Row="0" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" DisplayMemberPath="Key" SelectedValuePath="Value" SelectedValue="{Binding SelectedValue, Mode=TwoWay}"/>
<Button Content="Change" Click="Button_Click" Grid.Row="1" />
<Button Content="Display" Click="Button_Click_1" Grid.Row="2" />
</Grid>
</navigation:Page>
public partial class TestCB : Page, INotifyPropertyChanged
{
public TestCB()
{
InitializeComponent();
}
private Random r = new Random();
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<MyClass> allItemsField = new ObservableCollection<MyClass>();
public ObservableCollection<MyClass> AllItems
{
get
{
return this.allItemsField;
}
set
{
if (this.allItemsField != value)
{
this.allItemsField = value;
this.OnPropertyChanged("AllItems");
}
}
}
private MyClass selectedItemField = null;
public MyClass SelectedItem
{
get
{
return this.selectedItemField;
}
set
{
if (this.selectedItemField != value)
{
this.selectedItemField = value;
this.OnPropertyChanged("SelectedItem");
}
}
}
private int selectedValueField;
public int SelectedValue
{
get
{
return this.selectedValueField;
}
set
{
if (this.selectedValueField != value)
{
this.selectedValueField = value;
this.OnPropertyChanged("SelectedValue");
}
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.SetCombo();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.SetCombo();
this.SelectRandom();
}
private void SetCombo()
{
this.AllItems.Clear();
int generated = 0;
while (generated < 10)
{
int val = r.Next(100);
string key = string.Format("Key #{0}", val);
if (!this.AllItems.Any(e => e.Key == key))
{
this.AllItems.Add(new MyClass { Key = key, Value = val });
generated++;
}
}
}
private void SelectRandom()
{
var tmp = this.AllItems[r.Next(this.AllItems.Count)];
this.SelectedValue = tmp.Value;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.SelectedItem.Key);
}
}
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private int valueField;
public int Value
{
get
{
return this.valueField;
}
set
{
if (this.valueField != value)
{
this.valueField = value;
this.OnPropertyChanged("Value");
}
}
}
private string keyField = string.Empty;
public string Key
{
get
{
return this.keyField;
}
set
{
if (this.keyField != value)
{
this.keyField = value;
this.OnPropertyChanged("Key");
}
}
}
}
EDIT: Please note that in my program, the ComboBox is part of a ListBoxItem (the ListBox being also databound), so I can't access it directly to reset the binding/force a reload.
I finally used the combobox found here : http://forums.lhotka.net/forums/p/9786/45971.aspx
It corrected my SelectedValue binding problem.
Have you tried setting SelectedItem instead? SelectedValue has some know issues.