I think I’ve found a way to identify whether the user is just viewing an existing row, editing an existing one, or adding a new one:
First, add a (physical, not virtual) column to track the number of times the row has been edited. I do this by adding
column, [ecount] (“edit count”), of type Number, with an App formula of =([_this] + 1).
Then, add a virtual column to indicate whether the row is being viewed, added, or edited. I call mine [vtype] (“view type”). Use the App formula:
=if(([ecount] = lookup([_thisrow].[id], MyTable, id, ecount)), “view”, if(([ecount] = 1), “add”, “edit”))
Replace MyTable with the name of the table, and id with the name of the key column.
You can then query [vtype] to determine how the row is being accessed: add, edit, or view.
The way this works is that lookup() goes into the actual table to retrieve values and bypasses the copy of a row being edited. If the the row is only being viewed (as in a Detail view, or anywhere outside a Form view), the row isn’t copied, so [ecount] and lookup(…, ecount) refer to the same thing, making them equal. If the row is being used in a Form view, column references refer to a copy of the row that contains the new values from the form. The form copy’s [ecount] column will have been updated by its App formula, so the copy’s won’t equal the original’s still in the table. A new row will always have an [ecount] value of 1; any other value means it was a preexisting row.