IllegalArgumentException: No view found for id for fragment when fast switching ActionBar Tabs - android-3.0-honeycomb

I am developing an Android app for tablets and not using the compatibility library.
There is just one Activity and it uses the ActionBar with 3 tabs.
In the TabListener I use setContentView to load the layout specific to that tab, and then add the relevant fragments to their frames.
This almost works exactly like I want, except when you switch between the tabs fast enough the app will crash.
I am using a Samsung Galaxy Tab as my debugging device and switching tabs is really fast. At a normal pace I can tap back and forth between them and the pages are loaded instantly. The problem is when I hyper switch between the tabs.
At first I got an
IllegalStateException: Fragment not added
as seen here:
http://code.google.com/p/android/issues/detail?id=17029
Following the suggestion to use try/catch blocks in onTabUnselected, I made the app a little more robust, but that lead to the issue at hand:
IllegalArgumentException: No view found for id 0x... for fragment ...
I have not found any other case on the web of anyone else having the same issue, so I am concerned that I may be doing something that's not supported. Again, what I am trying to do is use 3 different layouts in one Activity - when you click on a tab, the listener will call setContentView to change the layout, and then add the fragments. It works beautifully unless you start aggressively switching between tabs.
I got the idea for this from: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html
and instead of the TabListener keeping a reference to one fragment, I have an array of them. Also, I am not using attach/detach since those were just added in API 13.
My theory is that either setContentView hasn't finished creating the views, and that's why FragmentTransaction can't add them, OR the fragments are being added for one tab when another tab is selected and setContentView is called, destroying the other set of views.
I tried to hack something in to slow down tab switching but didn't get anywhere.
Here is the code for my TabListener:
private class BTabListener<T extends Fragment> implements ActionBar.TabListener{
private int mLayout;
private Fragment[] mFrags;
private TabData mTabData;
private Activity mAct;
private boolean mNoNewFrags;
public BTabListener(Activity act, int layout, TabData td, boolean frags){
mLayout = layout;
mTabData = td;
mAct = act;
mNoNewFrags = frags;
mFrags = new Fragment[mTabData.fragTags.length];
for(int i=0; i<mFrags.length; i++){
//on an orientation change, this will find the fragments that were recreated by the system
mFrags[i] = mAct.getFragmentManager().findFragmentByTag(mTabData.fragTags[i]);
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
//this gets called _after_ unselected
//note: unselected wont have been called after an orientation change!
//we also need to watch out because tab 0 always gets selected when adding the tabs
//set the view for this tab
mAct.setContentView(mLayout);
for(int i=0; i<mFrags.length; i++){
//this will be null when the tab is first selected
if(mFrags[i]==null ){
mFrags[i] = Fragment.instantiate(GUITablet.this, mTabData.classes[i].getName());
}
//if there was an orientation change when we were on this page, the fragment is already added
if(!mNoNewFrags || mDefaultTab!=tab.getPosition()){
ft.add(mTabData.containterIDs[i], mFrags[i], mTabData.fragTags[i]);
}
}
mNoNewFrags = false;
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// this gets called when another tab is selected, before it's onSelected method
for(Fragment f : mFrags){
try{ //extra safety measure
ft.remove(f);
}catch(Exception e){
e.printStackTrace();
System.out.println("unselect couldnt remove");
}
}
}
}
And finally, the stack trace:
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): java.lang.IllegalArgumentException: No view found for id 0x7f0b0078 for fragment Fraggle{40ab2230 #2 id=0x7f0b0078 dummy2}
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:729)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:926)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.BackStackRecord.run(BackStackRecord.java:578)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1226)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl$1.run(FragmentManager.java:374)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Handler.handleCallback(Handler.java:587)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Handler.dispatchMessage(Handler.java:92)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Looper.loop(Looper.java:132)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.ActivityThread.main(ActivityThread.java:4028)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at java.lang.reflect.Method.invokeNative(Native Method)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at java.lang.reflect.Method.invoke(Method.java:491)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at dalvik.system.NativeStart.main(Native Method)
THANK YOU!!

Okay, found a way around this:
Put the references to the fragments in the layout files, and surrounded the setContentView call in onTabSelected in a try/catch block.
The exception handling took care of it!

I know it's a slightly old question but I was having the same and find a different work around.
The method itself is described here Avoid recreating same view when perform tab switching
but on the context of this specific crash, doing Show/Hide instead of add/replace avoids multiple fast calls to onCreateView on the fragment.
My final code ended up something like this:
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
if (fragments[tab.getPosition()] == null) {
switch(tag.getPosition()){
case 0: fragments[tab.getPosition()] = new // fragment for this position
break;
// repeat for all the tabs, for each `case`
}
fragmentTransaction.add(R.id.container, fragments[tab.getPosition()]);
}else{
fragmentTransaction.show(fragments[tab.getPosition()]);
}
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
if (fragments[tab.getPosition()] != null)
fragmentTransaction.hide(fragments[tab.getPosition()]);
}

Seeing a similar case in a Google Play crash report.
java.lang.IllegalStateException:
Fragment not added: MessageDisplayFragment{406e28f0 #2 id=0x7f0b0020}
at android.app.BackStackRecord.hide(BackStackRecord.java:397)
at org.kman.AquaMail.ui.AccountListActivity$UIMediator_API11_TwoPane.
closeMessageDisplay(AccountListActivity.java:2585)
Relevant code:
AbsMessageFragment fragDisplay = (AbsMessageFragment)
mFM.findFragmentById(R.id.fragment_id_message_display);
if (fragDisplay != null) {
FragmentTransaction ft = mFM.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_NONE);
ft.show(some other fragment);
ft.hide(fragDisplay);
^^ This is where it crashes
The fragment is definitely there (it was returned by findFragmentById), but calling hide() blew up with the "IllegalStateException: Fragment not added"
Not added? How's it not added if findFragmentById was able to find it?
All FragmentManager calls are being made from the UI thread.
The fragment being removed (fragDisplay) was added earlier and I'm sure that its FragmentTransaction was commited.

Fixed it by looping through my fragments, removing any of them which have been added and then adding the new one. Also check first that you're not trying to replace it with null or an existing fragment.
Edit: looks like it was just
getChildFragmentManager().executePendingTransactions();
that stopped it from crashing
private void replaceCurrentTabFragment(TabFragment tabFragment){
if (tabFragment == null){ return; }
if (tabFragment == getActiveTabFragment()){ return; }
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
for (TabFragment fragment : mTabButtons.values()){
if (((Fragment)fragment).isAdded()){
ft.remove((Fragment)fragment);
}
}
ft.add(R.id.fragment_frameLayout, (Fragment) tabFragment);
ft.commit();
getChildFragmentManager().executePendingTransactions();
}
private TabFragment getActiveTabFragment(){
return (TabFragment) getChildFragmentManager().findFragmentById(R.id.fragment_frameLayout);
}

Related

Data Fetching Crashes in Xamarin Forms

I am trying to fetch Customer data to parse them into customer object to display on TableView. The following code sometimes works, sometimes not. Whenever it does crash, it shows Customer data is empty in the foreach loop even though I run the same code every time. I do not have clue what could be wrong in this circumstances. I am quite new on this platform. If I am missing anything/ extra information, please let me know.
namespace TableViewExample
{
public partial class MyDataServices : ContentPage
{
private ODataClient mODataClient;
private IEnumerable <IDictionary<string,object>> Customers;
public MyDataServices ()
{
InitializeComponent ();
InitializeDataService ();
GetDataFromOdataService ();
TableView tableView = new TableView{ };
var section = new TableSection ("Customer");
foreach (var customers in Customers) {
//System.Diagnostics.Debug.WriteLine ((string)customers ["ContactName"]);
var name = (string)customers ["ContactName"];
var cell = new TextCell{ Text = name };
section.Add (cell);
}
tableView.Root.Add (section);
Padding = new Thickness (10, 20, 10, 10);
Content = new StackLayout () {
Children = { tableView }
};
}
private void InitializeDataService(){
try {
mODataClient = new ODataClient ("myURL is here");
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
private void GetDataFromOdataService (){
try {
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
}
}
Its hard helping out here, however here are some things to consider:-
It sounds like the dataservice could either be not contactable / offline; too busy or it could even be throwing an exception itself and returning a data response that you are not expecting to receive, that then triggers an exception and crash in your application as your always expecting an exact response without catering for any abnormal responses / events.
If you are contacting an external service over the internet it may just be your internet connection is slow / faulty and not returning the information fast enough as other possibilities.
In your code you are assuming that you always get a response from the server - and that this response will always be of an anticipated structure that your expecting to decode - without factoring in any possibility of abnormal responses returned by the dataservice. I have not used ODataClient personally, so not sure how it behaves in the event of maybe no data received / timeout or in your case the dataservice and how it behaves internally in the response to a bad-request etc.
I am assuming an exception would get thrown, and you do get your debug line executed indicating a failure.
You may want to also adjust this statement so that you write out the exception as well, i.e.:-
private void GetDataFromOdataService ()
{
try
{
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR!" + ex.ToString());
}
}
If there was a bad response, then the line at Customers = ..... would throw the exception as there may be no Customers returned or some other information packaged in the response from the dataservice.
The Customers variable would also be null at this point I am assuming due to this failing.
So when you get back to your code at foreach (var customers in Customers) { it will then throw a null reference exception as Customers is infact null.
As all your current code executes in the constructor without any try and catch block around this, it will also crash your application at this point as well.
Also you are doing all of this work in the constructor. Try seperating this out. I haven't investigated exactly where the constructor gets called in an iOS page life-cycle, however, if it is in the viewDidLoad, then you have something like 10 seconds for everything to complete, otherwise it will exit automatically. I imagine in your case, this isn't applicable however.
Going forward also try putting your layout controls in the constructor, and move your data task to maybe the OnAppearing override instead.
Using async would definitely be advisable as well, but remember you need to inspect the response from your dataservice, as the error could be embedded within the response also and you will need to detect when it is OK to process the data.

JavaFX: Prevent selection of a different tab if the data validation of the selected tab fails

I'm creating a CRUD application that store data in a local h2 DB. I'm pretty new to JavaFX. I've created a TabPane to with 3 Tab using an jfxml created with Scene Builder 2.0. Each Tab contains an AncorPane that wrap all the controls: Label, EditText, and more. Both the TabPane and the Tabs are managed using one controller. This function is used to create and to update the data. It's called from a grid that display all the data. A pretty basic CRUD app.
I'm stuck in the validation phase: when the user change the tab, by selecting another tab, it's called a validation method of the corresponding tab. If the validation of the Tab fails, I want that the selection remains on this tab.
To achieve this I've implemented the following ChangeListener on the SelectionModel of my TabPane:
boolean processingTabValidationOnChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (processingTabValidationOnChange == false) {
boolean success;
switch (t.intValue()) {
case 0: success = validationTab1Passed();
break;
case 1: success = validationTab2Passed();
break;
case 1: success = validationTab3Passed();
break;
default: success = false;
}
if (success == false) {
processingTabValidationOnChange = true;
// select the previous tab
tabPane.getSelectionModel().select(t.intValue());
processingTabValidationOnChange = false;
}
}
}
});
I'm not sure that this is the right approach because:
The event changed is fired two times, one for the user selection and one for the .select(t.intValue()). To avoid this I've used a global field boolean processingTabValidationOnChange... pretty dirty I know.
After the .select(t.intValue()) the TabPane displays the correctly Tab as selected but the content of the tab is empty as if the AnchorPane was hidden. I cannot select again the tab that contains the errors because it's already selected.
Any help would be appreciated.
Elvis
I would approach this very differently. Instead of waiting for the user to select a different tab, and reverting if the contents of the current tab are invalid, prevent the user from changing tabs in the first place.
The Tab class has a disableProperty. If it is set to true, the tab cannot be selected.
Define a BooleanProperty or BooleanBinding representing whether or not the data in the first tab is invalid. You can create such bindings based on the state of the controls in the tab. Then bind the second tab's disableProperty to it. That way the second tab automatically becomes disabled or enabled as the data in the first tab becomes valid or invalid.
You can extend this to as many tabs as you need, binding their properties as the logic dictates.
Here's a simple example.
Update: The example linked above is a bit less simple now. It will dynamically change the colors of the text fields depending on whether the field is valid or not, with validation rules defined by bindings in the controller. Additionally, there are titled panes at the top of each page, with a title showing the number of validation errors on the page, and a list of messages when the titled pane is expanded. All this is dynamically bound to the values in the controls, so it gives constant, clear, yet unobtrusive feedback to the user.
As I commented to the James's answer, I was looking for a clean solution to the approach that I've asked. In short, to prevent the user to change to a different tab when the validation of the current tab fails. I proposed a solution implementing the ChangeListener but, as I explained: it's not very "clean" and (small detail) it doesn't work!
Ok, the problem was that the code used to switch back the previous tab:
tabPane.getSelectionModel().select(t.intValue());
is called before the process of switching of the tab itself it's completed, so it ends up selected... but hidden.
To prevent this I've used Platform.runLater(). The code .select() is executed after the change of tab. The full code becomes:
//global field, to prevent validation on .select(t.intValue());
boolean skipValidationOnTabChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (skipValidationOnTabChange == false) {
boolean success;
switch (t.intValue()) {
case 0:
success = validationTab1Passed();
break;
case 1:
success = validationTab2Passed();
break;
case 1:
success = validationTab3Passed();
break;
default:
success = false;
}
if (success == false) {
Platform.runLater(new Runnable() {
#Override
public void run() {
skipValidationOnTabChange = true;
tabPane.getSelectionModel().select(t.intValue());
skipValidationOnTabChange = false;
}
});
}
}
}
});
Anyway, if anyone has a better solution to accomplish this, you're welcome. In example using a method like consume() to prevent the tab to be selected two times. This way I can eliminated the global field skipValidationOnTabChange.
Elvis
I needed to achieve the similar thing. I've done this by changing the com.sun.javafx.scene.control.behavior.TabPaneBehaviour class by overriding selectTab method:
class ValidatingTabPaneBehavior extends TabPaneBehavior {
//constructors etc...
#Override
public void selectTab(Tab tab) {
try {
Tab current = getControl().getSelectionModel().getSelectedItem();
if (current instanceof ValidatingTab) {
((ValidatingTab) current).validate();
}
//this is the method we want to prevent from running in case of error in validation
super.selectTab(tab);
}catch (ValidationException ex) {
//show alert or do nothing tab won't be changed
}
}
});
The ValidatingTab is my own extension to Tab:
public class ValidatingTab extends Tab {
public void validate() throws ValidationException {
//validation
}
}
This is the "clean part" of the trick. Now we need to place ValidatingTabPaneBehavior into TabPane.
First you need to copy (!) the whole com.sun.javafx.scene.control.skin.TabPaneSkin to the new class in order to change its constructor. It is quite long class, so here is only the part when I switch the Behavior class:
public class ValidationTabPaneSkin extends BehaviorSkinBase<TabPane, TabPaneBehavior> {
//copied private fields
public ValidationTabPaneSkin(TabPane tabPane) {
super(tabPane, new ValidationTabPaneBehavior(tabPane));
//the rest of the copied constructor
}
The last thing is to change the skin in your tabPane instance:
tabPane.setSkin(new ValidationTabPaneSkin(tabPane));

SaveAs in COM hanging AutoCAD

I'm implementing an application which uses COM in AutoCAD's ObjectARX interface to automate drawing actions, such as open and save as.
According to the documentation, I should be able to call AcadDocument.SaveAs() and pass in a filename, a "save as type" and a security parameter. The documentation explicitly statses that if security is NULL, no security related operation is attempted. It doesn't, however, give any indication of the correct object type to pass as the "save as type" parameter.
I've tried calling SaveAs with a filename and null for the remaining arguments, but my application hangs on that method call and AutoCAD appears to crash - you can still use the ribbon but can't do anything with the toolbar and can't close AutoCAD.
I've got a feeling that it's my NULL parameters causing grief here, but the documentation is severely lacking in the COM/VBA department. In fact it says the AcadDocument class doesn't even have a SaveAs method, which it clearly does.
Has anyone here implemented the same thing? Any guidance?
The alternative is I use the SendCommand() method to send a _SAVEAS command, but my application is managing a batch of drawing and needs to know a) if the save fails, and b) when the save completes (which I'm doing by listening to the EndSave event.)
EDIT
Here's the code as requested - all it's doing is launching AutoCAD (or connecting to the running instance if it's already running), opening an existing drawing, then saving the document to a new location (C:\Scratch\Document01B.dwg.)
using (AutoCad cad = AutoCad.Instance)
{
// Launch AutoCAD
cad.Launch();
// Open drawing
cad.OpenDrawing(#"C:\Scratch\Drawing01.dwg");
// Save it
cad.SaveAs(#"C:\Scratch\Drawing01B.dwg");
}
Then in my AutoCad class (this._acadDocument is an instance of the AcadDocument class.)
public void Launch()
{
this._acadApplication = null;
const string ProgramId = "AutoCAD.Application.18";
try
{
// Connect to a running instance
this._acadApplication = (AcadApplication)Marshal.GetActiveObject(ProgramId);
}
catch (COMException)
{
/* No instance running, launch one */
try
{
this._acadApplication = (AcadApplication)Activator.CreateInstance(
Type.GetTypeFromProgID(ProgramId),
true);
}
catch (COMException exception)
{
// Failed - is AutoCAD installed?
throw new AutoCadNotFoundException(exception);
}
}
/* Listen for the events we need and make the application visible */
this._acadApplication.BeginOpen += this.OnAcadBeginOpen;
this._acadApplication.BeginSave += this.OnAcadBeginSave;
this._acadApplication.EndOpen += this.OnAcadEndOpen;
this._acadApplication.EndSave += this.OnAcadEndSave;
#if DEBUG
this._acadApplication.Visible = true;
#else
this._acadApplication.Visible = false;
#endif
// Get the active document
this._acadDocument = this._acadApplication.ActiveDocument;
}
public void OpenDrawing(string path)
{
// Request AutoCAD to open the document
this._acadApplication.Documents.Open(path, false, null);
// Update our reference to the new document
this._acadDocument = this._acadApplication.ActiveDocument;
}
public void SaveAs(string fullPath)
{
this._acadDocument.SaveAs(fullPath, null, null);
}
From the Autodesk discussion groups, it looks like the second parameter is the type to save as, and may be required:
app = new AcadApplicationClass();
AcadDocument doc = app.ActiveDocument;
doc.SaveAs("d:\Sam.dwg",AcSaveAsType.acR15_dwg,new Autodesk.AutoCAD.DatabaseServices.SecurityParameters());
Since you are in AutoCAD 2010, the type should be incremented to acR17_dwg or acR18_dwg.
Judging by the link to AutoDesk's forum on this topic, it sounds like as you need to close the object after saving...and remove the null's...If I were you, I'd wrap up the code into try/catch blocks to check and make sure there's no exception being thrown!
I have to question the usage of the using clause, as you're Launching another copy aren't you? i.e. within the OpenDrawing and Save functions you are using this._acadApplication or have I misunderstood?
using (AutoCad cad = AutoCad.Instance)
{
try{
// Launch AutoCAD
cad.Launch();
// Open drawing
cad.OpenDrawing(#"C:\Scratch\Drawing01.dwg");
// Save it
cad.SaveAs(#"C:\Scratch\Drawing01B.dwg");
}catch(COMException ex){
// Handle the exception here
}
}
public void Launch()
{
this._acadApplication = null;
const string ProgramId = "AutoCAD.Application.18";
try
{
// Connect to a running instance
this._acadApplication = (AcadApplication)Marshal.GetActiveObject(ProgramId);
}
catch (COMException)
{
/* No instance running, launch one */
try
{
this._acadApplication = (AcadApplication)Activator.CreateInstance(
Type.GetTypeFromProgID(ProgramId),
true);
}
catch (COMException exception)
{
// Failed - is AutoCAD installed?
throw new AutoCadNotFoundException(exception);
}
}
/* Listen for the events we need and make the application visible */
this._acadApplication.BeginOpen += this.OnAcadBeginOpen;
this._acadApplication.BeginSave += this.OnAcadBeginSave;
this._acadApplication.EndOpen += this.OnAcadEndOpen;
this._acadApplication.EndSave += this.OnAcadEndSave;
#if DEBUG
this._acadApplication.Visible = true;
#else
this._acadApplication.Visible = false;
#endif
// Get the active document
// this._acadDocument = this._acadApplication.ActiveDocument;
// Comment ^^^ out? as you're instantiating an ActiveDocument below when opening the drawing?
}
public void OpenDrawing(string path)
{
try{
// Request AutoCAD to open the document
this._acadApplication.Documents.Open(path, false, null);
// Update our reference to the new document
this._acadDocument = this._acadApplication.ActiveDocument;
}catch(COMException ex){
// Handle the exception here
}
}
public void SaveAs(string fullPath)
{
try{
this._acadDocument.SaveAs(fullPath, null, null);
}catch(COMException ex){
// Handle the exception here
}finally{
this._acadDocument.Close();
}
}
Thought I'd include some links for your information.
'Closing Autocad gracefully'.
'Migrating AutoCAD COM to AutoCAD 2010'.
'Saving AutoCAD to another format'
Hope this helps,
Best regards,
Tom.
I've managed to solve this in a non-optimal, very imperfect way so I'd still be interested to hear if anyone knows why the SaveAs method crashes AutoCAD and hangs my application.
Here's how I did it:
When opening a document or creating a new one, turn off the open/save dialog boxes:
this._acadDocument.SetVariable("FILEDIA", 0);
When saving a document, issue the _SAVEAS command passing in "2010" as the format and the filename (fullPath):
string commandString = string.Format(
"(command \"_SAVEAS\" \"{0}\" \"{1}\") ",
"2010",
fullPath.Replace('\\', '/'));
this._acadDocument.SendCommand(commandString);
When exiting AutoCAD turn file dialog prompting back on (probably isn't necessary but just makes sure):
this._acadDocument.SetVariable("FILEDIA", 1);
With C# and COM, when there are optional arguments, you need to use Type.Missing instead of null:
this._acadDocument.SaveAs(fullPath, Type.Missing, Type.Missing);
But since Visual Studio 2010, you can simply omit the optional arguments:
this._acadDocument.SaveAs(fullPath);

Blackberry CheckboxField[] - FieldChangeListener - Stackoverflow error

I have an array of CheckboxField[] elements that I need to dynamically initialize. My sample code is -
class abc extends MainScreen implements FieldChangeListener {
CheckboxField[] boxField;
abc() {
.
.
.
boxField = new CheckboxField[length];
VerticalFieldManager vfm = new VerticalFieldManager();
for(int i=0; i<length; i++) {
boxField[i] = new CheckboxField(var[i], false);
boxField[i].setChangeListener(this);
vfm.add(boxField[i]);
}
add(vfm);
}
public void fieldChanged(Field field, int context) {
// The idea is to disable all the other checkboxes when one
// is clicked.
boxField[0].setChecked(false); // Gives stackoverflow error on JVM.
}
}
Any help?
Edit: The problem only seems to be with .setChecked(boolean)
I've tried chkboxField[0].setFont(), chkboxField.getChecked(), both of them seem to work.
So, what's apparently happening is boxField[i].setChecked(false) calls the FieldChangeListener again, and this loops infinitely till the stack blows.
I was told to use
if(context != FieldChangeListener.PROGRAMMATIC) {
boxField[i].setChecked(false);
}
Based on your comment in the FieldChanged method, it sounds like you have mutually exclusive checkboxes (that is, you have a group of checkboxes and when any one is checked, all the rest should be unchecked).
If so, you may want to consider using the RadioButtonField instead. You can stick your radio buttons into a RadioButtonGroup and then the BlackBerry will take care of unchecking for you.

How to navigate back to the previous screen in Blackberry?

In Blackberry I can navigate from one screen to the next screen, but I can't navigate back to the previous screen. Pressing the escape key in the emulator terminates the entire application. Is there any other key in the emulator to go to the previous screen or any code to navigate back? If you know please help me.
As Andrey said, there is a display stack, so if you push screens without popping them, they will stay in stack, so closing current screen, previous screen will be shown automattically, and if there is no prev. screen, application will close.
However it's not good to hold many screens in display stack, so you can implement kind of stack inside of screens, to handle navigation manually.
Abstract screen class for screen stack implementation:
public abstract class AScreen extends MainScreen {
Screen prevScreen = null;
void openScreen(AScreen nextScreen) {
nextScreen.prevScreen = this;
UiApplication.getUiApplication().popScreen(this);
UiApplication.getUiApplication().pushScreen(nextScreen);
}
void openPrevScreen() {
UiApplication.getUiApplication().popScreen(this);
if (null != prevScreen)
UiApplication.getUiApplication().pushScreen(prevScreen);
}
}
Sample first screen:
public class FirstScreen extends AScreen implements FieldChangeListener {
ButtonField mButton = null;
public FirstScreen() {
super();
mButton = new ButtonField("Go second screen", ButtonField.CONSUME_CLICK);
mButton.setChangeListener(this);
add(mButton);
}
public void fieldChanged(Field field, int context) {
if (mButton == field) {
openScreen(new SecondScreen());
}
}
}
Sample second screen:
public class SecondScreen extends AScreen implements FieldChangeListener {
ButtonField mButton = null;
public SecondScreen() {
super();
mButton = new ButtonField("Go first screen", ButtonField.CONSUME_CLICK);
mButton.setChangeListener(this);
add(mButton);
}
public void fieldChanged(Field field, int context) {
if (mButton == field) {
openPrevScreen();
}
}
public boolean onClose() {
openPrevScreen();
return true;
}
}
The BlackBerry maintains a stack of screens; the display stack.
Screens are popped onto the stack, and popped off the stack through the UiApplication in charge of them. Popping the last screen off the stack closes the application by default.
If you are running a UiApplication, named MyUiApplication, you can add the screen to the stack by calling pushScreen(new SomeScreen());
The screen, if derived from MainScreen, as most BlackBerry screens are, is created with the DEFAULT_CLOSE flag, meaning that the ESCAPE button on the BlackBerry will naturally close the screen, causing popScreen() to be called. You can, of course, call popScreen() following any keypress or trackwheel/trackball click. The screen can also call close() on itself, which has the same result; the screen is popped off the stack, returning the application to the previous screen, or terminating the application if the last screen is popped off the display stack.
If the application is not created as a UiApplication, or if the screen was initially pushed onto the display stack from a non-UI thread (such as a background thread), then one must make sure that the call to close the screen is also done from the UI thread. This can be done by making sure that the eventLock is taken on the Application class prior to performing any UI operation (one would typically call invokeLater as well, in this situation).
If the original screen was popped onto the stack as a global screen (modeless, on top of all other screens), then it must be popped off the stack using something like:
Ui.getUiEngine().dismissStatus(this);
In any case, overriding onClose() and close() of the derived Screen will allow you to trap the occurring exception for debugging and further analysis.

Resources