Custom Object set to null after adding to combobox - object

I have created an javafx gui. I also have a custom class, which i use to create an object of that class everytime the add object button is pressed.
when i create a object of the class and print the object to the console the toString() method is called. but when i add the object a combobox and select it java throws a NullPointerException .I used the setOnAction method for the combobox but it doesnt even look at the handle method. Can anyone see whats going on in the stack trace?
The martian class is an abstract class. Martians are either red or green.
protected ComboBox<Martian> currentMartians = new ComboBox<>();
public class MartianSelectorHandler implements EventHandler<ActionEvent>{
#Override
public void handle(ActionEvent event) {
// TODO Auto-generated method stub
System.out.println("Inside combobox handler");
Martian m = (Martian) currentMartians.getValue();
txtaResults.setText(m.toString());
}
public Pane buildRightPane(){
VBox leftPane = new VBox();
leftPane.setPadding(new Insets(15,5,5,0));
leftPane.setAlignment(Pos.TOP_LEFT);
Label label = new Label("Curent Martians");
currentMartians.setPromptText("Martians");
leftPane.getChildren().addAll(label,currentMartians);
MartianSelectorHandler comboBox = new MartianSelectorHandler();
currentMartians.setOnAction(comboBox);
return leftPane;
}
Stack Trace
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at martian_stuff.Martian.equals(Martian.java:20)
at javafx.scene.control.MultipleSelectionModelBase.select(MultipleSelectionModelBase.java:385)
at javafx.scene.control.MultipleSelectionModelBase.clearAndSelect(MultipleSelectionModelBase.java:348)
at javafx.scene.control.ListView$ListViewBitSetSelectionModel.clearAndSelect(ListView.java:1400)
at com.sun.javafx.scene.control.behavior.CellBehaviorBase.simpleSelect(CellBehaviorBase.java:260)
at com.sun.javafx.scene.control.behavior.CellBehaviorBase.doSelect(CellBehaviorBase.java:224)
at com.sun.javafx.scene.control.behavior.CellBehaviorBase.mousePressed(CellBehaviorBase.java:150)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:95)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3758)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3486)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2495)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:350)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$350(GlassViewEventHandler.java:385)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda$315/1947016627.get(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:404)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:384)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:927)
the Martian class is a jar file. but the equals method shoud look something like
public boolean equals(Object o){
//boolean returnValue = ((getId() == ((Martian) o).getId())); ? true : false);
return ((getId() == ((Martian) o).getId()));
}
These methods add to the combo box
public Pane buildRow2(){
GridPane row2 = new GridPane();
row2.setHgap(10);
row2.setVgap(10);
row2.setPadding(new Insets(25, 25, 25, 25));
Button addGM = new Button("Add GM");
addGM.setOnAction(e->{
System.out.println("Adding Green Martian");
int newId = Integer.parseInt(txtfId1.getText());
int newVolume = Integer.parseInt(txtfVolume.getText());
GreenMartian gm = new GreenMartian(newId);
gm.setVolume(newVolume);
if (mm.addMartian(gm) == true){
mm.addMartian(gm);
currentMartians.getItems().add(gm);
txtaResults.appendText("Green Martian ID: " + newId + " added.\n");
}
else{System.out.println("Martian not added");}
});
Button addRM = new Button("Add RM");
addRM.setOnAction(e->{
System.out.println("Adding Red Martian");
int newId = Integer.parseInt(txtfId1.getText());
int newVolume = Integer.parseInt(txtfVolume.getText());
RedMartian rm = new RedMartian(newId);
rm.setVolume(newVolume);
if (mm.addMartian(rm) == true){
mm.addMartian(rm);
currentMartians.getItems().add(rm);
txtaResults.appendText("Red Martian ID: " + newId + " added.\n");
}
else{
txtaResults.appendText("**Martian not added. Duplicate ID.**");
System.out.println("Cant add");
}
});
Button groupSpeak = new Button("Group Speak");
groupSpeak.setOnAction(e->{
String speak = mm.groupSpeak();
txtaResults.setText(speak+"\n");
});
Button groupTele = new Button("Group Teleport");
Label stringDest = new Label("Enter Destination: ");
txtfDest = new TextField();
stringDest.setVisible(false);
txtfDest.setVisible(false);
Button okay1 = new Button("Okay!");
okay1.setOnAction(e->{
if(txtfDest != null)
txtaResults.setText(mm.groupTeleport(txtfDest.getText())+"\n");
stringDest.setVisible(false);
txtfDest.setVisible(false);
okay1.setVisible(false);
});
okay1.setVisible(false);
groupTele.setOnAction(e->{
stringDest.setVisible(true);
txtfDest.setVisible(true);
okay1.setVisible(true);
});
row2.add(addGM, 0, 0);
row2.add(addRM,1,0);
row2.add(groupSpeak,0,1);
row2.add(groupTele, 1,1);
row2.add(stringDest,2,0);
row2.add(txtfDest, 2,1);
row2.add(okay1, 2, 2);
return row2;
}

Related

Desperately need Xamarin.IOS modal MessageBox like popup

Coding in Xamarin IOS. I have a drop down list type popup, where, if The end user types in a new value, I want to ask a yes/no question: Do You want to add a new row?
The control is inside a UIStackView, which is inside a container UIView, which is in turn inside another which is presented via segue. Xamarin demanded a UIPopoverController, which I implemented. Here is The code I have so far:
using System.Threading.Tasks;
using Foundation;
using UIKit;
namespace PTPED_Engine
{
public enum MessagePopupType
{
YesNo = 1,
OKCancel = 2,
OKOnly = 3
}
public enum PopupResultType
{
OK = 1,
Cancel = 2,
Yes = 3,
No = 4
}
public static class AlertPopups
{
static NSObject nsObject;
public static void Initialize(NSObject nsObject)
{
AlertPopups.nsObject = nsObject;
}
public static Task<PopupResultType> AskUser(UIViewController parent, UIView V, string strTitle, string strMsg, MessagePopupType mpt)
{
using (UIPopoverController pc = new UIPopoverController(parent))
{
// pc.ContentViewController
// method to show an OK/Cancel dialog box and return true for OK, or false for cancel
var taskCompletionSource = new TaskCompletionSource<PopupResultType>();
var alert = UIAlertController.Create(strTitle, strMsg, UIAlertControllerStyle.ActionSheet);
// set up button event handlers
if (mpt == MessagePopupType.OKCancel)
{
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, a => taskCompletionSource.SetResult(PopupResultType.OK)));
alert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Default, a => taskCompletionSource.SetResult(PopupResultType.Cancel)));
}
if (mpt == MessagePopupType.YesNo)
{
alert.AddAction(UIAlertAction.Create("Yes", UIAlertActionStyle.Default, a => taskCompletionSource.SetResult(PopupResultType.Yes)));
alert.AddAction(UIAlertAction.Create("No", UIAlertActionStyle.Default, a => taskCompletionSource.SetResult(PopupResultType.No)));
}
if (mpt == MessagePopupType.OKOnly)
{
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, a => taskCompletionSource.SetResult(PopupResultType.OK)));
}
// show it
nsObject.InvokeOnMainThread(() =>
{
pc.PresentFromRect(V.Bounds, V, UIPopoverArrowDirection.Any, true);
});
return taskCompletionSource.Task;
}
}
}
}
and I invoke it as follows:
LookupCombo.Completed += async (object sender, CompletedEventArgs e) =>
{
C1AutoComplete AC = (C1AutoComplete)sender;
if (AC.Text.Trim() != "")
{
string sColName = AC.AccessibilityIdentifier.Trim();
var ValuesVC = (List<Lookupcombo_Entry>)AC.ItemsSource;
var IsThisAHit = from Lookupcombo_Entry in ValuesVC
where Lookupcombo_Entry.sDispVal.ToUpper().Trim() == e.value.ToUpper().Trim()
select Lookupcombo_Entry.sMapVal;
if (!IsThisAHit.Any())
{
string sTitle = "";
string sFull = _RM.GetString(sColName);
if (sFull == null) { sFull = "???-" + sColName.Trim(); }
sTitle = " Add New " + sFull.Trim() + "?";
string sPPrompt = "Do you want to add a new " + sFull.Trim() + " named " + AC.Text.Trim() + " to the Database?";
var popupResult = await AlertPopups.AskUser(CurrentViewController(), V, sTitle, sPPrompt, MessagePopupType.YesNo);
}
}
};
CurrentViewController is defined like this:
private UIViewController CurrentViewController()
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
return vc;
}
This does nothing. It hangs The user interface.
This should be built in, but it is only built in in Xamarin.Forms, which I do not want to use.
I have no problem in doing this stuff with an await, but this is simply not working. Can anyone help?
Thanks!
You can just use the ACR UserDialogs library:
https://github.com/aritchie/userdialogs
This is a solution I provided a few years ago, I think it is an ugly hack, compared to your elegant approach. You did not say what part does not work exactly, that might help spot the problem.
Here is my solution from a few years back:
iphone UIAlertView Modal

How to customize the UIContextualAction in tableview when swipe

I need to create an action button with a image and text. below image provide
an example.
![1]: https://i.stack.imgur.com/KEuHn.png
i have created a method like
public UIContextualAction ContextualFlagAction(int row)
{
var action = UIContextualAction.FromContextualActionStyle(UIContextualActionStyle.Normal, "Flag", Handler);
(contextualAction, view, handler) =>
{
Console.WriteLine("Hello World!");
handler(false);
});
action.Image = UIImage.FromFile(ResourceIdentifiers.DocumentIcon);
return action;
}
but this is not what i need to do.
how can i customize this action as the image in the above.
Maybe your problem showed is the image result,your code have set action.image
If you have a image that contains picture and label , picture is up,label is down,there will be you want.
public UIContextualAction ContextualFlagAction(int row)
{
var action = UIContextualAction.FromContextualActionStyle(UIContextualActionStyle.Normal, "Flag", Handler);
(contextualAction, view, handler) =>
{
Console.WriteLine("Hello World!");
handler(false);
});
action.Image = UIImage.FromFile(ResourceIdentifiers.DocumentIcon);
//this is your setted image
return action;
}
More info:
You can custom a TableViewCell in Xamarin.ios.
Write the following method in UITableViewCell, Rewrite DidTransitionToState method in viewcell, you can replace the action with button
private UITableView tableViewThis;
public TableViewCellClass(UITableView tableView)
{
this.tableViewThis = tableView;
}
public override void DidTransitionToState(UITableViewCellState mask)
{
base.DidTransitionToState(mask);
if ((mask & UITableViewCellState.ShowingDeleteConfirmationMask) == UITableViewCellState.ShowingDeleteConfirmationMask)
{
foreach (UIView subview in tableViewThis.Subviews)
{
if (subview.Class.Equals("UIContextualAction"))
//Delete the delete button of the system
tableViewThis.WillRemoveSubview(subview);
subview.BackgroundColor = UIColor.Clear;
UIButton editBtn = new UIButton(UIButtonType.Custom);
editBtn.Frame = new CGRect(10, 4, 50, 65);
editBtn.SetBackgroundImage(UIImage.FromFile("1.png"), UIControlState.Normal);
editBtn.AdjustsImageWhenHighlighted = false;
editBtn.TouchUpInside += (sender, e) =>
{
//do something you need
};
subview.AddSubview(editBtn);
}
}
}
UIButton can set both Title and Image. UIButton has two properties:
titleEdgeInsets(top,left,bottom,right)
and imageEdgeInsets(top,left,bottom,right).
By setting these two, you can implement the style you need.

PXProcessing SetError not showing in UI Grid

This my process screen:
as you can see it throws errors but it doesnt indicate the error mark on the grid.
After clicking the process button, it just unchecks the checkbox in my records
i want the grid to be like this(with the red 'x' mark):
this is my graph :
public PXCancel<PayrollFilter> Cancel;
public PXSetup<PayrollSetup> PayrollSetup;
public PXFilter<PayrollFilter> Filter;
[PXFilterable]
public PXFilteredProcessingJoin<PayrollEmployeeProcess, PayrollFilter,
InnerJoin<EPEmployee,
On<PayrollEmployee.employeeID, Equal<EPEmployee.bAccountID>>,
InnerJoin<Branch,
On<EPEmployee.parentBAccountID, Equal<Branch.bAccountID>>>>,
Where<PayrollEmployee.payPeriodID, Equal<Current<PayrollFilter.payPeriodID>>,
And<Branch.branchID, Equal<Current<AccessInfo.branchID>>>>> EmployeePayrollProcess;
#region Constructor
public PayrollProcess()
{
PayrollSetup setup = PayrollSetup.Current;
EmployeePayrollProcess.SetSelected<PayrollEmployeeProcess.selected>();
EmployeePayrollProcess.SetProcessDelegate(delegate (List<PayrollEmployeeProcess> employees)
{
if (Filter.Current == null) return;
var payPeriod = Filter.Current.PayPeriodID ?? 0;
var payrollPeriod = Filter.Current.PayrollPeriodID ?? 0;
if (payPeriod == 0 || payrollPeriod == 0) return;
PXLongOperation.StartOperation(this, delegate ()
{
bool errorOccured = false;
foreach (PayrollEmployeeProcess employee in employees)
{
PayrollRegisterEntry graph = PXGraph.CreateInstance<PayrollRegisterEntry>();
try
{
graph.ProcessPayroll(employee, payPeriod, payrollPeriod);
PXProcessing<PayrollEmployeeProcess>.SetInfo("Employee processed");
}
catch (Exception ex)
{
errorOccured = true;
//employees.IndexOf(employee),
PXProcessing<PayrollEmployeeProcess>.SetError(ex);
}
finally
{
graph.Clear();
}
}
if (errorOccured) throw new PXException("At least one employee was not processed.");
});
});
// EmployeePayrollProcess.
}`
can anyone can help me? I'm using Acumatica 6
Throwing an exception in Acumatica sets the error in the header. To set a Row or Field level error you need to set/raise it. There's a few ways to set/raise errors, what they have in common is that they don't use the 'throw' keyword.
For a processing screen with a filter, use the following syntax to raise the error:
PXFilteredProcessing<GridDetailDAC, GridFilterDAC>.SetError(rowIndex, new PXSetPropertyException("Error Message", PXErrorLevel.RowError));
Processing screen without filter:
PXProcessing.SetError(rowIndex, new PXException("Error Message"));

SWTBot for Jface dialog

Am using the following code to create a dialog for my login page. I need to write SWTBot test cases for the dialog that I create.
I have written SWTBot for windows but not for jface dialog.
How to get the access of the dialog in the test class by using the active shell? so that it can detect the buttons or the fields in the dialog.
Besides using active shell, is there any other method to access the dialog that I have created?
package com.login.model;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.layout.GridData;
import com.database.*;
import com.login.controller.UserValidation;
public class PasswordDialog extends Dialog {
private Text txtUser;
private Text txtPassword;
private String user = "";
private String password = "";
protected Composite container;
public Button ok;
public Button cancel;
public PasswordDialog(Shell parentShell) {
super(parentShell);
}
#Override
protected void setShellStyle(int arg0) {
// Use the following not to show the default close X button in the title
// bar
super.setShellStyle(SWT.TITLE);
}
#Override
protected Control createDialogArea(Composite parent) {
container = (Composite) super.createDialogArea(parent);
org.eclipse.swt.graphics.Color color = new org.eclipse.swt.graphics.Color(
container.getDisplay(), 255, 255, 255);
container.setBackground(color);
System.out.println(container);
GridLayout layout = new GridLayout(2, false);
layout.marginRight = 5;
layout.marginLeft = 10;
container.setLayout(layout);
// Grid for labels user and password
GridData gdForLabel = new GridData(SWT.LEFT, SWT.CENTER, false, false,
10, 5);
gdForLabel.horizontalIndent = 84;
// Grid for text box user and password
GridData gdForText = new GridData(SWT.CENTER, SWT.NONE, true, true, 10,
5);
gdForText.horizontalIndent = -30;
gdForText.minimumWidth = 200;
// To display the application image
Image image = new Image(container.getDisplay(), new ImageData(
"C:\\Users\\myName\\workspace\\Login\\Image\\c.gif"));
Label lblImg = new Label(container, SWT.NONE);
lblImg.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true,
10, 10));
lblImg.setImage(image);
lblImg.setBackground(color);
// The username label
Label lblUser = new Label(container, SWT.NONE);
lblUser.setLayoutData(gdForLabel);
lblUser.setFont(new Font(container.getDisplay(), new FontData("Corbel",
12, SWT.NORMAL)));
lblUser.setText("Username");
lblUser.setBackground(color);
// The username text box
txtUser = new Text(container, SWT.BORDER);
txtUser.setFont(new Font(container.getDisplay(), new FontData("Corbel",
12, SWT.NORMAL)));
txtUser.setLayoutData(gdForText);
txtUser.setText(user);
// The password label
Label lblPassword = new Label(container, SWT.NONE);
lblPassword.setLayoutData(gdForLabel);
lblPassword.setFont(new Font(container.getDisplay(), new FontData(
"Corbel", 12, SWT.NORMAL)));
lblPassword.setText("Password");
lblPassword.setBackground(color);
// The password text box
txtPassword = new Text(container, SWT.BORDER | SWT.PASSWORD);
txtPassword.setFont(new Font(container.getDisplay(), new FontData(
"Corbel", 12, SWT.NORMAL)));
txtPassword.setLayoutData(gdForText);
txtPassword.setText(password);
return container;
}
#Override
protected Point getInitialSize() {
// Initialise window size
return new Point(400, 400);
}
#Override
protected void createButtonsForButtonBar(Composite parent) {
// Set Color of Background to white
org.eclipse.swt.graphics.Color color = new org.eclipse.swt.graphics.Color(
container.getDisplay(), 255, 255, 255);
// Change parent layout data to fill the whole bar
parent.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
parent.setBackground(color);
// Create a spacer label
Label spacer = new Label(parent, SWT.NONE);
spacer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
spacer.setBackground(color);
// Update layout of the parent composite to count the spacer
GridLayout layout = (GridLayout) parent.getLayout();
layout.numColumns += 16;
layout.makeColumnsEqualWidth = false;
// The button creation
ok=createButton(parent, IDialogConstants.OK_ID, "OK", true);
cancel=createButton(parent, IDialogConstants.CANCEL_ID, "Cancel", false);
}
#Override
protected void okPressed() {
// Method to invoke connection to database and authenticate
user = txtUser.getText();
password = txtPassword.getText();
DataBaseConnectivity.createConnection();
boolean valid = UserValidation.validateString(user);
if(valid){
boolean auth = DataBaseConnectivity.authenticate(user, password);
// Password authentication
if (auth)
super.okPressed();
else {
Label rt = new Label(container, SWT.CENTER);
rt.setSize(200, 150);
rt.setLocation(100, 295);
org.eclipse.swt.graphics.Color color = new org.eclipse.swt.graphics.Color(
container.getDisplay(), 255, 0, 0);
rt.setForeground(color);
color = new org.eclipse.swt.graphics.Color(container.getDisplay(),
255, 255, 255);
rt.setBackground(color);
rt.setText("Username or Password is wrong");
System.out.println(rt.getText());
}
}
else{
Label rt = new Label(container, SWT.CENTER);
rt.setSize(200, 150);
rt.setLocation(100, 295);
org.eclipse.swt.graphics.Color color = new org.eclipse.swt.graphics.Color(
container.getDisplay(), 255, 0, 0);
rt.setForeground(color);
color = new org.eclipse.swt.graphics.Color(container.getDisplay(),
255, 255, 255);
rt.setBackground(color);
rt.setText("Username is invalid");
System.out.println(rt.getText());
}
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
You have to first invoke login dialog using some action assigned to it. Then you can perform different operations like setting text in userName and password text boxes, clicking OK button etc using SWTWorkbenchBot methods.
If you are trying to test login controls on splash screen, then it will not be posible using SWTBot. Because SplashHandler does not get loaded when called from SWTBot.
The only way to launch your application in that case is to bypass your splash screen programatically. It means you have to write a code to skip login dialog/splash login screen.

Access ViewController in DependencyService to present MFMailComposeViewController

How can i access the ViewController in my DependencyService to present a MFMailComposeViewController? I tried using Application.Context but this seems to be only working on Android. Any advice?
You can present a MFMailComposeViewController by doing a window.RootController.PresentViewController (mail controller, true, null);. Depending on your app architecture, the RootViewController might not be an usable ViewController in the hierarchy. In that case you get a
Warning: Attempt to present <MFMailComposeViewController: 0x16302c30> on <Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x14fd1530> whose view is not in the window hierarchy!
In that case, you have to dig for the concrete ViewController, in my case it is:
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
which is a bit wicked, but works (An issue for this have been filed for future fix).
The full solution then looks like:
in your AppDelegate.cs, add this:
public UIWindow Window {
get { return window; }
}
in your PCL project, declare the interface: ISendMailService.cs
public interface ISendMailService
{
void ComposeMail (string[] recipients, string subject, string messagebody = null, Action<bool> completed = null);
}
in your iOS project, implement and register the interface: SendMailService.cs
[assembly: DependencyAttribute(typeof(SendMailService))]
public class SendMailService : ISendMailService
{
public void ComposeMail (string[] recipients, string subject, string messagebody = null, Action<bool> completed = null)
{
var controller = new MFMailComposeViewController ();
controller.SetToRecipients (recipients);
controller.SetSubject (subject);
if (!string.IsNullOrEmpty (messagebody))
controller.SetMessageBody (messagebody, false);
controller.Finished += (object sender, MFComposeResultEventArgs e) => {
if (completed != null)
completed (e.Result == MFMailComposeResult.Sent);
e.Controller.DismissViewController (true, null);
};
//Adapt this to your app structure
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.ChildViewControllers[0].ChildViewControllers[1].ChildViewControllers[0];
var navcontroller = rootController as UINavigationController;
if (navcontroller != null)
rootController = navcontroller.VisibleViewController;
rootController.PresentViewController (controller, true, null);
}
}
And you can now consume it from your Xamarin.Forms PCL project:
new Button {
Font = Font.SystemFontOfSize (NamedSize.Medium),
Text = "Contact us",
TextColor = Color.White,
BackgroundColor = ColorsAndStyles.LightBlue,
BorderRadius = 0,
Command = new Command (()=>{
var mailservice = DependencyService.Get<ISendMailService> ();
if (mailservice == null)
return;
mailservice.ComposeMail (new [] {"foo#example.com"}, "Test", "Hello, World");
})
}
Use: UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);
I would like to add an additional answer based off of the KeyWindow not always being the main window. (this occurs when you are presenting your controller after the user has interacted with an action sheet or alert dialog)
public static UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
throw new InvalidOperationException("There's no current active window");
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
This will guarantee that you get the Xamarin Forms platform renderer window, then find the foremost presented ViewController and return it for use presenting whatever UI or view controller you need to present.
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(controller, true, null);
This only works in above all solutions
Just for a reference. It took me some time to figure it out how to launch it from modal window.
Here comes the solution:
var rootController = ((AppDelegate)(UIApplication.SharedApplication.Delegate)).Window.RootViewController.PresentedViewController;
var navcontroller = rootController as UINavigationController;
if (navcontroller != null)
rootController = navcontroller.VisibleViewController;
rootController.PresentViewController (controller, true, null);

Resources