Is it possible to catch single button click on the ALV Grid column header ? I know about possibilities to catch double_click, create event for the hotspot on the cell. I have not found yet one header click. (I do not want to provide sort or any other data options). Only thing I found is event click_col_header OF cl_gui_alv_grid but it is protected so I am not able to provide my action.
Thank you in advance !
Yes. Here is how to do it with CL_GUI_ALV_GRID:
Set the ALV layout setting SGL_CLK_HD = 'X' (this activates the sorting of rows based on the clicked column)
Redefine the sorting behavior with your own code, by handling the event BEFORE_USER_COMMAND and set a new command with the method SET_USER_COMMAND
Define the new behavior you want during the event USER_COMMAND
Use the method GET_SELECTED_COLUMNS to know which column has been clicked
(PS: if one wonders, CL_GUI_ALV_GRID has the event CLICK_COL_HEADER, but it's never triggered)
Here is a demo (also posted below Raymond answer in the SAP forum):
CLASS lcl_alv DEFINITION.
PUBLIC SECTION.
METHODS constructor.
METHODS free.
METHODS on_before_user_command FOR EVENT before_user_command OF cl_gui_alv_grid IMPORTING e_ucomm.
METHODS on_user_command FOR EVENT user_command OF cl_gui_alv_grid IMPORTING e_ucomm.
DATA go_alv TYPE REF TO cl_gui_alv_grid.
DATA gt_sflight TYPE TABLE OF sflight.
ENDCLASS.
CLASS lcl_alv IMPLEMENTATION.
METHOD constructor.
CREATE OBJECT go_alv
EXPORTING
i_parent = cl_gui_container=>screen0.
SET HANDLER on_user_command FOR go_alv.
SET HANDLER on_before_user_command FOR go_alv.
SELECT * FROM sflight INTO TABLE gt_sflight.
go_alv->set_table_for_first_display(
EXPORTING
i_structure_name = 'SFLIGHT'
is_layout = VALUE #( sgl_clk_hd = abap_true )
CHANGING
it_outtab = gt_sflight ).
ENDMETHOD.
METHOD free.
go_alv->free( ).
ENDMETHOD.
METHOD on_before_user_command.
CASE e_ucomm.
WHEN go_alv->mc_fc_sort.
go_alv->set_user_command( i_ucomm = 'ZZSORT' ).
ENDCASE.
ENDMETHOD.
METHOD on_user_command.
CASE e_ucomm.
WHEN 'ZZSORT'.
go_alv->get_selected_columns( IMPORTING et_index_columns = data(columns) ).
MESSAGE |Columns: { COND #( WHEN lines( columns ) > 0 THEN columns[ 1 ]-fieldname ) }| TYPE 'I'.
ENDCASE.
ENDMETHOD.
ENDCLASS.
DATA go_alv TYPE REF TO lcl_alv.
PARAMETERS dummy.
AT SELECTION-SCREEN OUTPUT.
IF go_alv IS NOT BOUND.
go_alv = NEW lcl_alv( ).
ENDIF.
AT SELECTION-SCREEN ON EXIT-COMMAND.
go_alv->free( ).
FREE go_alv.
Related
I am using objectbox and attempting to force recomp on my lazy column with information from the viewModel.
I currently use a method that has been state hoisted to the view model but can't seem to retrieve the value in the view model and force recomp.
In my view model (DataFieldsViewModel), I've obtained all the Data Fields like this:
private val _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java)
var dataFieldsBox: Box<DataField> = _dataFieldsBox
and then it is passed in the composable screen using
fields = viewModel.dataFieldsBox
and the data fields object removed with
is DataFieldEvent.ConfirmDelete -> {
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(deletedDataField.value)
}
the reference for deletedDataField is kept so that it can be restored from a snack bar.
The delete works, the restore works, it's just that the lazy column does not update until I click another view that forces recomp. I've even mocked up a simple button with state hoisted increment and decrement methods and a value in the view model to test out recomp and got that working pretty quick, just don't know how to do it for these ObjectBox DataField objects
Edit 18/08
Tried using mutableStateOf
private var _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java)
var dataFieldsBox = mutableStateOf(_dataFieldsBox)
and then attempting to collect the value with
val fields by viewModel.dataFieldsBox
and then deleting with
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(event.dataField)
only fix right now is the two hacky ways which i don't like. I read something about despite the values in a mutable list changing because the reference is the same the recomp doesn't happen to save on recomps.
So tried to amend the list and then duplicate with new values which also did nothing.
is DataFieldEvent.ConfirmDelete -> {
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(event.dataField)
val newList = _dataFieldsBox
dataFieldsBox = mutableStateOf(newList)
}
Would be helpful if you update viewmodel code you are having.
Not worked on ObjectBox. With your current code, here is the thing.
Composable function will recompose when it is able to listen to changes. For normal variable it does not listen to changes. You can make them listen in several ways.
You can do it with mutableStateOf(). You can have mutableState variable which takes type of _dataFieldsBox . And so can access it by viewModel._dataFieldsBox.value.
private val _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java
var _dataFieldsBox = mutableStateOf(_dataFieldsBox)
And then in view model,
_dataFieldsBox.value.remove(_dataFieldsBox) // This will remove the value in the state variable and composable function will recompose.
You can also use LiveData and observe for the changes and it will recompose.
I am trying to find out over which source file element the cursor is located (code is inside a pad)
//Obtain document
Document sf = IdeApp.Workbench.ActiveDocument;
//out argument
DocumentRegion dr;
//Call using offset
Microsoft.CodeAnalysis.ISymbol o = sf.GetLanguageItem(sf.Editor.CaretOffset , out dr);
The ISymbol returned "o" is Object's Equals. The document sf is a simple class with a parameterless constructor. The cursor is inside the constructor. I was expecting my class constructor.
Where is the error?
Ok. I found a work around to get context data out of the current editor caret offset. It requires to obtain AnalysisDocument from the current document, then the SemanticModel of the document and after obtaining this model, calling GetEnclosingSymbol with the caret offset.
when I try to run the below code i'm getting the error through code analysis.
//Code
For Each UltraGridRow In transactionFieldsGrid.Rows.GetAllNonGroupByRows()
If (Field.FieldTypeId = 1000) Then
Dim cboUltra = New UltraCombo()
cboUltra.DataSource = LoadLookupMulticolumn(Field.LookUpCode)
UltraGridRow.Cells("FieldValue").ValueList = cboUltra
EndIf
Next
//Error
CA2000 Dispose objects before losing scope In method 'TransactionFieldsController.LoadTransactionFieldsGrid(UltraGridBase, Collection(Of TransactionField), WorkflowsController, Boolean)', object 'cboUltra' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'cboUltra' before all references to it are out of scope.
To overcome this I rewritten the code as:
For Each UltraGridRow In transactionFieldsGrid.Rows.GetAllNonGroupByRows()
If (Field.FieldTypeId = 1000) Then
Using cboUltra As New UltraCombo()
cboUltra.DataSource = LoadLookupMulticolumn(Field.LookUpCode)
UltraGridRow.Cells("FieldValue").ValueList = cboUltra
End Using
EndIf
Next
But when I tried like the above code. I'm getting an Object reference error when the below line gets executed.
transactionFieldsGrid.ActiveRow = transactionFieldsGrid.Rows.GetRowAtVisibleIndex(0)
Note: The grid and rows contain values.
Any solutions?
In this case, you can't dispose the UltraCombo instances in the method because the grid has references to the UltraCombo. For disposing the UltraCombo's that you are creating, you would need to store them in a collection scoped to the form and dispose them when the form is disposed. This is necessary because within the method is too soon since the grid still needs access to the combo.
Note that if you need to use the same data for multiple drop downs in the grid, then it would be better to only have one instance of that drop down and reuse it. For this you could create a helper object that would track the instances of the UltraCombo and return the correct instance for a specific LookUpCode and if it hasn't already created the instance it would when it is requested. If these are stored in a dictionary in the helper object, you could implement IDisposable on the helper and dispose all of the UltraCombos when dispose is called on the helper. You could then have a single instance of this helper on your form and call its dispose when the form is disposed.
has anyone written a generic "LaunchForm" function? For all the menu items I have that open a form, I would like to write one function that will launch the form as opposed to writing the same code several times.
any tips would be very helpful.
It's winforms + vb.net
thanks
TR
You mean something like this?
C#
public F Launch<F>() where F : Form, new()
{
F dlg = new F();
dlg.MdiParent = this;
dlg.Show();
return dlg;
}
VB.NET
Public Function Launch(Of F As {Form, New})() As F
Dim dlg As New F()
dlg.MdiParent = Me
dlg.Show()
Return dlg
End Function
You could attach the form type to the menuitem's .Tag property. Hook up a single event handler to all the menu item click events and pass the .Tag property value (form type) to a function that created a new instance and displayed it.
Alternately, if each form is to be a singleton you could create a dictionary of(MenuItem, Form), prepopulate with form instances and do the appropriate lookup/show. Or even skip the dictionary and pop and instance of the form into the menuitem's .Tag property.
There's all kinds of options and without knowing the intended usage it's hard to suggest just one.
I try to call skype instance by COM on F#.
A aim is get mood message.
test.fs
// Import skype4com Api
open SKYPE4COMLib
type SKYPE4COM =
new() = new SKYPE4COM()
let GetMood =
let aSkype = new SKYPE4COM
mood <- aSkype.CurrentUserProfile.MoodText
mood
But when build(before too),error occur.
Incomplete structured construct at or before this point in expression
Thanks in advance.
this is next version what I think.
test01.fs
// Import skype4com Api
open SKYPE4COMLib
let GetMood =
let aSkype = new SKYPE4COMLib() // line 1
mood <- aSkype.CurrentUserProfile.MoodText // line 2
mood // line 3
error message(when building).
line in 1:error FS0039: The type 'SKYPE4COMLib' is not defined
line in 2:error FS0039: The value or constructor 'mood' is not defined
line in 3:error FS0039: The value or constructor 'mood' is not defined
also like that...
Your code has several issues. First of all, your constructor for the SKYPE4COM class appears to be recursive (?!), which is going to cause a stack overflow if you try to create an instance. Secondly, the error that you're receiving is because you are using the new operator, but you haven't completed the call to the constructor (i.e. you need to apply the constructor using parentheses: let aSkype = new SKYPE4COM()). Even then, though, you've got another issue because your type doesn't expose a CurrentUserProfile property, so your code still won't work.
Try something like this:
open SKYPE4COMLib
let getMood() =
SkypeClass().CurrentUserProfile.MoodText
Consider using a Type Extension to add a member to an already existing type:
open SKYPE4COMLib
type SKYPE4COMLib with
member this.GetMood() =
aSkype.CurrentUserProfile.MoodText
This would allow you to access GetMood as if it were a member function defined on the SKYPE4COMLib type:
let x = new SKYPE4COMLib()
printfn "%A" (x.GetMood())