Extensions des vues existantes

Des éléments supplémentaires peuvent être ajoutés à tout vue existante dans POS. Une extension peut être l’ajout d’une nouvelle colonne au datagrid, la suppression d’une colonne existante ou l’ajout d’un n’importe quel contrôle dans l’espace préparé auparavant. Il est également possible d’appeler les logiciels des entreprises tierces en ajoutant simplement le contrôle du bouton sous lequel sera caché la logique initialisant un autre processus.

Ajouter un nouvel élément au conteneur existant

Grâce aux extensions, il est possible d’ajouter tout contrôle à la vue existante sélectionnée, mais uniquement dans les lieux préparés auparavant. Il n’y a pas de limite du nombre des éléments ajoutés, mais ils ne peuvent être placés que dans les conteneurs préparés à l’extension (ItemsContainer et Grid).

Afin d’ajouter un contrôle à la vue existante, il faut commencer par déterminer si la vue est gérable et si elle contient le conteneur approprié où il sera possible de placer le nouvel élément. À ces fins, il faut ouvrir dans l’application POS la vue de la gestion d’interface. Ensuite, sélectionner de la liste des vues déroulante la vue pour laquelle nous allons créer une extension. Ensuite, il faut appuyer sur la barre Éléments et sélectionner à partir de la liste des conteneurs déroulante dans quel endroit de vue sera placé nouvel élément. Après avoir sélectionné un conteneur, il faut enregistrer son nom, car c’est un identifiant global qui sera indispensable à l’étape suivante de la création d’extension.

Si, lors de la création de notre propre vue, nous souhaitons la rendre extensible, il faut préparer l’espace pour cette éventualité en ajoutant un ou plusieurs contrôles de conteneur ou en construisant la vue à l’aide du contrôle Grid (Comarch.POS.Presentation.Core.Controls), en n’oubliant pas de leur donner des identifiants LayoutId uniques (pour plus de détails, voir l’article Gestion de vue et de ses éléments).

Pour ajouter un contrôle à la vue, il faut d’abord créer un nouveau module (voir le chapitre Nouveau module dans l’article Créer des vues) ou, s’il a déjà été créé, il faut aller au corps de la méthode Initialize() dans la classe Module. Afin d’étendre la vue avec un nouveau contrôle, il faut utiliser la méthode

AddButtonToContainer en cas d’ajout de bouton ou

AddElementToContainer<TFrameworkElement> – en cas d’ajout de tout contrôle du type FrameworkElement

Les paramètres requis pour les deux méthodes sont :

  • containerLayoutId (string) – identifiant du conteneur auquel sera ajouté un contrôle,
  • buttonLayoutId / elementLayoutId (string) – identifiant unique du nouveau contrôle (chaque contrôle doit avoir dans le conteneur un identifiant unique),
  • styleKey (string) – nom de clé facultatif dans le fichier ModernUI.xaml où sera défini le style du contrôle
  • buttonViewModelFunc / elementViewModelFunc (Func<IViewModel, FrameworkElementViewModel>) – paramètre facultatif permettant de créer un ViewModel local pour le contrôle. Dans le ViewModel nous pourrons définir la logique avec laquelle le contrôle pourra se binder (à l’aide de style).

Par analogie, pour ajouter un élément au Grid, il faut appeler :

AddElementToGrid<TFrameworkElement> – les paramètres sont les mêmes que pour les éléments ajoutés à un conteneur

Exemple :

Nous voulons ajouter à la vue du reçu un bouton qui, lorsqu’il sera cliqué, fait afficher une notification avec la valeur du document.

Le nom du conteneur auquel le bouton sera ajouté est DocumentViewRightButtonsContainer. Dans la classe Module du nouveau module d’extension, dans la méthode Initialize, nous ajoutons la ligne :

AddButtonToContainer("DocumentViewRightButtonsContainer", "ExtensionButton1", "ButtonStyle", ButtonViewModelFunc);

où l’ExtensionButton1 est le nom unique (identifiant LayoutId) du nouveau bouton, le ButtonStyle est le nom de la clé avec le style pour ce bouton et le ButtonViewModelFunc est la méthode qui retourne le ViewModel local où sera implémentée la logique appelant la notification avec le contenu approprié.

private FrameworkElementViewModel ButtonViewModelFunc(IViewModel viewModel)
{
    return new ButtonViewModel(viewModel, ViewManager, Container);
}
 
public class ButtonViewModel : FrameworkElementViewModel
{
    public DelegateCommand ExtensionButtonCommand { get; set; }
 
    private readonly IDocumentViewModel _documentViewModel;
    private readonly INotificationService _notifyService;
 
    public ButtonViewModel(IViewModel viewModel, IViewManager viewManager, IUnityContainer container) : base(viewModel, viewManager)
    {
        if (viewModel.IsDesignMode)
            return;

        _notifyService = container.Resolve<INotificationService>();
        _documentViewModel = (DocumentViewModel)viewModel;
        ExtensionButtonCommand=new DelegateCommand(ExtensionButtonAction);
    }
 
    private void ExtensionButtonAction()
    {
        _notifyService.Show($"Montant de document : {_documentViewModel.Document.Value}", NotifyIcon.Information);                
    }
}

Dans le fichier ModernUI.xaml de notre module, nous ajoutons le style pour le nouveau bouton en y définissant le contenu du bouton et nous relions l’action de clic à la commande associée à la méthode ExtensionButtonAction.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:buttons="clr-namespace:Comarch.POS.Presentation.Core.Controls.Buttons;assembly=Comarch.POS.Presentation.Core">

<Style x:Key="ButtonStyle" TargetType="buttons:Button"
       BasedOn="{StaticResource {x:Type buttons:Button}}">
    <Setter Property="Content" Value="Pokaż wartość" />
    <Setter Property="Command" Value="{Binding ExtensionButtonCommand}" />
</Style>

Le code entier de l’exemple est disponible dans le chapitre Ajouter un contrôle au conteneur d’une vue existante dans l’article Exemples.

Ajouter une colonne à un DataGrid existant

La façon la plus simple d’ajouter une nouvelle colonne à un datagrid existant de la vue de document (par exemple du reçu/de la facture, de la commande client etc.) est assigner dans le système ERP un attribut à un élément de document (pour plus de détails, voir l’article Gestion des attributs). En revanche, si nous ne voulons pas que la nouvelle colonne soit un attribut, il faut procéder comme dans le cas de l’ajout des contrôles aux conteneurs. Afin d’étendre une liste DataGrid existante, il faut connaître son identifiant unique. Pour le connaître, il faut ouvrir la vue contenant le datagrid en mode de gestion des vues, le cliquer et trouver son LayoutId dans la rubrique Propriétés. Ensuite, nous accédons au contrôle à l’aide de la méthode RegisterDataGridExtension de la classe ModuleBase et nous implémentons l’ajout d’une nouvelle colonne dans la collection. Les paramètres de cette méthode sont :

  • dataGridLayoutId (string) – identifiant du layoutId du datagrid étendu,
  • action (Action<DataGrid, IViewModel, bool>) – délégué à la méthode qui sera appelée lors de la création du contrôle de datagrid

Exemple  1.

Nous voulons ajouter une colonne à la liste de la vue d’un nouveau reçu qui affichera l’information si la position ajoutée ne dépasse pas le montant défini.

L’identifiant Layout Id de la liste dans un reçu c’est ReceiptDocumentViewDataGrid. Dans la méthode Initialize de la classe Module nous ajoutons :

RegisterDataGridExtension("ReceiptDocumentViewDataGrid", DataGridNewColumn);

Ensuite, nous implémentons la méthode DataGridNewColumn :

private void DataGridNewColumn(DataGrid dataGrid, IViewModel viewModel, bool isDesignMode)
{
    var column = new DataGridTextColumn
    {
        Header = "Dépasse 100 ?",
        Binding = new Binding {Converter = new ValidateConverter()}
    };
 
    Layout.SetId(column, "DocumentViewDataGridExtendedColumn1"); 
    dataGrid.Columns.Add(column);
}

La valeur du paramètre isDesignMode change en true, lorsque la vue contenant ce datagrid est ouverte en mode de gestion des vues. Ensuite, nous ajoutons la classe de convertisseur ValidateConverter qui contiendra la logique retournant la valeur appropriée pour chaque cellule dans la colonne ajoutée :

internal class ValidateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var row = value as TradeDocumentItemRow;
 
        if (row!=null)
        {
            return row.Price > 100 ? "OUI" : "NON";
        }
 
        //en cas de positions reprises
        return "Ne concerne pas";
    }
   … 
}

L’exemple ci-dessus suppose que toutes les informations requises à afficher les valeurs sont disponibles dans l’entité de ligne. Dans le cas où la logique commerciale pour la nouvelle colonne doit être également étendue, il faut d’abord télécharger les données indispensables pour la nouvelle colonne. Les données téléchargées peuvent être stockées dans une propriété publique spécialement préparée disponible dans chaque viewmodel – CustomDataDictionary. C’est une propriété du type répertoire (string, object) à laquelle nous pouvons faire appel dans la colonne définie à l’aide de binding.

 

Exemple  2.

Nous ajoutons une nouvelle colonne dans la vue du reçu qui affichera le nom du tarif du produit ajouté à la liste. L’entité de produit (IDocumentItemRow) ne contient que l’identifiant du tarif (PriceListId), mais n’a pas de son nom.

Nous commençons par télécharger la liste complète des tarifs et l’enregistrer dans le répertoire CustomDataDictionary. Il suffit de télécharger les tarifs une seule fois au début, par exemple lors de l’ouverture de vue. À ces fins, nous pouvons utiliser les extension points pour nous brancher sur la méthode après l’initialisation – AfterOnInitialization, ou par l’héritage de la classe DocumentViewModel et la surcharge de la méthode OnInitialization. Il est également possible de télécharger la liste des tarifs lors du branchement avec la nouvelle colonne, c’est-à-dire dans l’action de la méthode RegisterDataGridExtension. Aux fins de cet exemple, nous allons sélectionner cette dernière méthode.

Dans la méthode Initialize de la classe Module nous faisons appel :

RegisterDataGridExtension("ReceiptDocumentViewDataGrid", DataGridNewColumnWithCustomBL);

Ensuite, nous implémentons la méthode DataGridNewColumnWithCustomBL

private void DataGridNewColumnWithCustomBL(DataGrid dataGrid, IViewModel viewModel, bool isDesignMode)
{
       if (viewModel is CustomDocumentViewModel vm)
       {
                //fill custom dictionary with dictionary of data for custom column (priceListId => name)
                vm.CustomDataDictionary.Add(CustomColumnTest, new Dictionary<int, string>
                {
                    {1, "premier" },
                    {2, "deuxième" }
                });
 
                //after initial price changed refresh custom column binding
                vm.AfterSetInitialPrice += () => { 
                       vm.OnPropertyChanged(nameof(vm.CustomDataDictionary)); 
                };
       }
 
       var column = new DataGridTextColumn
       {
          Header = "Price list name",
          Binding = new MultiBinding
          {
            Converter = new CustomMultiConverter(),
            Bindings =
            {
              new Binding(),
              new Binding
              {
                RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DocumentView), 1),
 Path = new PropertyPath($"DocumentViewModel.CustomDataDictionary[{CustomColumnTest}]")
              }
            }
          }
       };
 
    dataGrid.Columns.Add(column);
}

Dans la première partie de la méthode, nous simulons le téléchargement des tarifs en complétant le répertoire CustomDataDictionary avec un répertoire avec deux valeurs (id du tarif, nom du tarif). Ceci est fait exprès, car CustomDataDictionary pourrait être utilisé à stocker également des autres informations (par exemple pour une autre logique commerciale). La clé CustomColumnTest est un champ du type const string défini dans la classe Module, contenant un nom unique grâce auquel il sera possible d’identifier notre collection de données (tarifs).

public const string CustomColumnTest = "CustomColumnTest";

Dans la seconde partie de la méthode, nous créons une colonne avec multibinding et un convertisseur. Le multibinding définit le binding à l’entité de ligne actuelle, ainsi qu’au répertoire avec les tarifs contenus dans le répertoire CustomDataDictionary sous la clé CustomColumnTest. Dans le convertisseur, par contre, nous obtenons les deux objets afin de pouvoir retourner le nom de la liste de prix sur la base de l’id contenu dans l’entité et du nom contenu dans le dictionnaire.

internal class CustomMultiConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var documentItemRow = values[0] as IDocumentItemRow;
            var dictionary = values[1] as Dictionary<int, string>;
 
            //if item has price list id then show price list name (when initial price changes, price list id nulls)
            if (documentItemRow?.PriceListId.HasValue ?? false)
            {
                string name = null;
                if (dictionary?.TryGetValue(documentItemRow.PriceListId.Value, out name) ?? false)
                {
                    return name;
                }
            }
 
            return null;
        }
... 
    }

L’exemple complet contient encore le branchement sur la méthode SetInitialPrice dans lequel est appelé la demande d’actualiser le binding avec CustomDataDictionary, car après la modification du prix initial, le prix affiché n’est plus le prix du tarif (la propriété PriceListId est désormais null) et la nouvelle colonne avec le nom ne doit plus l’afficher.

Le code complet des exemples est disponible dans le chapitre Ajouter une colonne à DataGrid dans une vue existante dans l’article Exemples.

Accès à un élément existant

Il est également possible d’avoir accès aux propriétées de chaque contrôle existant qui a un layoutId défini. À cette fin, il faut utiliser dans la classe Module la méthode AttachToFrameworkElement. Les paramètres de cette méthode sont analogues à ceux de la méthode RegisterDataGridExtension.

Ajouter des éléments à la zone de statut

La zone de statut se caractérise par le fait que les éléments y emplacés sont disponibles durant le fonctionnement de l’application, indépendamment des vues ouvertes. L’accès à cette zone peut se faire de tout vue de base. Afin d’ajouter à la zone de statut un contrôle avec une logique personnalisée, il faut appeler la méthode AddElementToStatusBar<TFrameworkElement> dans la méthode Initialize de la classe Module. Les arguments de la méthode sont :

  • elementLayoutId (string) – identifiant unique du contrôle ajouté,
  • styleKey (string) – nom de clé dans le fichier ModernUI.xaml où sera défini le style du contrôle
  • elementViewModelFunc (Func<IStatusBar,StatusBarEementBase>) – délégué à la méthode qui sera appelée lors de la création de contrôle

Pour voir un exemple d’implémentation, consultez le chapitre Exemple d’extension de la zone de statut dans l’article Exemples.

Czy ten artykuł był pomocny?