Enable/Disable FINISH button of wizard based on user input - axapta

I have created a wizard in AX 2012 using wizard wizard... Now i need to put 1 functionality i.e., to Enable or Disable FINISH button based on user input.
I have already tried these 3 ways but without success..
this.finishenabled() -- on SetupNavigation method of wizard class
finishenabled[formrun.tabidx()] = false -- on SetupNavigation method of wizard class
syswizard.finishenable(false, curtabidx(),false) - on Tabpage of wizard form
please do reply if anyone have a solution for this....

The Wizard class has a validate method in which you will do the following:
boolean validate()
{
if(SomeTestCondition)
{
return true;
}
return false;
}
According to Microsoft, this method does the following:
Used to validate user input, and called before the wizard is closed.
It returns false if user input is invalid. This will prevent the run method from being called when the user clicks the Finish button.
Wizard Class on MSDN
Additionally, you can use the textchanged() method on the field you want to validate (or if not text, you can use the changed method of the object).
if (this.text())
{
if (!sysWizard.isNextEnabled())
{
sysWizard.nextEnabled(true, sysWizard.curTab(), false);
}
}
else
{
if (sysWizard.isNextEnabled())
sysWizard.nextEnabled(false, sysWizard.curTab(), false);
}
Also from MSDN Enable Buttons

In SysWizard class the check to enable / disable the finishButton is inside a check for this.hasFinishButton() (see SysWizard.enableButtons).
I overcame this issue by overwriting the hasFinishButton() method on your wizard class and set the ret = true. This does mean however that your finish buttons will show in all steps, but you can hide this with other code if necessary.

The simplest way to enable/disable the Finish button on a Wizard form called from a SysWizard class is to retrieve the FormControl object from the FormRun object using the FormControlId and then set the Enabled property based on the your test condition, such as whether another FormControl contains a value. There are many ways to implement this. I'll provide two examples.
In the first example, all of the modifications are done on the Wizard Form.
A FormControl is used that can be called like any FormControl that has the AutoDeclaration property set to Yes.
In the second example, I'll override the finishEnabled() method on my Wizard class, so it behaves in the manner that was expected.
In each example, the formControl is found using the FormControlId which takes the control's label text ("Finish") as the argument. I found the correct Label ID by doing a "Lookup Label/Text" on "Finish" in the code editor and then selected the SYS label with "Label for Finish button in wizard" in the label's Description.
Example 1: FormControl object on Wizard Form:
In the Form classDeclaration add the following:
class FormRun extends ObjectRun
{
//FormControl objects used to get SysWizard Finish Button
FormControlId finishButtonId;
FormControl finishButton;
}
Initialize the new FormControl in the top level Form init() method:
void init()
{
super();
if (element.Args().caller())
{
sysWizard = element.Args().caller();
}
finishButtonId = sysWizard.formRun().controlId("#SYS302811");
finishButton = sysWizard.formRun().control(finishButtonId);
finishButton.enabled(false);
}
Now you can use the control like you would any other form control. In this case, I'm using the state of checkbox control named IsFinished in my WizardForm as the test condition and updating the FormControl state from the IsFinished.clicked() method:
public void clicked()
{
super();
//set FormControl state based on the current value of the checkbox
finishButton.enabled(this.checked());
}
*Example 2:*Override the finishEnabled() method in your Wizard class:
Note that you'll need to set the default values for the method parameters otherwise AX will throw a compile error because it doesn't match the signature from the base class. For some reason, AX doesn't properly create the method signature. Get rid of the default call to super and replace it with the code below:
public boolean finishEnabled(boolean _enabled = false,
int _idx = this.curTab(),
boolean _setfocus = false)
{
return this.formRun().control(this.formRun().controlId("#SYS302811")).enabled(_enabled);
}
Initialize the control value in the Form init() method:
void init()
{
super();
if (element.Args().caller())
{
sysWizard = element.Args().caller();
}
sysWizard.finishEnabled();
}
Call the class method when your controls are updated:
public void clicked()
{
super();
//set FormControl state based on the current value of the checkbox
sysWizard.finishEnabled(this.checked());
}

Related

dialogPostRun method in RunBaseReport

I have a RunBaseReport which contains overrided dialog method where I'm adding couple of my controls. One of those controls is a combobox.
Controls enabled() property should be changed when I'm modifying combobox.
So basically I need to know when the value of my dfReportType dialog field changes.
public Object dialog(Object dialog)
{
DialogRunbase dialog = dialog;
;
//adding my combobox
dfReportType = dialog.addFieldValue(typeid(ReportType), ReportType:DefaultType);
//adding some other controls here
return dialog;
}
According to many articles that I found I need to override dialogPostRun Method and do something like this:
public void dialogPostRun(DialogRunbase dialog)
{
super(dialog);
dialog.dialogForm().formRun().controlMethodOverload(true);
dialog.dialogForm().formRun().controlMethodOverloadObject(this);
}
But unfortunately I don't have this method in RunBaseReport class.
Which should be there according to msdn .
Are there any other workarounds?
I'm currently on AX 2012 but I still looked at it. I have the method available in the context menu, but not on the first column. I have to go over "Plus..." to find the method in the second column.
Well, there is no dialogPostRun method in Report object that inherits RunBaseReport, but we have this method in Class that inherits RunBaseReport.
So that was my mistake. I used report object instead of class.
If you want to make custom dialog for the report but you also want to use all default controls you should:
Create class
Inherit RunBaseReport
Override dialog, getFromDialog etc.
Override lastValueElementName method
public identifiername lastValueElementName()
{
//just put name of your report object
return reportStr(YourReportName);
}
Don't forget to add main() method if you going to make call from menuItem.

How to activate another button using click event of certain button in MS Dynamics AX?

I'm creating a new form in Axapta.
How can I make a Show-->Line View to be activated by clicking on New--> Item?
Should I do this by X++ override methods or it is possible not to use code?
I think it can only be done with code. Override your datasource's create() method and then call the button clicked() method. Note that to call directly the button's Auto declaration propery need to be set to Yes.
For instance in a SalesTable form you could overwrite SalesTable.create() method :
void create(boolean append = true)
{
SalesTable newSalesTable;
EditDetailsButton.clicked();
// rest code goes here
}

How to select an item of mx:ComboBox on the basis of substring entered through keyboard

I am using <mx:ComboBox /> and I want to select a matching item on the basis of string entered through keyboard. Currently, <mx:ComboBox /> selects the first matching item based on the first character only. I want this functionality to be customized. I am unable to find that KeyboardEvent listener which does the matching so that I can override it.
To do this yourself, you should look at the following bits and pieces of code below from the ComboBox and ListBase classes. ListBase is what the ComboBox component uses for it's drop down list.
The ComboBox appears to be deferring the keyboard input to the drop down list. It then listens for events from the drop down list to know when the selection has changed (as a result of keyboard or mouse input).
Flex components usually override a method called keyDownHandler() to process the keyboard input when they have focus. Starting there, we come across ComboBox line 2231:
// Redispatch the event to the dropdown
// and let its keyDownHandler() handle it.
dropdown.dispatchEvent(event.clone());
event.stopPropagation();
So now the keyDownHandler() in the drop down list will get executed. That method has a giant switch statement, where the default case statement on line 9197 of ListBase looks like this:
default:
{
if (findKey(event.charCode))
event.stopPropagation();
}
This is where the drop down list decides what to select based on keyboard input (when the input is not an arrow key or page up, etc.). The protected findKey() method simply calls the public findString() method to do this work.
So to override this behavior yourself:
extend the ListBase class and override the findKey() or findString() methods with your custom logic
extend ComboBox class and override the createChildren() method so you can instantiate your custom ListBase class instead of the default one.
Here is the class which I've used in order to make it work. searchStr is user inputted string which needs to be matched. If no dataprovider item gets matched to the searchStr, the overridden listener falls back to the default behaviour. I am using Timer to flush the inputted searchStr after 2 seconds. The possible drawback is that it is assuming the dataprovider to be a collection of String values. But you can modify it accordingly as the need may be.
public class CustomComboBox extends ComboBox
{
private var searchStr:String="";
private var ticker:Timer;
public function CustomComboBox()
{
super();
ticker = new Timer(2000);
ticker.addEventListener(TimerEvent.TIMER, resetSearchString);
}
override protected function keyDownHandler(event:KeyboardEvent):void
{
super.keyDownHandler(event);
// code to search items in the list based on user input.
// Earlier, the default behavior shows the matched items in the dropdown, based on first character only.
// user input is invisible to user.
if((event.charCode>=0x20 && event.charCode<=0x7E) || event.charCode==8) //Range of printable characters is 0x20[space] to 0x7E[~] in ASCII. 8 is ASCII code of [backspace].
{
ticker.reset();
ticker.start();
if(event.charCode==8)
{
if(searchStr=="")
return;
searchStr = searchStr.substr(0, searchStr.length-1);
}
else
{
searchStr += String.fromCharCode(event.charCode);
searchStr = searchStr.toLowerCase();
}
for each(var str:String in dataProvider)
{
if(str.toLowerCase().indexOf(searchStr, 0)>-1)
{
this.selectedItem = dropdown.selectedItem = str;
dropdown.scrollToIndex(dropdown.selectedIndex);
break;
}
}
}
}
/**
* reset the search string and reset the timer.
**/
private function resetSearchString(evt:TimerEvent):void
{
searchStr = "";
ticker.reset();
}
}

Extending DropDownList to include an extra option

I want to extend DropDownList control to include an option for creating or editing the options. For example; for a list of projects in the dropdown list, there will be another option that says "Create new project..." or "Edit projects..." and this will be the last option in the list. When user selects this option, the selectedIndex or selectedItem will not change and corresponding action will be taken (for example a popup window shows up). This will be a convenient way for the end user.
Now I want this to work independent of the context and the class must be reusable. User will only specify the optionText and optionFunction to work this out. The basic structure of the class looks like this:
public class OptiveDropDownList extends DropDownList
{
private var _enableOption:Boolean;
private var _optionText:String;
private var _originalDataProvider:IList;
[Bindable] public var optionFunction:Function;
public function OptiveDropDownList()
{
super();
}
public function set optionText(value:String):void
{
_optionText = value;
dataProvider = _originalDataProvider;
}
public function set enableOption(value:Boolean):void
{
_enableOption = value;
dataProvider = _originalDataProvider;
}
public override function set dataProvider(value:IList):void
{
_originalDataProvider = value;
var dp:IList = null;
if(!value){
dp=new ArrayCollection(value.toArray());
if(_enableOption){
var opt:Object=new Object();
opt[labelField]=_optionText;
dp.addItem(opt);
}
}
super.dataProvider = dp;
}
[Bindable]
public override function get dataProvider():IList
{
return _originalDataProvider;
}
}
I hope my code is clear to understand, I am adding an extra object to the dataprovider for the option. Field names are self-explanatory.
Now my question is how to know whether the dataprovider's items have changed? Which functions should I override and how to do it. I have tried using a ChangeWatcher to watch the length property of the dataprovider, but it doesnt work if only an object in the dataprovider has changed. I need to capture these changes and update the view.
I also need to capture the selection and call optionFunction, preventing the default action not to give index out of bounds error.
Thanks in advance.
Just add an event listener to the original dataProvider. All implementations of IList should dispatch CollectionEvent.COLLECTION_CHANGE when the the list changes (e.g. add, remove or when an existing object in the list has been changed). In your event handler you can update the DropDownList's dataProvider accordingly.
By overriding the mx_internal method setSelectedIndex() you can adjust the selection according to your wishes. Take a look at the blog post "Disable selection on some items in a spark List" for some inspiration.

How can I trigger an itemEditEnd Event in a Flex List when the CheckBox is checked/unchecked?

I have a List component that has drop-in CheckBox itemEditor that also serves as the itemRenderer. It displays each item as a simple CheckBox with a label.
However, the itemEditEnd Event does not get triggered until I click on something outside of the List. I want it triggered once the CheckBox is checked or unchecked.
I was thinking of manually dispatching the ListEvent.ITEM_EDIT_END in a CLICK Event handler, but then the itemEditEnd Event would get dispatched twice. There's gotta be a better way to do this.
Any ideas?
Thanks.
Here is the solution I came up with. I changed my List to use the component as an itemRenderer only, not as a itemRenderer and itemEditor. I then added a MouseEvent.CLICK handler to call a function in the List from the itemRenderer to perform the necessary actions:
My List Component:
package
{
import mx.controls.List;
import mx.core.ClassFactory;
public class CustomCheckBoxList extends List
{
public function CustomCheckBoxList()
{
super();
itemRenderer = new ClassFactory(CheckBoxRenderer);
}
public function dispatchSelectionEvent(item:Object, selected:Boolean):void
{
// Take action here...
}
}
}
My ItemRenderer:
package
{
import flash.events.MouseEvent;
import mx.controls.CheckBox;
public class CheckBoxRenderer extends CheckBox
{
public function CheckBoxRenderer()
{
super();
}
override protected function clickHandler(event:MouseEvent):void
{
super.clickHandler(event);
CustomCheckBoxList(listData.owner).dispatchSelectionEvent(data, selected);
}
}
}
I've just run into this. I'm using a custom component rather than the drop-in approach, and this works when using the renderer as the editor.
Note that the Flex folks evidently came up with the notion that users would want to toggle their checkboxes a few times before settling on the state to commit to...at which point they'd hit the Enter key. How obvious!
My solution is to synthesize a keyboard event that is equivalent to hitting Enter. The tricky part is that one must use the callLater() method to dispatch the event because the list control won't have registered its keyboard listener on the editor until after the checkbox's click handler gets called. Here's my click handler for the checkbox in my custom renderer/editor component:
private function onClick(value:Object):void {
newValue = value;
var list:ListBase = ListBase(owner);
list.callLater(dispatchEvent, [new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, Keyboard.ENTER, Keyboard.ENTER)]);
}

Resources