Listes sources dans Xamarin.Mac

Cet article traite de l’utilisation des listes sources dans une application Xamarin.Mac. Il décrit la création et la maintenance de listes sources dans Xcode et Interface Builder et l’interaction avec ces listes dans le code C#.

Lorsque vous utilisez C# et .NET dans une application Xamarin.Mac, vous avez accès aux mêmes listes de sources qu’un développeur travaillant dans Objective-C et Xcode . Étant donné que Xamarin.Mac s’intègre directement à Xcode, vous pouvez utiliser le Générateur d’interface de Xcode pour créer et gérer vos listes de sources (ou éventuellement les créer directement dans du code C#).

Une liste de sources est un type spécial d’affichage Plan utilisé pour afficher la source d’une action, comme la barre latérale dans le Finder ou iTunes.

Exemple de liste de sources

Dans cet article, nous allons aborder les principes de base de l’utilisation des listes sources dans une application Xamarin.Mac. Il est fortement suggéré de commencer par l’article Hello, Mac , en particulier les sections Introduction to Xcode et Interface Builderet Outlets and Actions , car il couvre les concepts et techniques clés que nous allons utiliser dans cet article.

Vous pouvez également consulter la section Exposing C# classes/méthodes to Objective-C du document Xamarin.Mac Internals . Il explique les Register commandes et Export utilisées pour connecter vos classes C# à des objets et des éléments d’interface Objective-C utilisateur.

Présentation des listes sources

Comme indiqué ci-dessus, une liste de sources est un type spécial d’affichage Plan utilisé pour afficher la source d’une action, comme la barre latérale dans le Finder ou iTunes. Une liste source est un type de table qui permet à l’utilisateur de développer ou de réduire des lignes de données hiérarchiques. Contrairement à un affichage Table, les éléments d’une liste source ne sont pas dans une liste plate, ils sont organisés dans une hiérarchie, comme les fichiers et les dossiers sur un disque dur. Si un élément d’une liste de sources contient d’autres éléments, il peut être développé ou réduit par l’utilisateur.

La liste source est une vueNSOutlineView de plan de style (), qui est elle-même une sous-classe de l’affichage Table (NSTableView) et, par conséquent, hérite d’une grande partie de son comportement de sa classe parente. Par conséquent, de nombreuses opérations prises en charge par un mode Plan sont également prises en charge par une liste source. Une application Xamarin.Mac contrôle ces fonctionnalités et peut configurer les paramètres de la liste de sources (dans le code ou le Générateur d’interface) pour autoriser ou interdire certaines opérations.

Une liste de sources ne stocke pas ses propres données, mais s’appuie sur une source de données (NSOutlineViewDataSource) pour fournir à la fois les lignes et les colonnes requises, en fonction des besoins.

Le comportement d’une liste de sources peut être personnalisé en fournissant une sous-classe du délégué en mode Plan (NSOutlineViewDelegate) pour prendre en charge le type de plan pour sélectionner les fonctionnalités, la sélection et la modification d’élément, le suivi personnalisé et les vues personnalisées pour des éléments individuels.

Étant donné qu’une liste de sources partage une grande partie de son comportement et de ses fonctionnalités avec une vue Table et un mode Plan, vous pouvez consulter notre documentation vues table et vues de plan avant de continuer avec cet article.

Utilisation des listes sources

Une liste de sources est un type spécial d’affichage Plan utilisé pour afficher la source d’une action, comme la barre latérale dans le Finder ou iTunes. Contrairement aux vues hiérarchiques, avant de définir notre liste de sources dans Le Générateur d’interface, nous allons créer les classes de sauvegarde dans Xamarin.Mac.

Tout d’abord, nous allons créer une classe SourceListItem pour contenir les données de notre liste de sources. Dans le Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter un>nouveau fichier... Sélectionnez Général>Classe vide, entrez SourceListItem comme nom, puis cliquez sur le bouton Nouveau :

Ajout d’une classe vide

Faites en sorte que le SourceListItem.cs fichier ressemble à ce qui suit :

using System;
using System.Collections;
using System.Collections.Generic;
using AppKit;
using Foundation;

namespace MacOutlines
{
    public class SourceListItem: NSObject, IEnumerator, IEnumerable
    {
        #region Private Properties
        private string _title;
        private NSImage _icon;
        private string _tag;
        private List<SourceListItem> _items = new List<SourceListItem> ();
        #endregion

        #region Computed Properties
        public string Title {
            get { return _title; }
            set { _title = value; }
        }

        public NSImage Icon {
            get { return _icon; }
            set { _icon = value; }
        }

        public string Tag {
            get { return _tag; }
            set { _tag=value; }
        }
        #endregion

        #region Indexer
        public SourceListItem this[int index]
        {
            get
            {
                return _items[index];
            }

            set
            {
                _items[index] = value;
            }
        }

        public int Count {
            get { return _items.Count; }
        }

        public bool HasChildren {
            get { return (Count > 0); }
        }
        #endregion

        #region Enumerable Routines
        private int _position = -1;

        public IEnumerator GetEnumerator()
        {
            _position = -1;
            return (IEnumerator)this;
        }

        public bool MoveNext()
        {
            _position++;
            return (_position < _items.Count);
        }

        public void Reset()
        {_position = -1;}

        public object Current
        {
            get 
            { 
                try 
                {
                    return _items[_position];
                }

                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
        #endregion

        #region Constructors
        public SourceListItem ()
        {
        }

        public SourceListItem (string title)
        {
            // Initialize
            this._title = title;
        }

        public SourceListItem (string title, string icon)
        {
            // Initialize
            this._title = title;
            this._icon = NSImage.ImageNamed (icon);
        }

        public SourceListItem (string title, string icon, ClickedDelegate clicked)
        {
            // Initialize
            this._title = title;
            this._icon = NSImage.ImageNamed (icon);
            this.Clicked = clicked;
        }

        public SourceListItem (string title, NSImage icon)
        {
            // Initialize
            this._title = title;
            this._icon = icon;
        }

        public SourceListItem (string title, NSImage icon, ClickedDelegate clicked)
        {
            // Initialize
            this._title = title;
            this._icon = icon;
            this.Clicked = clicked;
        }

        public SourceListItem (string title, NSImage icon, string tag)
        {
            // Initialize
            this._title = title;
            this._icon = icon;
            this._tag = tag;
        }

        public SourceListItem (string title, NSImage icon, string tag, ClickedDelegate clicked)
        {
            // Initialize
            this._title = title;
            this._icon = icon;
            this._tag = tag;
            this.Clicked = clicked;
        }
        #endregion

        #region Public Methods
        public void AddItem(SourceListItem item) {
            _items.Add (item);
        }

        public void AddItem(string title) {
            _items.Add (new SourceListItem (title));
        }

        public void AddItem(string title, string icon) {
            _items.Add (new SourceListItem (title, icon));
        }

        public void AddItem(string title, string icon, ClickedDelegate clicked) {
            _items.Add (new SourceListItem (title, icon, clicked));
        }

        public void AddItem(string title, NSImage icon) {
            _items.Add (new SourceListItem (title, icon));
        }

        public void AddItem(string title, NSImage icon, ClickedDelegate clicked) {
            _items.Add (new SourceListItem (title, icon, clicked));
        }

        public void AddItem(string title, NSImage icon, string tag) {
            _items.Add (new SourceListItem (title, icon, tag));
        }

        public void AddItem(string title, NSImage icon, string tag, ClickedDelegate clicked) {
            _items.Add (new SourceListItem (title, icon, tag, clicked));
        }

        public void Insert(int n, SourceListItem item) {
            _items.Insert (n, item);
        }

        public void RemoveItem(SourceListItem item) {
            _items.Remove (item);
        }

        public void RemoveItem(int n) {
            _items.RemoveAt (n);
        }

        public void Clear() {
            _items.Clear ();
        }
        #endregion

        #region Events
        public delegate void ClickedDelegate();
        public event ClickedDelegate Clicked;

        internal void RaiseClickedEvent() {
            // Inform caller
            if (this.Clicked != null)
                this.Clicked ();
        }
        #endregion
    }
}

Dans le Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter un>nouveau fichier... Sélectionnez Classe vide générale>, entrez SourceListDataSource comme nom, puis cliquez sur le bouton Nouveau. Faites en sorte que le SourceListDataSource.cs fichier ressemble à ce qui suit :

using System;
using System.Collections;
using System.Collections.Generic;
using AppKit;
using Foundation;

namespace MacOutlines
{
    public class SourceListDataSource : NSOutlineViewDataSource
    {
        #region Private Variables
        private SourceListView _controller;
        #endregion

        #region Public Variables
        public List<SourceListItem> Items = new List<SourceListItem>();
        #endregion

        #region Constructors
        public SourceListDataSource (SourceListView controller)
        {
            // Initialize
            this._controller = controller;
        }
        #endregion

        #region Override Properties
        public override nint GetChildrenCount (NSOutlineView outlineView, Foundation.NSObject item)
        {
            if (item == null) {
                return Items.Count;
            } else {
                return ((SourceListItem)item).Count;
            }
        }

        public override bool ItemExpandable (NSOutlineView outlineView, Foundation.NSObject item)
        {
            return ((SourceListItem)item).HasChildren;
        }

        public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, Foundation.NSObject item)
        {
            if (item == null) {
                return Items [(int)childIndex];
            } else {
                return ((SourceListItem)item) [(int)childIndex]; 
            }
        }

        public override NSObject GetObjectValue (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
        {
            return new NSString (((SourceListItem)item).Title);
        }
        #endregion

        #region Internal Methods
        internal SourceListItem ItemForRow(int row) {
            int index = 0;

            // Look at each group
            foreach (SourceListItem item in Items) {
                // Is the row inside this group?
                if (row >= index && row <= (index + item.Count)) {
                    return item [row - index - 1];
                }

                // Move index
                index += item.Count + 1;
            }

            // Not found 
            return null;
        }
        #endregion
    }
}

Cela fournit les données de notre liste de sources.

Dans le Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter un>nouveau fichier... Sélectionnez Classe vide générale>, entrez SourceListDelegate comme nom, puis cliquez sur le bouton Nouveau. Faites en sorte que le SourceListDelegate.cs fichier ressemble à ce qui suit :

using System;
using AppKit;
using Foundation;

namespace MacOutlines
{
    public class SourceListDelegate : NSOutlineViewDelegate
    {
        #region Private variables
        private SourceListView _controller;
        #endregion

        #region Constructors
        public SourceListDelegate (SourceListView controller)
        {
            // Initialize
            this._controller = controller;
        }
        #endregion

        #region Override Methods
        public override bool ShouldEditTableColumn (NSOutlineView outlineView, NSTableColumn tableColumn, Foundation.NSObject item)
        {
            return false;
        }

        public override NSCell GetCell (NSOutlineView outlineView, NSTableColumn tableColumn, Foundation.NSObject item)
        {
            nint row = outlineView.RowForItem (item);
            return tableColumn.DataCellForRow (row);
        }

        public override bool IsGroupItem (NSOutlineView outlineView, Foundation.NSObject item)
        {
            return ((SourceListItem)item).HasChildren;
        }

        public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
        {
            NSTableCellView view = null;

            // Is this a group item?
            if (((SourceListItem)item).HasChildren) {
                view = (NSTableCellView)outlineView.MakeView ("HeaderCell", this);
            } else {
                view = (NSTableCellView)outlineView.MakeView ("DataCell", this);
                view.ImageView.Image = ((SourceListItem)item).Icon;
            }

            // Initialize view
            view.TextField.StringValue = ((SourceListItem)item).Title;

            // Return new view
            return view;
        }

        public override bool ShouldSelectItem (NSOutlineView outlineView, Foundation.NSObject item)
        {
            return (outlineView.GetParent (item) != null);
        }

        public override void SelectionDidChange (NSNotification notification)
        {
            NSIndexSet selectedIndexes = _controller.SelectedRows;

            // More than one item selected?
            if (selectedIndexes.Count > 1) {
                // Not handling this case
            } else {
                // Grab the item
                var item = _controller.Data.ItemForRow ((int)selectedIndexes.FirstIndex);

                // Was an item found?
                if (item != null) {
                    // Fire the clicked event for the item
                    item.RaiseClickedEvent ();

                    // Inform caller of selection
                    _controller.RaiseItemSelected (item);
                }
            }
        }
        #endregion
    }
}

Cela fournit le comportement de notre liste de sources.

Enfin, dans le Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter un>nouveau fichier... Sélectionnez Classe vide générale>, entrez SourceListView comme nom, puis cliquez sur le bouton Nouveau. Faites en sorte que le SourceListView.cs fichier ressemble à ce qui suit :

using System;
using AppKit;
using Foundation;

namespace MacOutlines
{
    [Register("SourceListView")]
    public class SourceListView : NSOutlineView
    {
        #region Computed Properties
        public SourceListDataSource Data {
            get {return (SourceListDataSource)this.DataSource; }
        }
        #endregion

        #region Constructors
        public SourceListView ()
        {

        }

        public SourceListView (IntPtr handle) : base(handle)
        {

        }

        public SourceListView (NSCoder coder) : base(coder)
        {

        }

        public SourceListView (NSObjectFlag t) : base(t)
        {

        }
        #endregion

        #region Override Methods
        public override void AwakeFromNib ()
        {
            base.AwakeFromNib ();
        }
        #endregion

        #region Public Methods
        public void Initialize() {

            // Initialize this instance
            this.DataSource = new SourceListDataSource (this);
            this.Delegate = new SourceListDelegate (this);

        }
        
        public void AddItem(SourceListItem item) {
            if (Data != null) {
                Data.Items.Add (item);
            }
        }
        #endregion

        #region Events
        public delegate void ItemSelectedDelegate(SourceListItem item);
        public event ItemSelectedDelegate ItemSelected;

        internal void RaiseItemSelected(SourceListItem item) {
            // Inform caller
            if (this.ItemSelected != null) {
                this.ItemSelected (item);
            }
        }
        #endregion
    }
}

Cela crée une sous-classe réutilisable personnalisée de NSOutlineView (SourceListView) que nous pouvons utiliser pour générer la liste des sources dans n’importe quelle application Xamarin.Mac que nous créons.

Création et maintenance de listes de sources dans Xcode

Maintenant, nous allons concevoir notre liste de sources dans Le Générateur d’interface. Double-cliquez sur le fichier pour l’ouvrir Main.storyboard pour le modifier dans le Générateur d’interface, puis faites glisser une vue fractionnée à partir de l’inspecteur de bibliothèque, ajoutez-le au contrôleur d’affichage et définissez-le pour le redimensionner avec l’affichage dans l’éditeur de contraintes :

Contraintes de modification dans le Générateur d’interface.

Ensuite, faites glisser une liste de sources à partir de l’inspecteur de bibliothèque, ajoutez-la sur le côté gauche de l’affichage fractionné et définissez-la pour la redimensionner avec l’affichage dans l’éditeur de contraintes :

Modification des contraintes en faisant glisser une liste de sources vers l’affichage fractionné.

Ensuite, basculez vers la vue d’identité, sélectionnez la liste des sources, puis remplacez la classe parSourceListView:

Définition du nom de la classe

Enfin, créez un outlet pour notre liste de sources appelé SourceList dans le ViewController.h fichier :

Configuration d’un point de vente

Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Remplissage de la liste des sources

Nous allons modifier le RotationWindow.cs fichier dans Visual Studio pour Mac et faire en sorte que sa AwakeFromNib méthode ressemble à ce qui suit :

public override void AwakeFromNib ()
{
    base.AwakeFromNib ();

    // Populate source list
    SourceList.Initialize ();

    var library = new SourceListItem ("Library");
    library.AddItem ("Venues", "house.png", () => {
        Console.WriteLine("Venue Selected");
    });
    library.AddItem ("Singers", "group.png");
    library.AddItem ("Genre", "cards.png");
    library.AddItem ("Publishers", "box.png");
    library.AddItem ("Artist", "person.png");
    library.AddItem ("Music", "album.png");
    SourceList.AddItem (library);

    // Add Rotation 
    var rotation = new SourceListItem ("Rotation"); 
    rotation.AddItem ("View Rotation", "redo.png");
    SourceList.AddItem (rotation);

    // Add Kiosks
    var kiosks = new SourceListItem ("Kiosks");
    kiosks.AddItem ("Sign-in Station 1", "imac");
    kiosks.AddItem ("Sign-in Station 2", "ipad");
    SourceList.AddItem (kiosks);

    // Display side list
    SourceList.ReloadData ();
    SourceList.ExpandItem (null, true);

}

La Initialize () méthode doit être appelée par rapport au point de vente de notre liste de sources avant d’y ajouter des éléments. Pour chaque groupe d’éléments, nous créons un élément parent, puis ajoutons les sous-éléments à cet élément de groupe. Chaque groupe est ensuite ajouté à la collection SourceList.AddItem (...)de la liste de sources . Les deux dernières lignes chargent les données de la liste source et développent tous les groupes :

// Display side list
SourceList.ReloadData ();
SourceList.ExpandItem (null, true);

Enfin, modifiez le AppDelegate.cs fichier et faites en sorte que la DidFinishLaunching méthode ressemble à ce qui suit :

public override void DidFinishLaunching (NSNotification notification)
{
    mainWindowController = new MainWindowController ();
    mainWindowController.Window.MakeKeyAndOrderFront (this);

    var rotation = new RotationWindowController ();
    rotation.Window.MakeKeyAndOrderFront (this);
}

Si nous exécutons notre application, les éléments suivants s’affichent :

Exemple d’exécution d’application

Résumé

Cet article a examiné en détail l’utilisation des listes sources dans une application Xamarin.Mac. Nous avons vu comment créer et gérer des listes de sources dans le Générateur d’interface de Xcode et comment utiliser des listes de sources dans du code C#.