I created a test scenario to show the solution. Simple table for which a maintenance view would be created is:

The semantic meaning of this table does not matter in this case - it is just a simple table with two text fields which we would like to be able to maintain via SM30
.
We create a simple maintenance view for the table and add an event:

Added FORM routine CHECK_OBJECT_EVENT_01 (choose any include you like, it does not matter) for the event 01 - Before saving the data in the database
:

FORM check_object_event_01.
First create a structure type, which includes the table which we maintain and additionally the VIMTBFLAGS structure: Flag structure for view maint. tool: Flags for tables
. It would give us the possibility to access these flag fields:
TYPES BEGIN OF ty_s_table.
INCLUDE STRUCTURE zqm_cu_audobj_aa.
INCLUDE STRUCTURE vimtbflags.
TYPES END OF ty_s_table.
Afterwards in case something needs to be updated (<status>-upd_flag
= 'X') and not deleted (<action>
<> 'D') we are looping over the total
table:
DATA: ls_table TYPE ty_s_table,
lv_index LIKE sy-tabix,
lv_valid TYPE abap_bool VALUE abap_false,
lv_error TYPE abap_bool VALUE abap_false.
IF <status>-upd_flag = 'X' AND <action> <> 'D'.
LOOP AT total.
READ TABLE extract WITH KEY <vim_xtotal_key>.
IF sy-subrc EQ 0.
lv_index = sy-tabix.
ENDIF.
MOVE total TO ls_table.
PERFORM validate_object_string
USING ls_table-object_string
CHANGING lv_valid.
IF lv_valid = abap_false.
lv_error = abap_true.
ls_table-vim_mark = 'M'.
ELSE.
ls_table-vim_mark = ' '.
ENDIF.
MOVE ls_table TO total.
MODIFY total.
IF lv_index > 0.
extract = total.
MODIFY extract INDEX lv_index.
ENDIF.
ENDLOOP.
ENDIF.
IF lv_error = abap_true.
vim_abort_saving = 'X'.
MESSAGE 'Please correct this field' TYPE 'S' DISPLAY LIKE 'E'.
ENDIF.
ENDFORM.
Inside the loop we check the entered value for object_string
field of the table in a separate validate_object_string
form routine against the allowed values. For this simple test case I check if the value equals to 'Test', in which case we should mark (highlight) this line - this is done by assigning the value of vim_mark
field to M
. In case multiple entries are invalid they all will be marked (highlighted).
FORM validate_object_string USING iv_value TYPE plmt_audit_object_value_text
CHANGING cv_valid TYPE abap_bool.
cv_valid = abap_true.
IF iv_value = 'Test'.
cv_valid = abap_false.
ENDIF.
ENDFORM.
Afterwards if some of the entries were not successfully validated, we abort the saving by setting the vim_abort_saving
to 'X' and giving out an error message:

Next issue is to set the cursor on the invalid entry. To do it we have to modify PBO
for the screen generated by TMG. We can find the flow logic for the screen in SE80
inside the function group, double-clicking on the Maint.Screen No.
inside TMG or go from TMG to Environment -> Modification -> Maintenance Screens
-> select the screen:

I have added the MODULE check_object_pbo
, there is a code:
MODULE check_object_pbo OUTPUT.
LOOP AT SCREEN.
IF screen-intensified = 1.
SET CURSOR FIELD 'ZQM_CU_AUDOBJ_AA-OBJECT_STRING' LINE <vim_tctrl>-current_line.
EXIT.
ENDIF.
ENDLOOP.
ENDMODULE.
We look for the intensified screen element (it was set to intensified by routine in PBO generated by TMG based on the vim_mark
flag) and set the cursor to this field.
Please note that changes made to the screen flow logic would be overwritten if the maintenance view is regenerated - do not forget to add this change again (you can also copy the dynpro to some other number as a backup and afterwards copy it back again).