I am creating a method to display a dialog, this is what I made so far:
public void ShowDialog(String title = "",String msg = "")
{
this.alert.Title = title;
this.alert.AddButton("Ok");
this.alert.Message = msg;
this.alert.Show();
}
this is located to a common class so it can be used with other classes,
I want to add a parameter so that after pressing the "Ok" button it will redirect to a certain UIViewController, I tried it like this:
public void ShowDialog(String title = "",String msg = "", UIViewController view = null)
{
this.alert.Title = title;
this.alert.AddButton("Ok");
this.alert.Message = msg;
this.alert.Clicked += (object sender1, UIButtonEventArgs e) => {
if(e.ButtonIndex.ToString () == "0" && view != null){
NavigationController.PushViewController(view, true);
}
};
this.alert.Show();
}
but clearly this is not working, am I missing something? or is this possible to do?
Thanks in advance..
HOW I CALL IT
Common common = new Common();
common.ShowDialog("Error","Invalid Process!!", new HomeScreen());
I ended up doing this:
public void ShowDialog(String title = "",String msg = "", UIViewController instance = null, UIViewController redirectTo = null)
{
this.alert.Title = title;
this.alert.AddButton("Ok");
this.alert.Message = msg;
this.alert.Clicked += (object sender1, UIButtonEventArgs e) => {
if(e.ButtonIndex.ToString () == "0" && instance != null && redirectTo != null){
instance.NavigationController.PushViewController(redirectTo, true);
}
};
this.alert.Show();
}
then Call it like this:
Common common = new Common();
common.ShowDialog("Error","Invalid Process!", this, new HomeScreen());
Related
I have a combobox in my application, where items are loaded asynchronously depending on a search text you can enter in the text field.
This works fine, but every time the text of the first item is automatically selected during updating the datasource of the combobox.
This leads to unintended behaviour, because I need to have the search text entered by the user to stay in the textfield of the combobox until a selection is done by the user and not automatically overwrite the text with the first entry.
This is my code:
public partial class ProductGroupDescription : UserControl, Interfaces.TabPages.ITabPageProductGroupDescription
{
private Services.IProductGroupDescriptionService _ApplicationService;
public BindingList<ProductGroup> ProductGroups { get; set; } = new BindingList<ProductGroup>();
public string ProductGroupSearchText { get; set; } = string.Empty;
public ProductGroupDescription(Services.IProductGroupDescriptionService applicationService)
{
InitializeComponent();
InitialSetupControls();
_ApplicationService = applicationService;
}
public void InitialSetupControls()
{
var pgBindingSource = new BindingSource();
pgBindingSource.DataSource = ProductGroups;
Cbo_ProductGroup.DataSource = pgBindingSource.DataSource;
Cbo_ProductGroup.DataBindings.Add("Text", ProductGroupSearchText, "");
}
private async void Cbo_ProductGroup_TextChanged(object sender, EventArgs e)
{
if (Cbo_ProductGroup.Text.Length >= 2)
{
ProductGroupSearchText = Cbo_ProductGroup.Text;
Cbo_ProductGroup.SelectedIndex = -1;
bool withStopFlagged = Chk_StopFlag_PGs_Included.Checked;
List<ProductGroup> list = await _ApplicationService.GetProductGroupBySearchString(ProductGroupSearchText, withStopFlagged);
if (list != null && list.Count > 0)
{
ProductGroups.Clear();
list.ForEach(item => ProductGroups.Add(item));
Cbo_ProductGroup.DroppedDown = Cbo_ProductGroup.Items.Count > 0 && Cbo_ProductGroup.Focused;
}
}
}
}
I tried to set Cbo_ProductGroup.SelectedIndex = -1, but it does not solve my issue here.
I also saw this on SO: Prevent AutoSelect behavior of a System.Window.Forms.ComboBox (C#)
But is there really no simpler solution to this issue?
I got it to work now.
It worked, when I removed the binding of the text field of the combobox
Cbo_ProductGroup.DataBindings.Add("Text", ProductGroupSearchText, "");
and set the new (old) value directly to the text field of the combobox.
Cbo_ProductGroup.Text = searchText;
In this case, a new event Text_Changed is fired and so the application has a infinite loop. So I used a property (ShouldTextChangedEventBeIgnored), if the Text_Changed event should be ignored.
Thanks to #CaiusJard for many hints in the comments.
This is my final code:
public partial class ProductGroupDescription : UserControl, Interfaces.TabPages.ITabPageProductGroupDescription
{
private ApplicationLogic.Interfaces.Services.IProductGroupDescriptionService _ApplicationService;
public BindingList<ProductGroup> ProductGroups { get; set; } = new BindingList<ProductGroup>();
public bool ShouldTextChangedEventBeIgnored { get; set; } = false;
public ProductGroupDescription(ApplicationLogic.Interfaces.Services.IProductGroupDescriptionService applicationService)
{
_ApplicationService = applicationService;
InitializeComponent();
InitialSetupControls();
}
public void InitialSetupControls()
{
var pgBindingSource = new BindingSource();
pgBindingSource.DataSource = ProductGroups;
Cbo_ProductGroup.DataSource = pgBindingSource.DataSource;
}
private async Task<List<ProductGroup>> LoadProductGroupItems(string searchText)
{
bool withStopFlagged = Chk_StopFlag_PGs_Included.Checked;
return await _ApplicationService.GetProductGroupBySearchString(searchText, withStopFlagged);
}
private async Task SetProductGroupSearchBoxItems(List<ProductGroup> list, string searchText)
{
await Task.Run(() =>
{
if (list != null && list.Count > 0)
{
ShouldTextChangedEventBeIgnored = true;
Cbo_ProductGroup.Invoke((c) =>
{
ProductGroups.Clear();
list.ForEach(item => ProductGroups.Add(item));
c.DroppedDown = c.Items.Count > 0 && c.Focused;
c.Text = searchText;
c.Select(c.Text.Length, 0);
});
ShouldTextChangedEventBeIgnored = false;
}
});
}
private async void Cbo_ProductGroup_TextChanged(object sender, EventArgs e)
{
try
{
if (Cbo_ProductGroup.Text.Length >= 2 && ShouldTextChangedEventBeIgnored == false)
{
string searchText = Cbo_ProductGroup.Text;
List<ProductGroup> list = await LoadProductGroupItems(Cbo_ProductGroup.Text);
await SetProductGroupSearchBoxItems(list, searchText);
}
}
catch(Exception ex)
{
System.Diagnostics.Trace.Write(ex);
}
}
}
In a WKWebView, clicking on the Tel: links (example: ) does not open the phone dialer with the number from the link as they do in Chrome/Safari.
I have looked at the solution from the link below:
https://forums.xamarin.com/discussion/103689/after-ios-11-upgrade-wkwebview-does-not-load-my-website
However, in my C# project, I am unable to use two base classes (UIViewController, WKNavigationDelegate) in my class as my class WebViewController cannot have multiple base classes.
Is it possible to do this in the DidFinishNavigation method to open the dialer when Tel: links are clicked?
My full code is below with changes that mimics the idea from the link above. Would it be possible for me to achieve this with the way my web view is designed?
[Register("WebViewController")]
public class WebViewController : UIViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
WKWebView webView = new WKWebView(View.Frame, new
WKWebViewConfiguration());
View.AddSubview(webView);
View.SendSubviewToBack(webView);
webView.AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
var url = new NSUrl("link goes here");
var request = new NSUrlRequest(url);
webView.LoadRequest(request);
webView.AllowsBackForwardNavigationGestures = true;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
NavigationController.NavigationBarHidden = true;
}
//open email and tel links
// https://forums.xamarin.com/discussion/103689/after-ios-11-upgrade-wkwebview-does-not-load-my-website
//https://forums.xamarin.com/discussion/47335/how-to-call-a-set-phone-number-from-a-button-click-using-xamarin-ios
[Export("webView:didFinishNavigation:")]
//[Export("webView:decidePolicyForNavigationAction:decisionHandler:")]
void DidFinishNavigation(WKWebView webView, WKNavigation navigation, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
{
var navType = navigationAction.NavigationType;
var targetFrame = navigationAction.TargetFrame;
var url = navigationAction.Request.Url;
if (
(url.ToString().StartsWith("http") && targetFrame == null)
||
url.ToString().StartsWith("mailto:")
|| url.ToString().StartsWith("tel:")
|| url.ToString().StartsWith("Tel:"))
{
UIApplication.SharedApplication.OpenUrl(url);
}
}
}
}
Fixed it by adding a custom navigation delegate class:
public override void ViewDidLoad()
{
base.ViewDidLoad();
WKWebView webView = new WKWebView(View.Frame, new WKWebViewConfiguration());
View.AddSubview(webView);
View.SendSubviewToBack(webView);
webView.AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
var url = new NSUrl("link");
var request = new NSUrlRequest(url);
webView.LoadRequest(request);
webView.AllowsBackForwardNavigationGestures = true;
//assign delegate
webView.NavigationDelegate = new MyWKNavigationDelegate();
}
//custom delegate
class MyWKNavigationDelegate : WKNavigationDelegate
{
[Export("webView:decidePolicyForNavigationAction:decisionHandler:")]
public override void DecidePolicy(WKWebView webView, WKNavigationAction
navigationAction, Action<WKNavigationActionPolicy> decisionHandler)
{
var navType = navigationAction.NavigationType;
var targetFrame = navigationAction.TargetFrame;
var url = navigationAction.Request.Url;
if (
url.ToString().StartsWith("http") && (targetFrame != null &&
targetFrame.MainFrame == true)
)
{
decisionHandler(WKNavigationActionPolicy.Allow);
}
else if (
//(url.ToString().StartsWith("http") && targetFrame == null)
//||
url.ToString().StartsWith("mailto:")
|| url.ToString().StartsWith("tel:")
|| url.ToString().StartsWith("Tel:"))
{
//decisionHandler(WKNavigationActionPolicy.Allow);
UIApplication.SharedApplication.OpenUrl(url);
}
}
}
I am trying to use PageRenderer to customize/reposition elements of ToolbarItem for iOS but here NavigationController throwing null reference exception.
Below my code
public class MyNavigationRenderer: PageRenderer
{
public new MyNavigationBar Element
{
get { return (MyNavigationBar)base.Element; }
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var LeftNavList = new List<UIBarButtonItem>();
var rightNavList = new List<UIBarButtonItem>();
var navigationItem = this.NavigationController.TopViewController.NavigationItem;
for (var i = 0; i < Element.ToolbarItems.Count; i++)
{
var reorder = (Element.ToolbarItems.Count - 1);
var ItemPriority = Element.ToolbarItems[reorder - i].Priority;
if (ItemPriority == 1)
{
UIBarButtonItem LeftNavItems = navigationItem.RightBarButtonItems[i];
LeftNavList.Add(LeftNavItems);
}
else if (ItemPriority == 0)
{
UIBarButtonItem RightNavItems = navigationItem.RightBarButtonItems[i];
rightNavList.Add(RightNavItems);
}
}
navigationItem.SetLeftBarButtonItems(LeftNavList.ToArray(), false);
navigationItem.SetRightBarButtonItems(rightNavList.ToArray(), false);
}
}
Below MyNavigationBar.cs class in portable/shared forms project
public class MyNavigationBar : NavigationPage
{
public MyNavigationBar(Page content) : base(content)
{
Init();
}
private void Init()
{
this.ToolbarItems.Add(new ToolbarItem() { Icon = "kid", Priority = 0, Order = ToolbarItemOrder.Primary });
this.ToolbarItems.Add(new ToolbarItem() { Text = "License", Priority = 0, Order = ToolbarItemOrder.Primary });
}
}
App starting
public App ()
{
InitializeComponent();
MainPage = new MyNavigationBar(new LoginPage());
}
See below screenshot getting exception
I faced this issue, but in my case, I was trying to get NavigationController from content page which didn't had NavigationController, make sure you null check before calling TopViewController,
var navController = this.NavigationController;
if(navController == null)
{
return;
}
UINavigationItem navigationItem = navController.TopViewController.NavigationItem;
For example,
When User opens the app, he will be presented with Login page, which didn't had any Navigation Bar.
I need to "update" the following code:
namespace Pizzahouse.Pages
{
public class IndexPage : ContentPage
{
public IndexPage()
{
Title = "Index";
var telephone = new Button()
{
Text = "Call",
WidthRequest = 50,
};
telephone.Clicked += (sender, e) => Device.OpenUri(new Uri("tel://123465789"));
Content = new ContentView()
{
Content = new StackLayout()
{
Children = {
new Image
{
Aspect = Aspect.AspectFit,
Source = Device.OnPlatform(
ImageSource.FromFile("PizzaIcon.png"),
ImageSource.FromFile("PizzaIcon.png"),
null)
}, telephone
}
}
};
}
}
}
I need to insert an image, but Xamarin.Forms says that the Device.OnPlataform() method is obsolete, and it says that I should use switch(Device.RuntimePlatform).
This exact code works, so what do you suggest? Thanks in advice.
Source = (Device.RuntimePlatform == Device.WinPhone) ? null : ImageSource.FromFile("PizzaIcon.png");
I want to give an out parameter on event but it doesn't work
like this
error :Cannot use parameter out in anonyme method or lambda expression
public void CallMyMethod()
{
//{... code here`removed...just for initialize object}
int? count = null;
MyMethod(myObject,count);
}
public static void MyMethod(AnObject myObject,out int?count)
{
//{... code removed...}
IEnumerable<AnAnotherObject> objects = myObject.GetAllObjects();//... get objects
count = (count == null) ? objects.Count() : count;
MyPopup popup = CreateMypopup();
popup.Show();
popup.OnPopupClosed += (o, e) =>//RoutedEventHandler
{
if (--count <= 0)
{
Finished();//method to finish the reccursive method;
}
else
{
MyMethod(myObject, out count);
}
};
}
The standard way to pass parameters to event handler is to define your own class
public class MyObjectArgs: EventArgs
{
MyObject myObj {get;set}
int? Count {get; set;}
}
and then pass the same instance of this class down the recursive calls
public void CallMyMethod()
{
//{... code here`removed...just for initialize object}
MyObjectArgs args = new MyObjectArgs();
args.Count = null;
args.myOby = myObject;
MyMethod(args);
}
public static void MyMethod(MyObjectArgs args)
{
//{... code removed...}
IEnumerable<AnAnotherObject> objects = args.myObj.GetAllObjects();//... get objects
args.Count = (args.Count == null) ? objects.Count() : args.Count;
MyPopup popup = CreateMypopup();
popup.Show();
popup.OnPopupClosed += (o, e) =>//RoutedEventHandler
{
if (--args.Count <= 0)
{
Finished();//method to finish the reccursive method;
}
else
{
MyMethod(args);
}
};
}
Also the line where you count the objects seems to be incorrect, perhaps you want this ?
args.Count = (args.Count == null) ? objects.Count() : args.Count + objects.Count();