How to simple overwrite showContextMenu on all columns of a grid? - axapta

If I overwrite showContextMenu on a grid it only add additional menu to the pop up menu when I click on a grid but do not click on any column. One way is to overwrite showContextMenu on all columns in grid but this solution does not looks right. Is there some better way how to insertItem to showContextMenu on all columns of a grid?
I am using AX 2012.
Standard code works only if grid is clicked but not a single column of a grid.
int ret,ii;
int myMenu = 2;
PopupMenu popupMenu = PopupMenu::create(_menuHandle);
FormListItem item;
;
deleteAttachment = popupMenu.insertItem('My menu');
ret = super(_menuHandle);
if(ret == myMenu)
{
//My code
}
return ret;

You need to use method registerOverrideMethod.
You can test how it works as follows. E.g. you have a form with a grid (property Name = 'Grid', AutoDeclaration = 'Yes'), and there are a few StringEdit controls in that grid.
1) Create following method in your form:
public void formControlContext(FormStringControl _formStringControl)
{
_formStringControl.context();
info(strFmt(#"Overridden context of control '%1'", _formStringControl.name()));
}
2) Override method init in your form:
public void init()
{
FormStringControl fsc;
int controlNum;
super();
for (controlNum = 1; controlNum <= Grid.controlCount(); controlNum++)
{
fsc = Grid.controlNum(controlNum);
fsc.registerOverrideMethod(methodStr(FormStringControl, context), identifierStr(formControlContext));
}
}
You only need to modify method 'formControlContext' according to your needs.
P.S. I just noticed you need to override method showContextMenu anot not context. The idea is the same - use registerOverrideMethod. You can override any method this way.

I've never played with overwriting the ShowContextMenu, but I did just make a blog post about how to recursively loop over every form control that sounds like it might help you.
http://alexondax.blogspot.com/2014/05/how-to-use-recursion-to-loop-over-form.html
I'd imagine you could create some sort of handler and key/value thing if you're creative.

Related

Xamarin Forms Grid conditional RowSpan

I have a generic Grid List Control. Which let me Bind the Item Source and ItemTemplate and it will generates the Column and Rows based on Number of Items or Based on how Users set the Rows and Columns. Its perfect until here. It supports all the generics I needed.
Now the Problem part:
I have a ContentView which will be used as Item Template for the Grid. This Template will be used for Multiple Data Types. I'm able to do it properly. Now I have one Data Type Where the First Cell should have RowSpan if it meets certain condition: The code below works perfect. It creates the RowSpan perfectly.
bool isYearBuilt = false;
bool isAny = false;
public FilterItemView()
{
InitializeComponent();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
var context = BindingContext as PropertyFilterItem;
if (context == null)
return;
if(context.FilterType==PropertyFilterTypes.YearBuilt)
{
if(context.IsAny)
{
isYearBuilt = true; isAny = true;
}
}
}
protected override void OnParentSet()
{
base.OnParentSet();
GridList grid = this.Parent as GridList;
if (grid == null)
return;
if(isYearBuilt && isAny)
{
Grid.SetRowSpan(this, 2);
}
}
These are the Images; which will describe it more perfectly:
This is how it looks based on above code. We can see that it is creating RowSpan.
This is what I'm expecting to do.
The only problem is - While Setting RowSpan it is not refreshing the subsequent cells. Because, I'm applying the RowSpan in Runtime after the Grid and all the Cells are created. How can I refresh the Grid and other remaining cells to moved to their own cells?
Thank you for helping me.
Set the grid list's ItemsSource = null and set it back again to the item source you want.
Cause of the problem:
As you have said, While Setting RowSpan it is not refreshing the subsequent cells.
Solution:
After you have set rowspan using Grid.SetRowSpan(this, 2); you have to bind the datasource again.
Since I had my own Custom Grid List control. I implemented more Properties which let me know When I have to RowSpan for Child View in Grid and Adjust subsequent Child items. That was the only best way to deal with this problem rather than re-assigning the ItemSource property. Because, If we assign item source it will redraw all the items (including the rows and columns - not row/colspans). So we would still end up with same issue.
Solution: Added RowSpan logic in my Grid Control.

Custom layout inside FragmentGridPagerAdapter

I am trying to add a custom layout page within cards.
Here: https://developer.android.com/training/wearables/ui/2d-picker.html
explains that CardFragment can easily add a card by:
CardFragment fragment = CardFragment.create(title, text, page.iconRes);
now if I decided I want to have a custom layout, how do I add it instead of creating a CardFragment?
Check this image:
The third page is a full screen custom layout. How can I achieve this?
FragmentGridPagerAdapter can actually support any subclass of Fragment that you need to use. CardFragment is just a convenience for a standard wearable layout.
So, you can just create a custom Fragment with a simple layout (such as a full-size ImageView) and return it for the appropriate page index.
On getFragment(int row, int col) method from your FragmentGridPagerAdapter class, you can create as many fragments as you want.
you just need to test your row/col values in order to instantiate your fragment class in the correct position.
In your case, something like this:
public Fragment getFragment(int row, int col) {
Fragment fragment;
if (row == 0 && col == 2) {
fragment = new YourFullScreenFragment();
} else {
fragment = CardFragment.create(title, text, page.iconRes);
}
return fragment;
}
Cheers
CardFrame is probably that one that you are looking for - it very similar to CardFragment but you can implement your own layout to it. Also as matiash pointed you can use any fragment that you like in such structure.

How to keep a list from scrolling on dataProvider refresh/update/change?

I have a simple list and a background refresh protocol.
When the list is scrolled down, the refresh scrolls it back to the top. I want to stop this.
I have tried catching the COLLECTION_CHANGE event and
validateNow(); // try to get the component to reset to the new data
list.ensureIndexIsVisible(previousIndex); // actually, I search for the previous data id in the IList, but that's not important
This fails because the list resets itself after the change (in DataGroup.commitProperties).
I hate to use a Timer, ENTER_FRAME, or callLater(), but I cannot seem to figure out a way.
The only other alternatives I can see is sub-classing the List so it can catch the dataProviderChanged event the DataGroup in the skin is throwing.
Any ideas?
Actually MUCH better solution to this is to extend DataGroup. You need to override this.
All the solutions here create a flicker as the scrollbar gets resetted to 0 and the it's set back to the previous value. That looks wrong. This solution works without any flicker and the best of all, you just change DataGroup to FixedDataGroup in your code and it works, no other changes in code are needed ;).
Enjoy guys.
public class FixedDataGroup extends spark.components.DataGroup
{
private var _dataProviderChanged:Boolean;
private var _lastScrollPosition:Number = 0;
public function FixedDataGroup()
{
super();
}
override public function set dataProvider(value:IList):void
{
if ( this.dataProvider != null && value != this.dataProvider )
{
dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
}
super.dataProvider = value;
if ( value != null )
{
value.addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
}
}
override protected function commitProperties():void
{
var lastScrollPosition:Number = _lastScrollPosition;
super.commitProperties();
if ( _dataProviderChanged )
{
verticalScrollPosition = lastScrollPosition;
}
}
private function onDataProviderChanged(e:CollectionEvent):void
{
_dataProviderChanged = true;
invalidateProperties();
}
override public function set verticalScrollPosition(value:Number):void
{
super.verticalScrollPosition = value;
_lastScrollPosition = value;
}
}
I ll try to explain my approach...If you are still unsure let me know and I ll give you the source code as well.
1) Create a variable to store the current scroll position of the viewport.
2) Add Event listener for Event.CHANGE and MouseEvent.MOUSE_WHEEL on the scroller and update the variable created in step 1 with the current scroll position;
3) Add a event listener on your viewport for FlexEvent.UpdateComplete and set the scroll position to the variable stored.
In a nutshell, what we are doing is to have the scroll position stored in variable every time user interacts with it and when our viewport is updated (due to dataprovider change) we just set the scroll position we have stored previously in the variable.
I have faced this problem before and solved it by using a data proxy pattern with a matcher. Write a matcher for your collection that supports your list by updating only changed objects and by updating only attributes for existing objects. The goal is to avoid creation of new objects when your data source refreshes.
When you have new data for the list (after a refresh), loop through your list of new data objects, copying attributes from these objects into the objects in the collection supporting your list. Typically you will match the objects based on id. Any objects in the new list that did not exist in the old one get added. Your scroll position will normally not change and any selections are usually kept.
Here is an example.
for each(newObject:Object in newArrayValues){
var found:Boolean = false;
for each(oldObject:Object in oldArrayValues){
if(oldObject.id == newObject.id){
found = true;
oldObject.myAttribute = newObject.myAttribute;
oldObject.myAttribute2 = newObject.myAttribute2;
}
}
if(!found){
oldArrayValues.addItem(newObject);
}
}
My solution for this problem was targeting a specific situation, but it has the advantage of being very simple so perhaps you can draw something that fits your needs from it. Since I don't know exactly what issue you're trying to solve I'll give you a description of mine:
I had a List that was progressively loading data from the server. When the user scrolled down and the next batch of items would be added to the dataprovider, the scrollposition would jump back to the start.
The solution for this was as simple as stopping the propagation of the COLLECTION_CHANGE event so that the List wouldn't catch it.
myDataProvider.addEventListener(
CollectionEvent.COLLECTION_CHANGE, preventRefresh
);
private function preventRefresh(event:CollectionEvent):void {
event.stopImmediatePropagation();
}
You have to know that this effectively prevents a redraw of the List component, hence any added items would not be shown. This was not an issue for me since the items would be added at the end of the List (outside the viewport) and when the user would scroll, the List would automatically be redrawn and the new items would be displayed. Perhaps in your situation you can force the redraw if need be.
When all items had been loaded I could then remove the event listener and return to the normal behavior of the List component.

Auto select the first index in a sparks List control in flex4

I have a spark List control. It has a dataProvider that shows reply from twitter search.
I have created a function for change handler like this:
protected function list_changeHandler(event:IndexChangeEvent):void
{
ta.text = coverflow.selectedItem.title;
}
So, whenever I select each of the items in the List, I will see the message(ta.text)
but now, instead of me manually clicking the first time, I want it to automatically click/select the first item and see the first message(ta.text)
How can I achieve this?
You should set the requireSelection property to true on the list control
<s:List id="myList" dataProvider="{myDataProvider}" requireSelection="true"/>
How about to try this solution? :)
Your list control also has event name creationComplete (similar to change event). Try to select your first item with this:
protected function list1_creationCompleteHandler(event:FlexEvent):void
{
if(event.target.dataProvider != null )
{
(event.target as List).selectedIndex = 0;
}
}
You may not need to convert event.target to List. but it may help you access code completion while you are coding.
Actually, in thinking about this, you probably need to subclass the list and override the dataProvider setter.
override public function set dataProvider(data:*) : void {
super._dataProvider = data;
// This will be an ArrayCollection or XMLListCollection, so will have a length
if (data && data.length > 0) {
this.selectedIndex = 0;
invalidateDisplayList();
}
}
Set the dataProvider of the list, set the selected item, and then either call your handler function directly with a null parameter, or make the list dispatch an indexChanged event so that your handler function gets executed.

How do you add a row listener to a Flextable in GWT?

How do you add a row listener to a specific row, or all rows in a table? I need to add a type of "onMouseOver" listener to the rows so that when you hover over them, it changes the background color of the row, much like getRowFormatter will allow you to do.
// this is click
final FlexTable myTable = new FlexTable();
myTable.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Cell cell = myTable.getCellForEvent(event);
int receiverRowIndex = cell.getRowIndex(); // <- here!
}
});
Supposing GWT 1.5.3:
CLICK EVENT HANDLING
If you are using FlexTable and you wanted a click event handler, you could use the FlexTable.addTableListener() and register your own TableListener. The TableListener object will need to implement the onCellClicked callback which would give you the row number.
OTHER EVENTS HANDLING (e.g. HOVER)
If you need to handle other type of events other than click (say, hover), GWT currently doesn't have a ready interface for that. You pretty much left on your own to implement them yourself. There's two ways of doing it that I can think of now:
The quick and dirty way, is probably by exploiting JSNI, which provides a means for you to inject Javascript into your GWT code. I didn't use much JSNI (apart from really hard workarounds which is not worth the effort writing it in pure GWT) in my code so I can't show you an example; but frankly I won't recommend this as it reduces maintainability and extensibility.
If you wanted a native, GWT interface, you can create a new class that inherits HTMLTable or FlexTable. At the constructor, call the sinkEvents function with your needed events. (e.g. for hover, you'll probably need sinkEvents(Event.ONMOUSEOVER)). Then you'll need the onBrowserEvent function that handles the mouseover.
A quick template of how the code should look like:
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlexTable;
public class FlexTableWithHoverHandler
extends FlexTable
{
public FlexTableWithHoverHandler()
{
super();
sinkEvents(Event.ONMOUSEOVER);
}
#Override
public void onBrowserEvent(Event event)
{
switch(DOM.eventGetType(event))
{
case Event.ONMOUSEOVER:
// Mouse over handling code here
break;
default:
break;
}
}
}
The best of learning how to code this is by looking at the GWT source code itself (search for sinkEvent) and getting the feel on how to do it the GWT way.
I found it much simple to add javascript directly to the TR element. My code assumes that a widgets DOM parent is a TD and the grandparent is the TR so you need to be sure you know your DOM.
Here's my code. Nice and simple, no JSNI or GWT DOM event management required.
TableRowElement rowElement = (TableRowElement) checkbox.getElement().getParentElement().getParentElement();
rowElement.setAttribute("onMouseOver", "this.className='" + importRecordsResources.css().normalActive() + "'");
rowElement.setAttribute("onMouseOut", "this.className='" + importRecordsResources.css().normal() + "'");
I just did it this simple way:
protected void handleRowsSelectionStyles(ClickEvent event) {
int selectedRowIndex = fieldTable.getCellForEvent(event).getRowIndex();
int rowCount = fieldTable.getRowCount();
for (int row = 0; row < rowCount; row++) {
Element rowElem = fieldTable.getRowFormatter().getElement(row);
rowElem.setClassName(row == selectedRowIndex ? "row selected" : "row");
}
}
You call this method from the cells you want to be clickable
int row = 0;
for (final RowDataProxy rowData : rowDataList) {
Label fieldName = new Label(rowData.name());
fieldName.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
handleRowsSelectionStyles(event);
}
});
fieldTable.setWidget(row++, 0, fieldName);
}
Best Regards,
Zied Hamdi

Resources