Può capitare di dover gestire in Power Apps un ordinamento manuale in una lista, ad esempio, in una Todo list, si può voler definire una diversa priorità degli elementi da gestire tramite appositi pulsanti di spostamento su e giù.
Ordinamento manualeOrdinamento manuale
Per far questo è necessario avere una lista SharePoint o tabella SQL con un campo numerico intero, ad esempio di nome Position.
Lista SharePointLista SharePoint
La Gallery dovrà avere 2 pulsanti up e down
Pulsanti di spostamentoPulsanti di spostamento
con ad esempio questa formula come query sui dati:
Power Apps
Gallery.Items= SortByColumns(TodoList, "Position", Ascending)
E' fondamentale che l'ordinamento sia fatto sul campo Position.

Lo spostamento andrà realizzato tramite il metodo RowNumber associando il seguente codice di spostamento nell'evento OnSelect dei pulsanti:
Power Apps
UpdateContext(
    {
        _MoveUp: "up", /* impostare "down" per scendere */
        _FirstRowNumber: 0,
        _FirstID: ThisItem.ID
    }
);

/* salvo la query di ordinamento basata sul campo Position, deve coincidere con quella impostata nella Gallery.Items */
ClearCollect(
    _TmpTable,
    SortByColumns(
        TodoList,
        "Position",
        Ascending
    )
);

/* creo una nuova lista ordinata con RowNumber sequenziale/progressivo e l'ID riga */
Clear(_TableRowNumber);
ForAll(
    _TmpTable,
    Collect(
        _TableRowNumber,
        Last(
            FirstN(
                AddColumns(
                    ShowColumns(
                        _TmpTable,
                        "ID",
                        "Position"
                    ),
                    "RowNumber",
                    CountRows(_TableRowNumber) + 1
                ),
                CountRows(_TableRowNumber) + 1
            )
        )
    )
);

/* faccio pulizia di quello che non serve più */
Clear(_TmpTable);

/* trovo la riga corrente */
UpdateContext(
    {
        _FirstRowNumber: LookUp(
            _TableRowNumber,
            ID = _FirstID
        ).RowNumber
    }
);

/* e quella successiva o precedente */
UpdateContext(
    {
        _SecondRow: LookUp(
            _TableRowNumber,
            RowNumber = _FirstRowNumber + If(
                _MoveUp = "up",
                -1,
                1
            )
        )
    }
);

/* procedo con lo scambio ed aggiorno il db/lista */
If(
    IsBlank(_SecondRow) = false,

    /* scambio */
    Patch(
        _TableRowNumber,
        LookUp(
            _TableRowNumber,
            ID = _FirstID
        ),
        {RowNumber: _SecondRow.RowNumber}
    );
    Patch(
        _TableRowNumber,
        LookUp(
            _TableRowNumber,
            ID = _SecondRow.ID
        ),
        {RowNumber: _FirstRowNumber}
    );

    /* aggiorno i valori sul DB */
    ForAll(
        _TableRowNumber,
        If(
            _TableRowNumber[@Position] <> _TableRowNumber[@RowNumber],
            /* aggiorno solo se sono cambiati rispetto all'esistente */
            Patch(
                TodoList,
                LookUp(
                    TodoList,
                    ID = _TableRowNumber[@ID]
                ),
                {Position: _TableRowNumber[@RowNumber]}
            )
        );
    );
);

/* faccio pulizia di quello che non serve più */
Clear(_TableRowNumber);
UpdateContext(
    {
        _MoveUp: "",
        _SecondRow: Blank()
    }
);
Visto che la colonna Position potrebbe contenere dei valori non consecutivi o duplicati a causa di interventi manuali sulla lista, come prima cosa creo una collection temporanea _TableRowNumber ordinata per Position con l'aggiunta di una colonna RowNumber contentente un numero pregressivo che parte da 1.

A questo punto, partendo dalla riga corrente, è sufficiente scambiare i RowNumber con la riga precedente (move up) o successiva (move down), in base al pulsante premuto.

In ultimo salvo i valori di RowNumber sulla campo Position della lista/tabella.
Attenzione il sistema proposto funziona bene su liste con poche decine elementi, pena un calo delle prestazioni.
Nel caso del pulsante di spostamento in giù (down), copiare il codice e cambiare l'impostazione delle variabile _MoveUp
Power Apps
UpdateContext(
    {
        _MoveUp: "down",
        _FirstRowNumber: 0,
        _FirstID: ThisItem.ID
    }
);
...

Metodo alternativo

Anziché duplicare lo stesso codice nei due pulsanti up e down, si può mettere il codice in un unico punto sull'evento Gallery.OnSelect e nei pulsanti up e down impostare solo una variabile.

A questo punto il pulsante up conterrà:
Power Apps
gallerySxIcnUp.OnSelect = 
UpdateContext({_MoveUp: "up"}); 
Select(Parent);  /* richiamo l'evento Gallery.OnSelect */
quello down
Power Apps
gallerySxIcnDown.OnSelect = 
UpdateContext({_MoveUp: "down"}); 
Select(Parent)
infine Gallery.OnSelect conterrà una If che gestirà gli eventi move (up/down) oppure (else) gli altri eventi della riga
Power Apps
Gallery.OnSelect =
If(_MoveUp ="up" Or _MoveUp = "down",
    /* move */
    ...,
    /* else */
    ...
);
Il codice completo della Gallery.OnSelect sarà
Power Apps
If(
    _MoveUp = "up" Or _MoveUp = "down",
    /* move */
    UpdateContext({_FirstID: ThisItem.ID});

    /* salvo la query di ordinamento basata sul campo Position */
    ClearCollect(
        _TmpTable,
        SortByColumns(
            TodoList,
            "Position",
            Ascending
        )
    );
    
    /* creo una nuova lista ordinata con RowNumber sequenziale/progressivo e l'ID riga */
    Clear(_TableRowNumber);
    ForAll(
        _TmpTable,
        Collect(
            _TableRowNumber,
            Last(
                FirstN(
                    AddColumns(
                        ShowColumns(
                            _TmpTable,
                            "ID",
                            "Position"
                        ),
                        "RowNumber",
                        CountRows(_TableRowNumber) + 1
                    ),
                    CountRows(_TableRowNumber) + 1
                )
            )
        )
    );
    
    /* faccio pulizia di quello che non serve più */
    Clear(_TmpTable);
    
    /* trovo la riga corrente */
    UpdateContext(
        {
            _FirstRowNumber: LookUp(
                _TableRowNumber,
                ID = _FirstID
            ).RowNumber
        }
    );
    UpdateContext(
        {
            _SecondRow: LookUp(
                _TableRowNumber,
                RowNumber = _FirstRowNumber + If(
                    _MoveUp = "up",
                    -1,
                    1
                )
            )
        }
    );
    
    /* procedo con lo scambio ed aggiorno il db/lista */
    If(
        IsBlank(_SecondRow) = false,
        /* scambio */
        Patch(
            _TableRowNumber,
            LookUp(
                _TableRowNumber,
                ID = _FirstID
            ),
            {RowNumber: _SecondRow.RowNumber}
        );
        Patch(
            _TableRowNumber,
            LookUp(
                _TableRowNumber,
                ID = _SecondRow.ID
            ),
            {RowNumber: _FirstRowNumber}
        );
        /* aggiorno i valori sul DB */
        ForAll(
            _TableRowNumber,
            If(
                _TableRowNumber[@Position] <> _TableRowNumber[@RowNumber],
                /* aggiorno solo se sono cambiati rispetto all'esistente */
                Patch(
                    TodoList,
                    LookUp(
                        TodoList,
                        ID = _TableRowNumber[@ID]
                    ),
                    {Position: _TableRowNumber[@RowNumber]}
                )
            );
        );
    );
    /* faccio pulizia di quello che non serve più */
    Clear(_TableRowNumber);
    UpdateContext({_SecondRow: Blank()});
    ,
    /* else*/
    ...
);
/* resetto la variabile con l'evento */
UpdateContext({_MoveUp: ""});
Attenzione è importante alla fine resettare la variabile _MoveUp.

Gestione abilitazione pulsanti

Idealmente il pulsante up sulla prima riga dovrebbe essere disabilitato così come il pulsante down nell'ultima riga.

Per far questo si può gestire la proprietà DisplayMode confrontando gli ID della prima e ultima riga nella Gallery.

Il pulsante up userà l'istruzione FirstN:
Power Apps
gallerySxIcnUp.DisplayMode = If(First(FirstN(SortByColumns(TodoList, "Position", Ascending),1)).ID = ThisItem.ID, DisplayMode.Disabled, DisplayMode.Edit)
mentre quello down userà LastN:
Power Apps
gallerySxIcnDown.DisplayMode = If(First(LastN(SortByColumns(TodoList, "Position", Ascending),1)).ID = ThisItem.ID, DisplayMode.Disabled, DisplayMode.Edit)
anche in questo caso devo va usata la stessa formula della proprietà Gallery.Items (SortByColumns(TodoList, "Position", Ascending)).
Potrebbe interessarti anche: