Xamarin Forms overriding Android switch property using Effects - xamarin.forms

I would like to override the On/Off text of a switch using an Effect rather than a custom renderer.
In Android, I have the following code:
protected override void OnAttached()
{
try
{
if (Control is Android.Widget.Switch control)
{
control.TextOn = "Yes";
control.TextOff = "No";
}
}
OnAttached executes as expected but 'Control' is not an Android.Widget.Switch control but of a related type, e.g. 'android.support.v7.widget.SwitchCompat'. How can I make the code recognize that it should update the text in this case?

Do you want to achieve the result like following effect?
You can achieve it like following code.
using Android.Runtime;
using Android.Support.V7.Widget;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(SwitchDemo.Droid.ClickEffect), nameof(SwitchDemo.Droid.ClickEffect))]
namespace SwitchDemo.Droid
{
public class ClickEffect : PlatformEffect
{
protected override void OnAttached()
{
// throw new NotImplementedException();
if (Control is SwitchCompat control)
{
control.ShowText = true;
control.TextOn = "Yes";
control.TextOff = "No";
}
}
protected override void OnDetached()
{
//throw new NotImplementedException();
}
}
}
Here is PCL code.
ClickEffect.cs
public class ClickEffect: RoutingEffect
{
public ClickEffect() : base($"MyCompany.{nameof(ClickEffect)}")
{
}
}
Use it in xaml.
<Switch>
<Switch.Effects>
<local:ClickEffect/>
</Switch.Effects>
</Switch>

Related

How to change the color of searchbar search icon and cancel button color in xamarin forms

How to change the color of searchbar icon from xaml, I want to change the cancel and search icon color of a search bar in xamarin forms application.How to implement this. Please help on this
As adamm said, you can modify the "cancel button color" via CancelButtonColor.
Similarly, if you want to implement a custom SearchBar in iOS, you can also create a custom renderer for it.
For UISearchBar, you can modify the color of the icon via SearchTextField.LeftView.TintColor.
[assembly: ExportRenderer(typeof(MySearchBar), typeof(MySearchBarRenderer))]
namespace searchbar.iOS
{
public class MySearchBarRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.BackgroundColor = UIColor.Yellow;
UISearchBar searchbar = Control as UISearchBar;
searchbar.SearchTextField.LeftView.TintColor = UIColor.Orange;
// UPDATE
var clearButton = searchbar.SearchTextField.ValueForKey((Foundation.NSString)"clearButton") as UIButton;
//clearButton.SetTitleColor(UIColor.Blue, UIControlState.Normal);
//clearButton.TintColor = UIColor.Orange;
clearButton.SetImage(UIImage.FromBundle("CloseIcon.png"), UIControlState.Normal);
}
}
}
}
Cancel button color can be change by setting CancelButtonColor:
<SearchBar Placeholder="Search items..."
CancelButtonColor="Orange"
PlaceholderColor="Orange"
TextColor="Orange"
TextTransform="Lowercase"
HorizontalTextAlignment="Center"
FontSize="Medium"
FontAttributes="Italic" />
For the icon color you need to use custom renderers.
For example, on Android you can create a new file (something like SearchBar.Droid.cs) and add this in it:
using Android.Content;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Xamarin.Forms.SearchBar), typeof(MyApp.Renderers.SearchBarIconColorCustomRenderer))]
namespace MyApp.Renderers
{
public class SearchBarIconColorCustomRenderer : SearchBarRenderer
{
public SearchBarIconColorCustomRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
var icon = Control?.FindViewById(Context.Resources.GetIdentifier("android:id/search_mag_icon", null, null));
(icon as ImageView)?.SetColorFilter(Color.Orange.ToAndroid());
}
}
}
Edit:
For iOS, try something like this (I didn't test it):
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(Xamarin.Forms.SearchBar), typeof(MyApp.iOS.Renderers.iOSSearchBarIconColorCustomRenderer))]
namespace MyApp.iOS.Renderers
{
public class iOSSearchBarIconColorCustomRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var searchbar = (UISearchBar)Control;
searchbar.TintColor = UIColor.Orange;
}
}
}
}

How to Check For Dark Mode in Xamarin.Forms

Now that iOS 13 and Android Q allow the user to enable Dark Mode at the operating system level, how can I check for it in Xamarin.Forms?
I've created this in my Xamarin.Forms project, but I'm not sure how to retrieve the values from Xamarin.iOS and Xamarin.Android.
IEnvironment.cs
using System.Threading.Tasks;
namespace MyNamespace
{
public interface IEnvironment
{
Theme GetOperatingSystemTheme();
Task<Theme> GetOperatingSystemThemeAsync();
}
public enum Theme { Light, Dark }
}
App.cs
using Xamarin.Forms;
namespace MyNamespace
{
public App : Application
{
// ...
protected override async void OnStart()
{
base.OnStart();
Theme theme = DependencyService.Get<IEnvironment>().GetOperatingSystemTheme();
SetTheme(theme);
}
protected override async void OnResume()
{
base.OnResume();
Theme theme = DependencyService.Get<IEnvironment>().GetOperatingSystemTheme();
SetTheme(theme);
}
void SetTheme(Theme theme)
{
//Handle Light Theme & Dark Theme
}
}
}
UPDATE as of April 2020:
It is no longer necessary to use platform-specific services to check for light/dark mode in Xamarin.Forms.
We can now get the current theme directly via:
OSAppTheme currentTheme = Application.Current.RequestedTheme;
where the RequestedTheme property returns an OSAppTheme enumeration member: Unspecified, Dark, or Light.
For more info: see documentation and the updated Xamarin.Forms Application.cs code.
We can use the Xamarin.Forms dependency service to access the platform-specific code from iOS and Android.
I've gone into more depth here in this blog post:
https://codetraveler.io/2019/09/10/check-for-dark-mode-in-xamarin-forms/
Xamarin.Forms Code
IEnvironment
using System.Threading.Tasks;
namespace MyNamespace
{
public interface IEnvironment
{
Theme GetOperatingSystemTheme();
Task<Theme> GetOperatingSystemThemeAsync();
}
public enum Theme { Light, Dark }
}
App.cs
using Xamarin.Forms;
namespace MyNamespace
{
public App : Application
{
// ...
protected override async void OnStart()
{
base.OnStart();
Theme theme = DependencyService.Get<IEnvironment>().GetOperatingSystemTheme();
SetTheme(theme);
}
protected override async void OnResume()
{
base.OnResume();
Theme theme = DependencyService.Get<IEnvironment>().GetOperatingSystemTheme();
SetTheme(theme);
}
void SetTheme(Theme theme)
{
//Handle Light Theme & Dark Theme
}
}
}
Xamarin.iOS
using System;
using UIKit;
using Xamarin.Forms;
using MyNamespace;
using MyNamespace.iOS;
[assembly: Dependency(typeof(Environment_iOS))]
namespace MyNamespace.iOS
{
public class Environment_iOS : IEnvironment
{
public Theme GetOperatingSystemTheme()
{
//Ensure the current device is running 12.0 or higher, because `TraitCollection.UserInterfaceStyle` was introduced in iOS 12.0
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
{
var currentUIViewController = GetVisibleViewController();
var userInterfaceStyle = currentUIViewController.TraitCollection.UserInterfaceStyle;
switch (userInterfaceStyle)
{
case UIUserInterfaceStyle.Light:
return Theme.Light;
case UIUserInterfaceStyle.Dark:
return Theme.Dark;
default:
throw new NotSupportedException($"UIUserInterfaceStyle {userInterfaceStyle} not supported");
}
}
else
{
return Theme.Light;
}
}
// UIApplication.SharedApplication can only be referenced by the Main Thread, so we'll use Device.InvokeOnMainThreadAsync which was introduced in Xamarin.Forms v4.2.0
public async Task<Theme> GetOperatingSystemThemeAsync() =>
Device.InvokeOnMainThreadAsync(GetOperatingSystemTheme);
static UIViewController GetVisibleViewController()
{
UIViewController viewController = null;
var window = UIApplication.SharedApplication.KeyWindow;
if (window.WindowLevel == UIWindowLevel.Normal)
viewController = window.RootViewController;
if (viewController is null)
{
window = UIApplication.SharedApplication
.Windows
.OrderByDescending(w => w.WindowLevel)
.FirstOrDefault(w => w.RootViewController != null && w.WindowLevel == UIWindowLevel.Normal);
if (window is null)
throw new InvalidOperationException("Could not find current view controller.");
viewController = window.RootViewController;
}
while (viewController.PresentedViewController != null)
viewController = viewController.PresentedViewController;
return viewController;
}
}
}
Xamarin.Android
using System;
using System.Threading.Tasks;
using Android.Content.Res;
using Plugin.CurrentActivity;
using Xamarin.Forms;
using MyNamespace;
using MyNamespace.Android;
[assembly: Dependency(typeof(Environment_Android))]
namespace MyNamespace.Android
{
public class Environment_Android : IEnvironment
{
public Task<Theme> GetOperatingSystemThemeAsync() =>
Task.FromResult(GetOperatingSystemTheme());
public Theme GetOperatingSystemTheme()
{
//Ensure the device is running Android Froyo or higher because UIMode was added in Android Froyo, API 8.0
if(Build.VERSION.SdkInt >= BuildVersionCodes.Froyo)
{
var uiModeFlags = CrossCurrentActivity.Current.AppContext.Resources.Configuration.UiMode & UiMode.NightMask;
switch(uiModelFlags)
{
case UiMode.NightYes:
return Theme.Dark;
case UiMode.NightNo:
return Theme.Light;
default:
throw new NotSupportedException($"UiMode {uiModelFlags} not supported");
}
}
else
{
return Theme.Light;
}
}
}
}
For iOS:
if (UITraitCollection.CurrentTraitCollection.UserInterfaceStyle == UIUserInterfaceStyle.Dark)
{ ... }

Rounded corner for Entry - Xamarin forms UWP

Is there anything possible to customize the radius of Entry to having a slightly rounded corner?
You can use Custom Renderer in xamarin.forms
in iOS
//...
using App11;
using App11.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyiOSEntry))]
namespace App11.iOS
{
public class MyiOSEntry:EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Layer.MasksToBounds = true;
Control.Layer.CornerRadius = 10; //set the rounded corner
Control.Layer.BorderColor = UIColor.Red.CGColor;
Control.Layer.BorderWidth = 3;
}
}
}
}
in Android
creat a xml file in the folder Resource->drawable edit_text_style.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:shape="rectangle">
<solid
android:color="#ffffff" />
<corners
android:radius="10dp" />
<stroke
android:width="2dp"
android:color="#3bbdfa" />
</shape>
</item>
in Custom Renderer
using Android.Support.V4.Content.Res;
using App11;
using App11.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyAndriodEntry))]
namespace App11.Droid
{
public class MyAndriodEntry:EntryRenderer
{
public MyAndriodEntry(Context context):base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Control.SetBackground(ResourcesCompat.GetDrawable(Resources, Resource.Drawable.edit_text_style, null) );
}
}
}
}
in UWP
create a folder named Styles and add a new item as type Resource Dictionary and name it Dictionary1.xaml
in Dictionary1.xaml put this code for a rounded Textbox .
in Custom Renderer
using App11;
using App11.UWP;
using Windows.UI.Xaml.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyUWPEntry))]
namespace App11.UWP
{
public class MyUWPEntry:EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Control.Style = (Windows.UI.Xaml.Style)App11.UWP.App.Current.Resources["StyleRoundedTextBox"];
}
}
}
}
how do I changed this style and how do I create this code ?
It's simple , in msdn.com search for "objectName" default style in uwp then you will find default style for the object you need . change it in the way you want and add it to application resources directly or link it (like what I did here) then load your style in CustomRenderer
for more detail about UWP yo can refer here
in Forms
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace App11
{
public class MyEntry : Entry
{
public MyEntry()
{
}
}
}
in xxx.cs file
Content = new StackLayout
{
Children = {
new MyEntry {Text = "In Shared Code",}
},
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
For Windows app you can customize an entry using a renderer.
public class CustomEntryRenderer : ViewRenderer<CustomEntry, TextBox>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomEntry> e)
{
base.OnElementChanged(e);
var textBox = new TextBox();
textBox.BorderThickness = new Windows.UI.Xaml.Thickness(1);
textBox.BorderBrush = new SolidColorBrush(GetSolidColorBrush("#444444").Color);
textBox.CornerRadius = new Windows.UI.Xaml.CornerRadius(10);
this.SetNativeControl(textBox);
}
public SolidColorBrush GetSolidColorBrush(string hex)
{
hex = hex.Replace("#", string.Empty);
byte r = (byte)(Convert.ToUInt32(hex.Substring(0, 2), 16));
byte g = (byte)(Convert.ToUInt32(hex.Substring(2, 2), 16));
byte b = (byte)(Convert.ToUInt32(hex.Substring(4, 2), 16));
SolidColorBrush myBrush = new SolidColorBrush(Windows.UI.Color.FromArgb(255, r, g, b));
return myBrush;
}
}
Holy shmoley it's not that hard.
Unless I'm missing something, just wrap it in a Frame that has IsClippedToBounds set to true and then put a corner radius on the frame.
Maybe there's some reason that's not a good solution, I guess, but it's one I use a lot.

Scroll list view to top on Android

I'm using the following to scroll a list view to the top...
var appointmentGroup = appointmentGroups.First();
appointmentsList.ScrollTo(appointmentGroup.First(), appointmentGroup, ScrollToPosition.Start, true);
This scrolls such that the first item of the first group is at the top of the screen. Except, I want the group's header to be at the top.
This seems slightly crazy but I can't see anyway to do this.
Looking at the source code...
position = templatedItems.GetGlobalIndexForGroup(group) + results.Item2 + 1;
It seems determined to scroll to the item instead of the header.
Shared:
using System;
using Xamarin.Forms;
namespace Infrastructure.UI.Xamarin
{
public class ListViewScroll : ListView
{
public Action ScrollToTopImplementation;
public void ScrollToTop() => ScrollToTopImplementation();
}
}
Android:
using Android.Content;
using Droid.Customization;
using Infrastructure.UI.Xamarin;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ListViewScroll), typeof(ListViewScrollRenderer))]
namespace Droid.Customization
{
public class ListViewScrollRenderer : ListViewRenderer
{
public ListViewScrollRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
var list = (ListViewScroll) e.NewElement;
list.ScrollToTopImplementation = () =>
Control.SmoothScrollToPosition(0);
}
}
}
iOS: (In order to have a uniform interface in the calling code.)
using Infrastructure.UI.Xamarin;
using iOS.Customization;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ListViewScroll), typeof(ListViewScrollRenderer))]
namespace iOS.Customization
{
public class ListViewScrollRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
var list = (ListViewScroll) e.NewElement;
list.ScrollToTopImplementation = () =>
Control.ScrollToRow(NSIndexPath.FromRowSection(0, 0), UITableViewScrollPosition.Top, true);
}
}
}

Custom Layout under Editor / Entry

I have an Entry/Editor in Xamarin.Forms, and I want a layout to appear underneath it, as long as it is focused.
The layout consists of a Grid with multiple Buttons in it.
I have added the Grid underneath the Editor in my Layout and toggle its visibility based on the IsFocused property of the Editor.
Sadly, when I press one of the Grids buttons, the Editor loses focus and thus the Layout will go invisibile.
EDIT:
Another very important point is, that the Grid can overlap other controls, currently this is happening, since im adding both the Editor and the Grid into another Grid as the last children.
I might have to workaround this as well, maybe just put this Grid into an android relative layout and add this layout to the native EditText?
I want it to remain visible after clicking one of the buttons. How should I approach this scenario?
I would write EditorRenderer for each platform and there add all buttons, it should fix Focus problem (as you never lost focus :))
Implementation example:
using System;
using Xamarin.Forms;
namespace Common
{
public class CustomEditor : Editor
{
public Action<string> ButtonXCallback { get; set; }
}
}
IOS:
using Common.iOS;
using Common;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof (CustomEditor), typeof (CustomEditorRenderer))]
namespace Common.iOS
{
public class CustomEditorRenderer : EditorRenderer
{
private UIButton ButtonX { get; set; }
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
var element = Element as CustomEditor;
if (Control != null && element != null)
{
if (ButtonX == null)
{
ButtonX = new UIButton();
ButtonX.SetImage(UIImage.FromBundle("ico_x.png"), UIControlState.Normal);
ButtonX.TouchUpInside += (sender, f) => {
var text = Control.Text;
if (element.ButtonXCallback != null)
element.ButtonXCallback.Invoke(text);
};
Control.AddSubview(ButtonX);
}
}
}
}
}
Droid:
using System;
using Android.Content;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Graphics.Drawables.Shapes;
using Common.Droid;
using Common;
using Android.Text;
using Android.Widget;
using Android.Runtime;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Views;
[assembly: ExportRenderer(typeof (CustomEditor), typeof (CustomEditorRenderer))]
namespace Common.Droid
{
public class CustomEditorRenderer : EditorRenderer
{
private ImageButton ButtonX { get; set; }
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
if (Element == null)
return;
var element = (CustomEditor) Element;
if (element == null)
return;
if (ButtonX == null)
{
ButtonX = new ImageButton(Context);
ButtonX.SetImageResource(Resource.Drawable.ico_x);
ButtonX.SetBackgroundColor(Android.Graphics.Color.Transparent);
ButtonX.Click += (sender, f) =>
{
var text = Control.Text;
if (element.ButtonXCallback != null)
element.ButtonXCallback.Invoke(text);
};
AddView(ButtonX);
}
}
}
}

Resources