Cette page montre un exemple de customizer pour l'écran de modification d'une vente ou d'un achat (Billing) dans MercatorPenguin. Il s'applique aux ventes. Le code source est fourni dans le zip disponible ci-dessous.
Ce customizer ajoute un bouton (PortButton) dont le code est inclus dans le projet. Il hérite donc de Microsoft.Maui.Controls.Button. Il permet de poser la question "Ajouter frais de port" et d'ajouter une ligne dans la vente en cours avec l'article "frais de port" et le montant encodé. Ce bouton est placé via un override de la méthode OnInit.
Ci-dessous le code d'ajout d'une ligne dans la vente en cours :
private async void PortButton_Clicked(object sender, EventArgs e)
{
try
{
IsEnabled = false;
DataRow drPort = customizer.Lignes.Rows.FirstOrDefault(dr => dr["id_article"].ToString() == ID_PORT);
decimal currentPort = drPort != null ? Convert.ToDecimal(drPort["pu"]) : 0;
decimal? pu = await Dialogs.AskDecimal(customizer.Page, "Quel montant pour les frais de port ?", 2, currentPort);
if (pu == null)
return;
if (drPort != null)
customizer.Lignes.RemoveRow(drPort);
drPort = customizer.Lignes.NewRow();
drPort["id_article"] = ID_PORT;
drPort["designatio"] = DESIGN_PORT;
drPort["q"] = 1;
drPort["pu"] = pu.Value;
customizer.Lignes.AddRow(drPort);
}
finally
{
IsEnabled = true;
}
}
Le customizer permet aussi d'exécuter du code après la sélection du client. Ceci via la méthode OnInsertingCustomerSupplier. Dans cet exemple, on récupère les coordonnées complètes du client, via une requête SQL passée via MercatorPenguinServer, pour les afficher dans une boîte de dialogue. Le focus est aussi placé dans la zone "Référence".
public override async Task OnInsertingCustomerSupplierAsync(InsertingCustomerSupplierEventArgs e)
{
string id_cli = e.Tiers["id_cli"].ToString();
ActivityIndicator.SetActive(true);
var r = await GetRunSqlData<(string c_nom, string c_adresse, string c_codep, string c_ville)>("select c_nom, c_adresse, c_codep, c_ville from CLI where c_id = @id_cli", [new RunSqlDescriptor.Parameter("@id_cli", id_cli)]);
ActivityIndicator.SetActive(false);
if (r.Result == null)
{
await Dialogs.Stop(Page, "Erreur lors de la récupération des informations du client : " + r.Error);
e.CancelInsertingCustomerSupplier = true;
return;
}
if (r.Result.Count > 0)
{
IEntry editEntryReference = Controls.OfType<IEditEntry>().FirstOrDefault(c => c.Source?.Equals("reference", StringComparison.InvariantCultureIgnoreCase) ?? false);
editEntryReference?.Focus();
await Dialogs.Stop(Page, r.Result[0].c_nom + Environment.NewLine + r.Result[0].c_adresse + Environment.NewLine + r.Result[0].c_codep + " " + r.Result[0].c_ville);
}
}
Les méthodes OnBeforeInsertItem et OnAfterInsertItem permettent d'exécuter du code, respectivement avant et après l'insertion d'un article.
public override async Task OnBeforeInsertItemAsync(BeforeInsertItemEventArgs e)
{
await Dialogs.Stop(Page, $"Vous allez rechercher un article sur base de \"{e.EntryArt.Text}\" !");
}
public override async Task OnAfterInsertItemAsync(AfterInsertItemEventArgs e)
{
decimal? pu = await Dialogs.AskDecimal(Page, $"Quel prix pour l'article \"{e.Item["designatio"]}\" ?", 2);
if (pu == null)
{
e.CancelInsertItem = true;
return;
}
e.Item["pu"] = pu.Value;
}
Les méthodes suivantes sont exécutées à ces moments :
- OnBeforeAddLine : avant l'ajout d'une ligne,
- OnAfterAddLine : après l'ajout d'une ligne,
- OnAfterDeleteLine : après la suppression d'une ligne,
- OnBeforeChangeLine : avant la modification d'une ligne.
public override async Task OnBeforeAddLineAsync(BeforeAddLineEventArgs e)
{
IEditEntry editEntryQ = Controls.OfType<IEditEntry>().FirstOrDefault(c => c.Source?.Equals("line|q", StringComparison.InvariantCultureIgnoreCase) ?? false);
if (!await Dialogs.AnswerYesNo(Page, $"Voulez-vous vraiment ajouter une ligne avec l'article \"{e.EntryArt.TargetSigLabel.Text}\" et Q = {editEntryQ.Text} ?"))
{
e.CancelAddLine = true;
}
}
public override async Task OnAfterAddLineAsync(AfterAddLineEventArgs e)
{
await Dialogs.Stop(Page, $"La ligne n° {e.Ligne.Table.Rows.Count} a été ajoutée avec succès ! Elle contient l'article \"{e.Ligne["designatio"]}\".");
}
public override async Task OnBeforeDeleteLineAsync(BeforeDeleteLineEventArgs e)
{
if (!await Dialogs.AnswerYesNo(Page, $"Voulez-vous vraiment supprimer cette ligne {e.Ligne["id_article"]} sur ce picking ?"))
{
e.CancelDeleteLine = true;
}
}
public override async void OnAfterDeleteLine(AfterDeleteLineEventArgs e)
{
await Dialogs.Stop(Page, $"La ligne a été sumprimée avec succès !");
}
public override async Task OnBeforeChangeLineAsync(BeforeChangeLineEventArgs e)
{
if (!await Dialogs.AnswerYesNo(Page, $"Voulez-vous vraiment modifier la colonne {e.ColumnToChange} de cette ligne {e.Line["id_article"]} avec cette nouvelle valeur {e.NewValue} ?"))
{
e.CancelChangeLine = true;
}
}
La méthode OnBeforeSave permet d'intercepter la demande de sauvegarde.
public override async Task OnBeforeSaveAsync(BeforeSaveEventArgs e)
{
if (!await Dialogs.AnswerYesNo(Page, $"Voulez-vous vraiment enregistrer les modifications sur {(BillingTypeVA == MercatorPenguin.Enums.TypeVAEnum.V ? "cette vente" : "cet achat")} ?"))
{
e.CancelSave = true;
}
}
Ce customizer montre aussi comment ajouter un contrôle créé par code (RecentSales). Celui-ci montre une grille reprenant les 10 derniers articles vendus chez le client concerné par la vente en cours. Cette grille est populée lors de l'initialisation de la vente et lors du changement de client, via editEntryIdCli.AfterTargetSigSearchAsync. Cette grille est ajoutée par le code de la méthode OnInitAsync.
public override async Task OnInitAsync(InitEventArgs e)
{
VerticalStackLayout verticalStackLayout = ScrollView.Content as VerticalStackLayout;
if (verticalStackLayout == null)
{
MercatorTunnel.PlatformMaui.Api.ShowToastLongBottom("Impossible de trouver le VerticalStackLayout dans la page !");
}
else
{
PortButton portButton = new PortButton(this);
verticalStackLayout.Children.Add(portButton);
IEditEntry editEntryIdCli = Controls.OfType<IEditEntry>().FirstOrDefault(c => c.Source?.Equals("id_cli", StringComparison.InvariantCultureIgnoreCase) ?? false);
if (editEntryIdCli == null)
{
MercatorTunnel.PlatformMaui.Api.ShowToastLongBottom("Impossible de trouver l'EditEntry du client !");
}
else
{
RecentSales recentSales = new RecentSales(this, editEntryIdCli);
verticalStackLayout.Children.Add(recentSales);
await recentSales.Populate(true);
}
}
} A télécharger :
0000003450.zip (5 Kb - 13/05/2026)