Im trying to access my bindable property for a custom button that I attempt to code a renderer for. First here is my PCL renderer:
public class BtnRenderer : Button
{
public static readonly BindableProperty HighLightProperty = BindableProperty.Create(nameof(HighlightedBackgroundColor), typeof(Color), typeof(BtnRenderer), default(Color));
public Color HighlightedBackgroundColor
{
get
{
return (Color)GetValue(HighLightProperty);
}
set
{
SetValue(HighLightProperty, value);
}
}
}
As you can see, I intend to set a HighlightedBackgroundColor from XAML, However, I don't know how to access it in my iOS renderer, what I have is:
[assembly: ExportRenderer(typeof(BtnRenderer), typeof(BtnRendereriOS))]
namespace BluetoothExample.iOS
{
public class BtnRendereriOS : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var normalBackgroundColor = Element.BackgroundColor.ToUIColor();
var _highlightBackgroundColor = Element.HighlightedBackgroundColor.ToUIColor(); //HERE IS MY PROBLEM
async Task NormalColorState(UIButton button)
{
await UIView.TransitionNotifyAsync(button, .25, UIViewAnimationOptions.TransitionCrossDissolve, () =>
{
button.BackgroundColor = normalBackgroundColor;
});
}
Control.TouchDown += async (object sender, EventArgs c) =>
{
var button = sender as UIButton;
await UIView.TransitionNotifyAsync(button, .25, UIViewAnimationOptions.TransitionCrossDissolve, () =>
{
button.BackgroundColor = _highlightBackgroundColor;
});
};
}
}
}
How do I access this property correctly?
//HERE IS MY PROBLEM
var _highlightBackgroundColor = Element.HighlightedBackgroundColor.ToUIColor();
Directly using Element is the base of your renderer (VisualElementRenderer<TElement>) so in order to access any custom properties on your subclass, just cast it (BtnRenderer in this case):
var _highlightBackgroundColor = (Element as BtnRenderer).HighlightedBackgroundColor.ToUIColor();
Related
I have a HybridWebView in my app and i need to change the uri dynamically (When the user click at some menu the uri changes), but it does not change at all... I use WebView and it works great, but when I use the HybridWebView the Uri is not changing.
This ContentView where is my HybridWebView is inside an AbsoluteLayout...
<ContentView AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0,0,1,1"
x:Name="contentView">
<local1:HybridWebView x:Name="hybridWebView"/></ContentView>
This is the code behind (Where i handle the click on the menus to change the url, every click is a different content):
public async void ChangeURL() { hybridWebView.Uri = MyUrl; }
This is my HybridWebView class:
public class HybridWebView : WebView
{
Action<string> action;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: nameof(Uri),
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void Cleanup()
{
action = null;
}
public void InvokeAction(string data)
{
if (action == null || data == null)
{
return;
}
action.Invoke(data);
}
public HybridWebView()
{
}
}
I just resolved it like this:
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control.Url != null)
{
var idn = new System.Globalization.IdnMapping();
var dotnetUri = new System.Uri(Element.Uri);
_url = ((HybridWebView)Element).Uri;
NSUrl nsUrl = new NSUrl(dotnetUri.Scheme, idn.GetAscii(dotnetUri.DnsSafeHost), dotnetUri.PathAndQuery);
Control.LoadRequest(new NSUrlRequest(nsUrl));
}
}
I had to add the OnElementPropertyChanged to change the url dynamically by the CustomRenderer
I´m using a custom renderer to customize the height and color of my progress bar, but my progress bar gets blurred:
My CustomRenderer looks like this:
public class ColorProgressBarRenderer : ProgressBarRenderer
{
public ColorProgressBarRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ProgressBar> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
if (Control != null)
{
UpdateBarColor();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ColorProgressBar.BarColorProperty.PropertyName)
{
UpdateBarColor();
}
}
private void UpdateBarColor()
{
var element = Element as ColorProgressBar;
Control.ProgressTintList = Android.Content.Res.ColorStateList.ValueOf(element.BarColor.ToAndroid());
Control.ScaleY = 10f;
}
}
My CustomProgressBar looks like this:
public class ColorProgressBar : ProgressBar
{
//public static BindableProperty BarColorProperty = BindableProperty.Create<ColorProgressBar, Color>(p => p.BarColor, default(Color));
public static BindableProperty BarColorProperty = BindableProperty.Create(nameof(BarColor), typeof(Color), typeof(ColorProgressBar), default(Color));
public Color BarColor
{
get { return (Color)GetValue(BarColorProperty); }
set { SetValue(BarColorProperty, value); }
}
}
This only happens in Android, with my iOS Renderer all is working fine!
Why this is happen?
You can try this advanced progress bar.
https://www.nuget.org/packages/MultiColor.ProgressBar/
Documentation: https://github.com/udayaugustin/ProgressBar/blob/master/README.md
It supports multi color.
It has properties like
Height
Width
Corner Radius and etc
I'm using Windows Template Studio (Prism) to create a test project, but from the documentation, I'm unable to figure out navigation. I now know how to do it with MVVM Light, but had to ask, because documentation doesn't detail it. How do I navigate from page to page in Prism Windows Template Studio?
Works for MVVM Light:
ViewModelLocator.Current.NavigationService.Navigate(typeof(MyViewModel).FullName, null)
Works on Template10:
BootStrapper.Current.NavigationService.Navigate(typeof(MyViewPage), null)
You could create test app with Windows Template Studio and check Navigation Page project type then check prism Design pattern. You will find the _navigationService in the ShellViewModel class.
Works on Windows Template Studio Prism
_navigationService.Navigate(pageKey, null);
ShellViewModel.cs
public class ShellViewModel : ViewModelBase
{
private static INavigationService _navigationService;
private WinUI.NavigationView _navigationView;
private bool _isBackEnabled;
private WinUI.NavigationViewItem _selected;
public ICommand ItemInvokedCommand { get; }
public bool IsBackEnabled
{
get { return _isBackEnabled; }
set { SetProperty(ref _isBackEnabled, value); }
}
public WinUI.NavigationViewItem Selected
{
get { return _selected; }
set { SetProperty(ref _selected, value); }
}
public ShellViewModel(INavigationService navigationServiceInstance)
{
_navigationService = navigationServiceInstance;
ItemInvokedCommand = new DelegateCommand<WinUI.NavigationViewItemInvokedEventArgs>(OnItemInvoked);
}
public void Initialize(Frame frame, WinUI.NavigationView navigationView)
{
_navigationView = navigationView;
frame.NavigationFailed += (sender, e) =>
{
throw e.Exception;
};
frame.Navigated += Frame_Navigated;
_navigationView.BackRequested += OnBackRequested;
}
private void OnItemInvoked(WinUI.NavigationViewItemInvokedEventArgs args)
{
var item = _navigationView.MenuItems
.OfType<WinUI.NavigationViewItem>()
.First(menuItem => (string)menuItem.Content == (string)args.InvokedItem);
var pageKey = item.GetValue(NavHelper.NavigateToProperty) as string;
_navigationService.Navigate(pageKey, null);
}
private void Frame_Navigated(object sender, NavigationEventArgs e)
{
IsBackEnabled = _navigationService.CanGoBack();
Selected = _navigationView.MenuItems
.OfType<WinUI.NavigationViewItem>()
.FirstOrDefault(menuItem => IsMenuItemForPageType(menuItem, e.SourcePageType));
}
private void OnBackRequested(WinUI.NavigationView sender, WinUI.NavigationViewBackRequestedEventArgs args)
{
_navigationService.GoBack();
}
private bool IsMenuItemForPageType(WinUI.NavigationViewItem menuItem, Type sourcePageType)
{
var sourcePageKey = sourcePageType.Name;
sourcePageKey = sourcePageKey.Substring(0, sourcePageKey.Length - 4);
var pageKey = menuItem.GetValue(NavHelper.NavigateToProperty) as string;
return pageKey == sourcePageKey;
}
}
_navigationService comes from ShellViewModel construct method. And this instance was created in the App class.
protected override UIElement CreateShell(Frame rootFrame)
{
var shell = Container.Resolve<ShellPage>();
shell.SetRootFrame(rootFrame);
return shell;
}
I have a xamarin forms image, and when the user taps on the image I want to get the x,y coordinates of where the user tapped. Not the x,y coordinates of the view per se, but the coordinates within the image. I have this working on Android below. What would the iOS custom renderer be like?
Create a interface for touch event:
public interface IFloorplanImageController
{
void SendTouched();
}
Create a custom control for image:
public class FloorplanImage : Image, IFloorplanImageController
{
public event EventHandler Touched;
public void SendTouched()
{
Touched?.Invoke(this, EventArgs.Empty);
}
public Tuple<float, float> TouchedCoordinate
{
get { return (Tuple<float, float>)GetValue(TouchedCoordinateProperty); }
set { SetValue(TouchedCoordinateProperty, value); }
}
public static readonly BindableProperty TouchedCoordinateProperty =
BindableProperty.Create(
propertyName: "TouchedCoordinate",
returnType: typeof(Tuple<float, float>),
declaringType: typeof(FloorplanImage),
defaultValue: new Tuple<float, float>(0, 0),
propertyChanged: OnPropertyChanged);
public static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
}
}
Implement the custom renderer:
[assembly: ExportRenderer(typeof(FloorplanImage), typeof(FloorplanImageRenderer))]
namespace EmployeeApp.Droid.Platform
{
public class FloorplanImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
if (Control != null)
{
Control.Clickable = true;
Control.SetOnTouchListener(ImageTouchListener.Instance.Value);
Control.SetTag(Control.Id, new JavaObjectWrapper<FloorplanImage> { Obj = Element as FloorplanImage });
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (Control != null)
{
Control.SetOnTouchListener(null);
}
}
base.Dispose(disposing);
}
private class ImageTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
public static readonly Lazy<ImageTouchListener> Instance = new Lazy<ImageTouchListener>(
() => new ImageTouchListener());
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
var obj = v.GetTag(v.Id) as JavaObjectWrapper<FloorplanImage>;
var element = obj.Obj;
var controller = element as IFloorplanImageController;
if (e.Action == Android.Views.MotionEventActions.Down)
{
var x = e.GetX();
var y = e.GetY();
element.TouchedCoordinate = new Tuple<float, float>(x, y);
controller?.SendTouched();
}
else if (e.Action == Android.Views.MotionEventActions.Up)
{
}
return false;
}
}
}
public class JavaObjectWrapper<T> : Java.Lang.Object
{
public T Obj { get; set; }
}
}
Use this control like this:
<local:FloorplanImage HeightRequest="300" x:Name="image" WidthRequest="300"
Aspect="AspectFit" Touched="image_Touched" />
code behind:
private void image_Touched(object sender, EventArgs e)
{
var cor = image.TouchedCoordinate;
}
I found this Customer success example on github that does exactly this.
[assembly: ExportRenderer(typeof(CustomImage), typeof(CustomImageRenderer))]
namespace FormsImageTapGesture.iOS
{
public class CustomImageRenderer : ImageRenderer
{
#region properties & fields
// ---------------------------------------------------------------------------
//
// PROPERTIES & FIELDS
//
// ---------------------------------------------------------------------------
private UIImageView nativeElement;
private CustomImage formsElement;
#endregion
#region methods
// ---------------------------------------------------------------------------
//
// METHODS
//
// ---------------------------------------------------------------------------
//
// Set up the custom renderer. In this case, that means set up the gesture
// recognizer.
//
protected override void OnElementChanged(ElementChangedEventArgs<Image> e) {
base.OnElementChanged (e);
if (e.NewElement != null) {
// Grab the Xamarin.Forms control (not native)
formsElement = e.NewElement as CustomImage;
// Grab the native representation of the Xamarin.Forms control
nativeElement = Control as UIImageView;
// Set up a tap gesture recognizer on the native control
nativeElement.UserInteractionEnabled = true;
UITapGestureRecognizer tgr = new UITapGestureRecognizer (TapHandler);
nativeElement.AddGestureRecognizer (tgr);
}
}
//
// Respond to taps.
//
public void TapHandler(UITapGestureRecognizer tgr) {
CGPoint touchPoint = tgr.LocationInView (nativeElement);
formsElement.OnTapEvent ((int)touchPoint.X, (int)touchPoint.Y);
}
#endregion
}
}
I am using Xamarin Forms picker control and require setting the text color, however there is no such property. I have tried making a custom renderer which worked out for me in android and ios (I ended up redrawing the control). In the wp8.1 platform there is no Draw event and the control itself in the renderer doesn't seem to have the properties to set the text color. I have also attempted changing the control the picker binds to unsuccessfully.
Currently I have created the bindable property TextColor in the PCL which is working. The code for my renderer is shown below (I have stripped all my test code and am putting only the basic code since I havent found anything useful yet and am putting my code just to keep everyone in context). Also note that the property Picker.TextColorProperty doesnt exist and is what I would like to do...
using Namespace.CustomControls;
using Namespace.WinPhone.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.WinPhone;
[assembly: ExportRendererAttribute(typeof(BindablePicker), typeof(BindablePickerRenderer))]
namespace Namspace.WinPhone.Renderers
{
public class BindablePickerRenderer : PickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
var picker = e.NewElement;
BindablePicker bp = (BindablePicker)this.Element;
if (this.Control != null)
{
var pickerStyle = new Style(typeof(Picker))
{
Setters = {
new Setter {Property = Picker.BackgroundColorProperty, Value = bp.BackgroundColor},
new Setter {Property = Picker.TextColorProperty, Value = bp.TextColor}
}
};
picker.Style = pickerStyle;
}
}
}
}
Anyhow I am wondering if anyone might have a little more knowledge on how to do this and could shed some light on me.
There is no TextColor property available in the Picker like you mention.
Even this being the case, we can still achieve changing the Picker text color for WindowsPhone.
I'm assuming you are inheriting from PickerRenderer as it was missing from your code example and I've added some extra things so this is more helpful to others:-
Define the interface in the PCL:-
public interface ICustomPicker2
{
Xamarin.Forms.Color MyBackgroundColor { get; set; }
Xamarin.Forms.Color MyTextColor { get; set; }
}
Extend the Xamarin.Forms Picker in the PCL:-
public class CustomPicker2
: Xamarin.Forms.Picker
, ICustomPicker2
{
public static readonly BindableProperty MyBackgroundColorProperty = BindableProperty.Create<CustomPicker2, Xamarin.Forms.Color>(p => p.MyBackgroundColor, default(Xamarin.Forms.Color));
public static readonly BindableProperty MyTextColorProperty = BindableProperty.Create<CustomPicker2, Xamarin.Forms.Color>(p => p.MyTextColor, default(Xamarin.Forms.Color));
public Xamarin.Forms.Color MyTextColor
{
get { return (Xamarin.Forms.Color)GetValue(MyTextColorProperty); }
set { SetValue(MyTextColorProperty, value); }
}
public Xamarin.Forms.Color MyBackgroundColor
{
get { return (Xamarin.Forms.Color)GetValue(MyBackgroundColorProperty); }
set { SetValue(MyBackgroundColorProperty, value); }
}
}
Create your WindowsPhone renderer like so in a class library:-
public class CustomPicker2Renderer
: PickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
var picker = e.NewElement;
CustomPicker2 bp = (CustomPicker2)this.Element;
if (this.Control != null)
{
var pickerStyle = new Style(typeof(Picker))
{
Setters = {
new Setter {Property = Picker.BackgroundColorProperty, Value = bp.MyBackgroundColor},
}
};
SetPickerTextColor(bp.MyTextColor);
picker.Style = pickerStyle;
}
}
private void SetPickerTextColor(Xamarin.Forms.Color pobjColor)
{
byte bytR = (byte)(pobjColor.R * 255);
byte bytG = (byte)(pobjColor.G * 255);
byte bytB = (byte)(pobjColor.B * 255);
byte bytA = (byte)(pobjColor.A * 255);
//
((System.Windows.Controls.Control)(((System.Windows.Controls.Panel)this.Control).Children[0])).Foreground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(bytA, bytR, bytG, bytB));
}
Note, the above is all what you need if you just want to set the text color the once.
However if you want to change the color after it has been initially set, then you will need to listen to the property change and act upon it like in the following:-
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
//
if (e.PropertyName == "MyTextColor")
{
SetPickerTextColor((this.Element as CustomPicker2).MyTextColor);
}
}
You will also need to export the renderer from the class library as well:-
[assembly: ExportRendererAttribute(typeof(CustomPicker2), typeof(CustomPicker2Renderer))]