Input Accessory View in Xamarin ios(Forms) in the PickerRenderer - xamarin.forms

How to add the Title "State" in the middle of the ToolBar for the PickerRenderer in Xamarin ios(Forms) ?

You could check the following code
using System;
using xxx.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(Picker), typeof(MyiOSPickerRenderer))]
namespace xxx.iOS
{
public class MyiOSPickerRenderer : PickerRenderer, IUIPickerViewDelegate, IUIPickerViewDataSource
{
string SelectedValue;
public MyiOSPickerRenderer()
{
}
public nint GetComponentCount(UIPickerView pickerView)
{
return 1;
}
public nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
return Element.Items.Count;
}
[Export("pickerView:viewForRow:forComponent:reusingView:")]
public UIView GetView(UIPickerView pickerView, nint row, nint component, UIView view)
{
UILabel label = new UILabel
{
//here you can set the style of item!!!
TextColor = UIColor.Blue,
Text = Element.Items[(int)row].ToString(),
TextAlignment = UITextAlignment.Center,
};
return label;
}
[Export("pickerView:didSelectRow:inComponent:")]
public void Selected(UIPickerView pickerView, nint row, nint component)
{
Control.Text = Element.Items[(int)row];
SelectedValue = Element.Items[(int)row];
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
SelectedValue = Element.Items[0];
UIPickerView pickerView = (UIPickerView)Control.InputView;
pickerView.WeakDelegate = this;
pickerView.DataSource = this;
UIToolbar toolbar = (UIToolbar)Control.InputAccessoryView;
UIBarButtonItem save = new UIBarButtonItem("Save", UIBarButtonItemStyle.Done, (object sender, EventArgs click) =>
{
Control.Text = SelectedValue;
toolbar.RemoveFromSuperview();
pickerView.RemoveFromSuperview();
Control.ResignFirstResponder();
});
UIBarButtonItem Title = new UIBarButtonItem("States", UIBarButtonItemStyle.Done, null);
UIBarButtonItem cancel = new UIBarButtonItem("Cancel", UIBarButtonItemStyle.Bordered, (object sender, EventArgs click) =>
{
toolbar.RemoveFromSuperview();
pickerView.RemoveFromSuperview();
Control.ResignFirstResponder();
});
UIBarButtonItem empty = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace, null);
toolbar.Items = new UIBarButtonItem[] { cancel, empty, Title, empty, save };
}
}
}
}

Related

How to change DatePicker Ok and Cancel button text in Xamarin forms?

I have an Xamarin.Forms app that supports many languages. How do I show the Calender for the DatePicker with DatePicker Ok and Cancel button text in local language text from resource file?
My custom renderer
[assembly: ExportRenderer(typeof(CustomImageDatePicker), typeof(CustomImageDatePickerRenderer))]
namespace AMS.Droid.Renderers
{
public class CustomImageDatePickerRenderer : DatePickerRenderer
{
public CustomImageDatePickerRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
try
{
base.OnElementChanged(e);
CustomImageDatePicker element = (CustomImageDatePicker)this.Element;
if (Control == null)
return;
Control.Background = null;
if (Control != null && this.Element != null && !string.IsNullOrEmpty(element.Image))
{
Control.Background = AddPickerStyles(element.Image);
}
}
catch (Exception ex)
{
var message = ex.Message;
}
}
public LayerDrawable AddPickerStyles(string imagePath)
{
GradientDrawable gd = new GradientDrawable();
gd.SetColor(Android.Graphics.Color.Transparent);
gd.SetCornerRadius(25);
gd.SetStroke(3, Android.Graphics.Color.Black);
this.Control.SetBackgroundColor(Android.Graphics.Color.Transparent);
this.Control.SetPadding(20, 10, -50, 10);
Drawable[] layers = { gd, GetDrawable(imagePath) };
LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.SetLayerInset(1, 0, 0, 30, 0);
return layerDrawable;
}
private BitmapDrawable GetDrawable(string imagePath)
{
try
{
int resID = Resources.GetIdentifier(imagePath.ToLower(), "drawable", this.Context.PackageName);
var drawable = ContextCompat.GetDrawable(this.Context, Resource.Drawable.brandIcon);
drawable.SetBounds(0, 0, (int)(drawable.IntrinsicWidth * 0.5),
(int)(drawable.IntrinsicHeight * 0.5));
var bitmap = ((BitmapDrawable)drawable).Bitmap;
var result = new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, 60, 60, true));
result.Gravity = Android.Views.GravityFlags.Right;
//result.SetBounds(10, 10, 50, 0);
return result;
}
catch(Exception ex)
{
var message = ex.Message;
}
return null;
}`
Xaml:
<customDatePicker:CustomDatePicker
x:Name="dpFromDate"
DateSelected="FromDate_Selected"
Margin="10,5,10,0"
Image="brandIcon.png"/>
You could do this with custom renderer.
On Android, the DatePicker dialog can be customized by overriding the CreateDatePickerDialog method in a custom renderer.
[assembly: ExportRenderer(typeof(Xamarin.Forms.DatePicker), typeof(CustomDatePickerRenderer))]
namespace App10.Droid
{
public class CustomDatePickerRenderer : DatePickerRenderer
{
public CustomDatePickerRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.DatePicker> e)
{
base.OnElementChanged(e);
//Disposing
if (e.OldElement != null)
{
datePicker = null;
}
//Creating
if (e.NewElement != null)
{
datePicker = e.NewElement;
}
}
DatePickerDialog pickerDialog;
private Xamarin.Forms.DatePicker datePicker;
protected override DatePickerDialog CreateDatePickerDialog(int year, int month, int day)
{
pickerDialog = new DatePickerDialog(Context, (o, e) =>
{
datePicker.Date = e.Date;
((IElementController)datePicker).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
}, year, month, day);
//ok
pickerDialog.SetButton((int)DialogButtonType.Positive, "(FR)-DONE", OnDone);
//cancel
pickerDialog.SetButton((int)DialogButtonType.Negative, "(FR)-CLEAR", OnCancel);
return pickerDialog;
}
private void OnCancel(object sender, DialogClickEventArgs e)
{
datePicker.Unfocus();
}
private void OnDone(object sender, DialogClickEventArgs e)
{
datePicker.Date = ((DatePickerDialog)sender).DatePicker.DateTime;
datePicker.Unfocus();
}
}
}
You could replace the (FR)-DONE and (FR)-CLEAR with your own local language text from resource file
//ok
pickerDialog.SetButton((int)DialogButtonType.Positive, "(FR)-DONE", OnDone);
//cancel
pickerDialog.SetButton((int)DialogButtonType.Negative, "(FR)-CLEAR", OnCancel);

Master detail page using MVVM

public MainPageView()
{
InitializeComponent();
MasterPage.NavMenuMListView.ItemSelected += OnItemSelected;
}
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
MasterNavigationItem item = (MasterNavigationItem)e.SelectedItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.Target));
MasterPage.NavMenuMListView.SelectedItem = null;
IsPresented = false;
}
}
I have a master detail page(this is the code behind) and i want to respect mvvm conventions. this approach that is used in the docs uses the model inside the view. how can i make that onitemselected function in the viewmodel?
how can i make that onitemselected function in the viewmodel?
Download the source file from the link below. https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/navigation-masterdetailpage/
I make some changes with the source file. Use the command to binding for the listview with the ItemSelected event.
Install the package on NuGet.
Xamarin.Forms.BehaviorsPack: https://github.com/nuitsjp/Xamarin.Forms.BehaviorsPack
MasterPage.xaml
<ListView.Behaviors>
<behaviorsPack:SelectedItemBehavior Command="{Binding SelectedItemCommand}" />
</ListView.Behaviors>
MainPage.cs
public partial class MainPage : MasterDetailPage
{
public ICommand SelectedItemCommand { get; set; }
public MainPage()
{
InitializeComponent();
SelectedItemCommand = new Command(execute: () =>
{
var item = masterPage.listView.SelectedItem as MasterPageItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
masterPage.listView.SelectedItem = null;
IsPresented = false;
}
});
masterPage.BindingContext = this;
//masterPage.listView.ItemSelected += OnItemSelected;
//if (Device.RuntimePlatform == Device.UWP)
//{
// MasterBehavior = MasterBehavior.Popover;
//}
}
//void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
//{
// var item = e.SelectedItem as MasterPageItem;
// if (item != null)
// {
// Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
// masterPage.listView.SelectedItem = null;
// IsPresented = false;
// }
//}
}

Xamarin.Forms 3.3 WKWebView inject javascript

I have followed this link Xamarin.Forms WKWebView to inject Javascript into WebView for iOS. It has worked until Xamarin.Forms 3.3.
In Xamarin.Forms 3.3, the default custom renderer for iOS can be changed from UIWebView to WKWebView. I have followed the changes in AssemblyInfo.cs. Xamarin.Forms 3.3.0. Unfortunately, the changes break the codes.
Below are the changes
//protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
//if (Control == null)
//{
userController = new WKUserContentController();
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
//var config = new WKWebViewConfiguration { UserContentController = userController };
webView = NativeView as WKWebView;
webView.Configuration.UserContentController = userController;
webView.WeakUIDelegate = Self;
view = Element as BibleWebView;
//webView = (WKWebView)Control;
// var cgRect = new CoreGraphics.CGRect(view.X, view.Y, view.WidthRequest, view.HeightRequest);
// webView = new WKWebView(cgRect, config)
// {
// WeakUIDelegate = Self,
// };
// SetNativeControl(webView);
//}
//if (e.OldElement != null)
//{
// userController.RemoveAllUserScripts();
// userController.RemoveScriptMessageHandler("invokeAction");
// var hybridWebView = e.OldElement as BibleWebView;
// hybridWebView.Cleanup();
// e.OldElement.ShowPopup -= OnShowPopup;
//}
//if (Control != null)
//{
// BibleWebView webview = Element as BibleWebView;
HtmlWebViewSource htmlSource = (HtmlWebViewSource)view.Source;
string html = htmlSource.Html;
webView.LoadHtmlString(new NSString(html), NSBundle.MainBundle.ResourceUrl);
view.ShowPopup += OnShowPopup;
//}
}
The old codes are commented and the new codes are uncommented. Any help will be much appreciated.
I'm using a custom renderer with Xamarin.Forms 3.3 using EvaluateJavaScript to load javascript in a loaded page.
HybridWebView is a class in my shared project that inherit from Xamarin.Forms.WebView
First option
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>
{
WKUserContentController userController;
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var formsWebView = sender as WebView;
if (formsWebView != null)
{
userController = new WKUserContentController();
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config);
SetNativeControl(webView);
Control.AllowsBackForwardNavigationGestures = true;
Control.NavigationDelegate = new CustomWebViewClient(Element);
if((formsWebView.Source as UrlWebViewSource) != null)
{
string url = System.Web.HttpUtility.UrlPathEncode((formsWebView.Source as UrlWebViewSource).Url);
Control.LoadRequest(new NSUrlRequest(new NSUrl(url)));
}
else if((formsWebView.Source as HtmlWebViewSource) != null)
{
Control.LoadHtmlString((formsWebView.Source as HtmlWebViewSource).Html, new NSUrl(""));
}
}
}
public class CustomWebViewClient : WKNavigationDelegate, INSUrlConnectionDataDelegate
{
private HybridWebView _webclient;
private WKWebView _webView;
public CustomWebViewClient(HybridWebView webclient)
{
_webclient = webclient;
}
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
string allowZoom = #"javascript:
var div = document.createElement('div');
div.setAttribute('id', 'div1');
div.innerHTML = 'Test';
document.getElementsByClassName('container')[0].appendChild(div);
div.onclick = document.getElementById('div1').onclick = function() { div.innerHTML = 'Change text'; }";
webView.EvaluateJavaScript(allowZoom, null);
_webView = webView;
}
}
}
Second option
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var formsWebView = sender as WebView;
string allowZoom = #"javascript:
var div = document.createElement('div');
div.setAttribute('id', 'div1');
div.innerHTML = 'Test';
document.getElementsByClassName('container')[0].appendChild(div);
div.onclick = document.getElementById('div1').onclick = function() { div.innerHTML = 'Change text'; }";
if (formsWebView != null)
{
userController = new WKUserContentController();
//Using WKUserScript
var script = new WKUserScript(new NSString(allowZoom), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config);
SetNativeControl(webView);
Control.AllowsBackForwardNavigationGestures = true;
Control.NavigationDelegate = new CustomWebViewClient(Element);
if((formsWebView.Source as UrlWebViewSource) != null)
{
string url = System.Web.HttpUtility.UrlPathEncode((formsWebView.Source as UrlWebViewSource).Url);
Control.LoadRequest(new NSUrlRequest(new NSUrl(url)));
}
else if((formsWebView.Source as HtmlWebViewSource) != null)
{
Control.LoadHtmlString((formsWebView.Source as HtmlWebViewSource).Html, new NSUrl(""));
}
}
}

Get click event from linkbutton in compositecontrol in Web Forms

I have created a composite control which houses a number of tiles (which are also custom controls). In my web page I want to catch the click-event of a tile, but I can't figure out how.
This is the TileButton: (the components can be designed dynamically, but I left this part out to keep it simple)
public class TileButton : LinkButton
{
public string Title { get; set; }
public string ImageUrl { get; set; }
private System.Web.UI.WebControls.Label title =
new System.Web.UI.WebControls.Label();
private Image image = new Image();
protected override void CreateChildControls()
{
base.CreateChildControls();
title.Text = Title;
image.ImageUrl = ImageUrl;
Controls.Add(title);
Controls.Add(image);
}
protected override void RenderContents(HtmlTextWriter writer)
{
RenderTitle(writer);
RenderImage(writer);
}
private void RenderImage(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Class,
"some css classes");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
image.RenderControl(writer);
writer.RenderEndTag();
}
private void RenderTitle(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Class,
"other css classes");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.AddAttribute(HtmlTextWriterAttribute.Class,
"title css classes");
writer.RenderBeginTag(HtmlTextWriterTag.H6);
title.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
}
}
This is the composite control:
public class TilesControl : CompositeDataBoundControl
{
public List<TileButton> TileButtons { get; set; } =
new List<TileButton>();
protected override int CreateChildControls(IEnumerable datasource, bool databinding)
{
base.CreateChildControls();
int count = 0;
foreach (var item in datasource)
{
var tileButton = item as TileButton;
tileButton.ID = "tile" + count;
Controls.Add(tileButton);
TileButtons.Add(tileButton);
count++;
}
return count;
}
protected override void Render(HtmlTextWriter writer)
{
AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Class, "some css classes");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
foreach (var tile in TileButtons)
{
tile.CssClass = "button css classes";
tile.RenderControl(writer);
}
writer.RenderEndTag();
}
}
And in the page it's included like this:
<Something:TilesControl runat="server" ID="tiles1" DataSourceID="source1"></Something:TilesControl>
It all renders perfectly, the datasource is bound, but now I want the code behind to do something when a tileButton is clicked.
Extra: I would like the TileButton to have a property that defines WHAT to do when the button is clicked (some type of delegate?). Any pointers on that?
Thanks a lot.
It looks like you are binding a List<TileButton> as the DataSource of TilesControl. So you can bind a Click event to one of those buttons in the List before you bind them as the DataSource.
protected void Page_Load(object sender, EventArgs e)
{
//create a new tilescontrol instance
TilesControl tc = new TilesControl();
//create a list of tilebuttons
List<TileButton> buttons = new List<TileButton>();
//add some buttons for testing
for (int i = 0; i < 10; i++)
{
TileButton b = new TileButton();
b.ID = "TileButton" + i;
b.Title = "TileButton " + i;
//add the click event of a button here
b.Click += TileButton_Click;
//add the button to the list
buttons.Add(b);
}
//bind the list of buttons to the tilescontrol
tc.DataSource = buttons;
tc.DataBind();
//add the tilescontrol to the page
PlaceHolder1.Controls.Add(tc);
}
private void TileButton_Click(object sender, EventArgs e)
{
//display results
Label1.Text = ((TileButton)sender).Title + " Clicked!";
}

Xamarin Forms Custom Stepper with 2 buttons and Entry

I implemented this CustomStepper:
using System;
using Xamarin.Forms;
namespace AppXamarin
{
public class CustomStepper : StackLayout
{
Button PlusBtn;
Button MinusBtn;
Entry Entry;
public static readonly BindableProperty TextProperty =
BindableProperty.Create(
propertyName: "Text",
returnType: typeof(int),
declaringType: typeof(CustomStepper),
defaultValue: 0,
defaultBindingMode: BindingMode.TwoWay);
public int Text
{
get { return (int)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public CustomStepper()
{
PlusBtn = new Button { WidthRequest = 30, HeightRequest = 30 };
MinusBtn = new Button { WidthRequest = 30, HeightRequest = 30 };
PlusBtn.Image = "exp20181029Artboard51";
MinusBtn.Image = "exp20181029Artboard52";
switch (Device.RuntimePlatform)
{
case Device.UWP:
case Device.Android:
{
PlusBtn.BackgroundColor = Color.Transparent;
MinusBtn.BackgroundColor = Color.Transparent;
break;
}
case Device.iOS:
{
PlusBtn.BackgroundColor = Color.Transparent;
MinusBtn.BackgroundColor = Color.Transparent;
break;
}
}
Orientation = StackOrientation.Horizontal;
PlusBtn.Clicked += PlusBtn_Clicked;
MinusBtn.Clicked += MinusBtn_Clicked;
Entry = new Entry { PlaceholderColor = Color.Gray, Keyboard = Keyboard.Numeric, WidthRequest = 30, BackgroundColor = Color.Transparent, FontSize = 15 };
Entry.Keyboard = Keyboard.Numeric;
Entry.Behaviors.Add(new NumericValidationBehavior());
Entry.SetBinding(Entry.TextProperty, new Binding(nameof(Text), BindingMode.TwoWay, source: this));
Entry.HorizontalTextAlignment = TextAlignment.Center;
Entry.TextChanged += Entry_TextChanged;
Children.Add(MinusBtn);
Children.Add(Entry);
Children.Add(PlusBtn);
}
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
if (!string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue != ".")
this.Text = int.Parse(e.NewTextValue);
}
private void MinusBtn_Clicked(object sender, EventArgs e)
{
if (Text > 0)
Text--;
}
private void PlusBtn_Clicked(object sender, EventArgs e)
{
Text++;
}
}
}
When placing normally in the page I can access it and take the text property and use it in my Xaml.cs code. But in my case, I'm placing it inside a listview and as you know in listview the items are bindable I can't access it directly. In the regular stepper when it is placed in the listview we can use the "ValueChanged" method and can easily get the value by using e.NewValue in the "ValueChanged" method in the Xaml.cs file. Is there a way that I can add something to the CustomStepper class that can help me access the Text property and uses it in the Xaml.cs file? Thanks in advance
You can create a property for EventHandlers. In this case, you would use the event modifier on the property to tell the program that the property is triggering an event. For example:
private EventHandler onValueChangedEvent = null;
public event EventHandler OnValueChanged
{
add
{
onValueChangedEvent = null;
onValueChangedEvent = value;
}
remove
{
// Will show a warning. You can ignore it.
onValueChangedEvent = null;
}
}
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
if (!string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue != ".")
this.Text = int.Parse(e.NewTextValue);
onValueChangedEvent?.Invoke(this, e);
}
You would then bind/assign an event handler in your xaml.cs code to the OnValueChanged property, which will get triggered when the value changes.

Resources