Erweiterung der vorhandenen Views

Jede Ansicht in POS kann mit zusätzlichen Elementen erweitert werden. Die Erweiterung kann darin bestehen, der Datentabelle eine neue Spalte hinzuzufügen, eine vorhandene Spalte zu löschen oder ein beliebiges Steuerelement an einer vordefinierten Stelle hinzuzufügen. Es ist auch möglich, Programme von Drittanbietern aufzurufen, indem man einfach eine Schaltflächensteuerung hinzufügt, unter der sich die Logik befindet, die einen anderen Prozess auslöst.

Ein neues Element dem Container einfügen

Mithilfe von Erweiterungen kann übrigens jedes Steuerelement zu einer ausgewählten bestehenden Ansicht hinzugefügt werden, jedoch nur an Stellen, die zuvor dafür vorbereitet wurden. Die Anzahl der Elemente, die hinzugefügt werden können, ist unbegrenzt, sie können jedoch nur in speziellen erweiterbaren Containern (ItemsContainer und Grid) implementiert werden.

Um ein Steuerelement zu einer bestehenden Ansicht hinzuzufügen, müssen Sie zunächst feststellen, ob die Ansicht verwaltbar ist und ob sie über einen geeigneten Container verfügt, in dem Sie das neue Element implementieren können. Öffnen Sie dazu die Ansicht der Layoutverwaltung in der POS-Anwendung. Wählen Sie in der Dropdown-Liste der Ansichten diejenige aus, für die die Erweiterung erstellt werden soll. Klicken Sie dann auf die Leiste „Elemente“ und wählen Sie in der Dropdown-Liste den Container aus, wo genau in dieser Ansicht das neue Element eingefügt werden soll. Nach der Auswahl des Containers ist es wichtig, den Namen des Containers zu speichern, da er eine globale ID wird, die im nächsten Schritt der Implementierung der Erweiterung benötigt wird.

Wenn Sie bei der Erstellung der benutzerdefinierten Ansicht ihre Erweiterung ermöglichen wollen, müssen Sie Platz dafür schaffen, indem Sie ein oder mehrere Container-Steuerelemente hinzufügen oder die Ansicht mithilfe von Grid-Steuerelementen (Comarch.POS.Presentation.Core.Controls) erstellen und dabei eindeutige LayoutIds zuordnen (mehr dazu im Kapitel Verwaltung von Ansichten und denen Elementen).

Das Hinzufügen eines Steuerelements zu einer Ansicht beginnt mit der Erstellung eines neuen Moduls (Neues Modul) oder, wenn es bereits erstellt wurde, mit der Initialize()-Methode in der Module-Klasse. Um die Ansicht um ein neues Steuerelement zu erweitern, verwenden Sie die Methode

AddButtonToContainer – wenn Sie eine Schaltfläche einfügen wollen, oder

AddElementToContainer<TFrameworkElement> – wenn Sie ein beliebiges Steuerelement vom Typ FrameworkElement einfügen wollen

Erforderliche Parameter der beiden Methoden:

  • containerLayoutId (string) – Container-Id, zu der das Steuerelement eingefügt wird
  • buttonLayoutId / elementLayoutId (string) – eindeutige ID des neuen Steuerelements (jedes Steuerelement muss über eine eindeutige ID im Container verfügen),
  • styleKey (string) – optionaler Schlüsselname in der ModernUI.xaml-Datei, in der der Style für das Steuerelement definiert wird,
  • buttonViewModelFunc / elementViewModelFunc (Func<IViewModel, FrameworkElementViewModel>) – optionaler Parameter, mit dem ein lokales ViewModel für das Steuerelement erfasst wird. Im ViewModel wird es möglich sein, die Logik zu definieren, an die das Steuerelement gebunden werden kann (mithilfe von Styles).

Analogisch, um ein Element zum Grid hinzuzufügen, rufen Sie Folgendes auf:

AddElementToGrid<TFrameworkElement> – Parameter wie für Elemente, die dem Container eingefügt werden

 

Beispiel

Sie wollen eine Schaltfläche in die Ansicht des Kassenbelegs einfügen, die, wenn sie angeklickt wird, eine Benachrichtigung mit dem Belegwert anzeigt.

Der Name des Containers, dem diese Schaltfläche eingefügt wird, ist DocumentViewRightButtonsContainer. In der Module-Klasse des neuen Moduls, in dem die Erweiterung implementiert wird, in der Initialize-Methode, fügen Sie folgende Zeile hinzu:

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

 

wo ExtentionButton1 eine eindeutige ID (LayoutId) für die neue Schaltfläche ist, ButtonStyle der Name für den Schlüssel aus den Styles für diese Schaltfläche ist und ButtonViewModelFunc die Methode ist, die das lokale ViewModel zurückgibt, in dem die Logik implementiert wird, die die Benachrichtigung mit dem entsprechenden Inhalt aufrufen wird.

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($"Wartość dokumentu: {_documentViewModel.Document.Value}", NotifyIcon.Information);                
    }
}

In der ModernUI.xaml-Datei des Moduls fügen Sie den Style für die neue Schaltfläche hinzu, geben Sie dort den Inhalt der Schaltfläche an und binden die Klick-Aktion mit dem Befehl, der mit der ExtensionButtonAction-Methode verknüpft ist.

<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>

Vollständiger Code des Beispiels ist in Steuerelement zum Container einer bestehender Ansicht hinzufügen zu finden.

Eine Spalte zum bestehenden DataGrid hinzufügen

Die einfachste Lösung, eine neue Spalte zu einem bestehenden DataGrid der Dokumentenansicht (z. B. des Kassenbelegs/der Rechnung/des Vertriebsauftrags usw.) ist die Zuweisung eines Attributs zum Dokumentenelement im ERP-System (mehr dazu in Umgang mit Attributen ). Soll die Spalte hingegen kein Attribut sein, dann kommt die Vorgehensweise wie beim Hinzufügen von Steuerelementen zu Containern zum Tragen. Um eine bestehende DataGrid-Liste zu erweitern, müssen Sie ihre eindeutige ID kennen. Die finden Sie in der Layoutverwaltung, in der Ansicht mit dem DataGrid. Markieren Sie diese Ansicht und finden Sie die LayoutId in der Spalte Eigenschaften. Greifen Sie dann mit der RegisterDataGridExtension-Methode in der ModuleBase-Klasse auf dieses Steuerelement zu und implementieren Sie das Hinzufügen einer neuen Spalte zum Datensatz. Die Parameter von dieser Methode sind:

  • dataGridLayoutId (string) – LayoutId von DataGrid, das erweitert werden soll,
  • action (Action<DataGrid, IViewModel, bool>) – Delegat zur Methode, die bei der Erstellung des Steuerelements von DataGrid aufgerufen wird

Beispiel 1.

Sie wollen eine Spalte zu der Liste in der neuen Kassenbelegansicht hinzufügen, die anzeigt, ob die hinzugefügte Position den festgelegten Betrag nicht überschreitet.

Die LayoutItd der Liste auf dem Kassenbeleg ist ReceiptDocumentViewDataGrid. In der Initialize-Methode der Module-Klasse fügen Sie Folgendes hinzu:

RegisterDataGridExtension("ReceiptDocumentViewDataGrid", DataGridNewColumn);

Implementieren Sie dann die Methode DataGridNewColumn:

private void DataGridNewColumn(DataGrid dataGrid, IViewModel viewModel, bool isDesignMode)
{
    var column = new DataGridTextColumn
    {
        Header = „Ist es größer als 100?",
        Binding = new Binding {Converter = new ValidateConverter()}
    };
 
    Layout.SetId(column, "DocumentViewDataGridExtendedColumn1"); 
    dataGrid.Columns.Add(column);
}

Der isDesignMode-Parameter nimmt den Wert true an, wenn die Ansicht, die diess DataGrid enthält, im Schnittstellenverwaltungsmodus geöffnet ist. Fügen Sie dann die ValidateConverter-Klasse hinzu, die die Logik enthält, um den entsprechenden Wert für jede Zelle der hinzugefügten Spalte zurückzugeben:

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 ? „JA" : „NEIN";
        }
 
        //bei einer Rückkaufposition
        return „N/A";
    }
   … 
}

Im Beispiel wird davon ausgegangen, dass alle für die Darstellung eines Wertes erforderlichen Informationen in der Zeilenentität vorhanden sind. Wenn die Geschäftslogik für eine neue Spalte ebenfalls erweitert werden muss, müssen zunächst die erforderlichen zusätzlichen Daten für die neue Spalte abgerufen werden. Die abgerufenen Daten können in einer speziell vorbereiteten öffentlichen Eigenschaft gespeichert werden, die in jedem Viewmodel verfügbar ist – und zwar dem CustomDataDictionary. Dies ist eine Dictionary-Eigenschaft (String, Objekt), auf die Sie Bezug in einer definierten Spalte mit Hilfe von Bindung nehmen können.

Beispiel 2.

Sie wollen eine neue Spalte in der Kassenbelegansicht hinzufügen, die den Namen einer Preisliste eines Artikels anzeigen, der dieser Liste eingefügt wird. Die Produktentität (IDocumentItemRow) enthält nur die ID der Preisliste (PriceListId), aber keinen Namen.

Laden Sie zuerst die vollständige Liste der Prislisten heruntern und speichern Sie diese in CustomDataDictionary. Es genügt, wenn Sie die Preislisten nur einmal zu Beginn herunterladen, z. B. wenn Sie die Ansicht öffnen. Zu diesem Zweck können Sie extension points verwenden und das AfterOnInitializationEvent einfügen, nach der Initialisierung von der Methode AfterOnInitialization aufgerufen wird, oder indem von der DocumentViewModel-Klasse geerbt wird und die OnInitialization-Methode überladen wird. Sie können dies auch beim Einfügen einer neuen Spalte tun, d.h. in der Aktion der RegisterDataGridExtension-Methode. Für dieses Beispiel wählen Sie die letzte Methode aus.

In der Initialize-Methode der Module-Klasse rufen Sie Folgendes auf:

RegisterDataGridExtension("ReceiptDocumentViewDataGrid", DataGridNewColumnWithCustomBL);

Implementieren Sie dann die Methode 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, „erste" },
                    {2, „zweite" }
                });
 
                //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);
}

Im ersten Teil der Methode wird der Abruf von Preislisten simuliert, indem das CustomDataDictionary mit einem Dictionary (Preislisten-ID, Preislistenname) mit zwei Werten gefüllt wird. Erstellen Sie absichtlich ein Dictionary im Dictionary, da das CustomDataDictionary möglicherweise auch für die Speicherung anderer Informationen nützlich sein könnte (z. B. für andere Geschäftslogik). Der Schlüssel CustomColumnTest ist ein in der Module-Klasse definiertes const string-Feld, das einen eindeutigen Namen enthält, mit dem Sie den Datensatz (Preislisten) identifizieren werden.

public const string CustomColumnTest = "CustomColumnTest";

Im zweiten Teil der Methode wird ein Konverter zusammen mit der Multibinding-Spalte erstellt. Multibinding definiert eine Bindung an die aktuelle Zeilenentität und an das Dictionary mit Preislisten im CustomDataDictionary unter dem Schlüssel CustomColumnTest. Im Konverter hingegen erhalten Sie beide Objekte, so dass Sie den Namen der Preisliste auf Basis von der in der Entität enthaltenen ID und dem im Dictionary enthaltenen Namen zurückgeben können.

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;
        }
... 
    }

Das vollständige Beispiel enthält noch das Hinzufügen in die SetInitialPrice-Methode, in der eine Anfrage zur Aktualisierung von Binding aus dem CustomDataDictionary gestellt wird, denn sobald der Anfangspreis geändert wird, ist der angezeigte Preis nicht mehr der Preis aus der Preisliste (die PriceListId-Eigenschaft ist jetzt null) und die neue Name der Spalte sollte ihn nicht mehr anzeigen.

Vollständiger Code der Beispiele ist in Spalte zum DataGrid im bestehenden View hinzufügen zu finden.

Zugriff zum bestehenden Element

Es ist auch möglich, auf die Eigenschaften jedes vorhandenen Steuerelements zuzugreifen, für das eine layoutId festgelegt wurde. Nutzen Sie dazu in der Module-Klasse die AttachToFrameworkElement-Methode. Die Methode hat analoge Parameter wie die RegisterDataGridExtension-Methode.

Elemente zum Statusbereich hinzufügen

Der Statusbereich zeichnet sich dadurch aus, dass die dort eingefügten Elemente bei laufender Anwendung jederzeit verfügbar sind, unabhängig von den geöffneten Ansichten. Auf diesen Bereich kann von jeder Basisansicht aus zugegriffen werden. Um dem Statusbereich ein Steuerelement mit benutzerdedinierter Logik hinzuzufügen, rufen Sie in der Module-Klasse in der Initialize-Methode die Methode AddElementToStatusBar<TFrameworkElement> auf. Die Argumente von dieser Methode sind:

  • elementLayoutId (string) – eindeutige ID des Steuerelements
  • styleKey (string) – Schlüsselname in der ModernUI.xaml-Datei, in der der Style für das Steuerelement definiert wird,
  • elementViewModelFunc (Func<IStatusBar,StatusBarEementBase>) – Delegat zur Methode, die bei der Erstellung des Steuerelements aufgerufen wird

Beispiel für Implementierung in Beispiel für Erweiterung des Statusbereichs

Czy ten artykuł był pomocny?