Zdrojové seznamy v Xamarin.Macu

Tento článek popisuje práci se zdrojovými seznamy v aplikaci Xamarin.Mac. Popisuje vytváření a správu zdrojových seznamů v Xcode a Interface Builder a interakci s nimi v kódu C#.

Při práci s C# a .NET v aplikaci Xamarin.Mac máte přístup ke stejným zdrojovým seznamům jako vývojář pracující v aplikaci a Objective-CObjective-C Vzhledem k tomu, že Xamarin.Mac se integruje přímo s Xcode, můžete použít Interface Builder Xcode k vytvoření a údržbě zdrojových seznamů (nebo je volitelně vytvořit přímo v kódu jazyka C#).

Zdrojový seznam je speciální typ zobrazení obrysu, který slouží k zobrazení zdroje akce, jako je boční panel ve Finderu nebo iTunes.

Příklad seznamu zdrojů

V tomto článku se budeme seznamovat se základy práce se zdrojovými seznamy v aplikaci Xamarin.Mac. Důrazně doporučujeme, abyste si nejprve prošli článek Hello, Mac, konkrétně oddíly Úvod do Xcode a Interface Builder a Výstupy a Akce, protože se zabývá klíčovými koncepty a technikami, které budeme v tomto článku používat.

Možná se budete chtít podívat i na část dokumentu Exposing C# classes / methods to Objective-CExposing C# classes / methods to Objective-C která vysvětluje příkazy a používané k připojení tříd jazyka C# k objektům a prvkům uživatelského RegisterExportObjective-C rozhraní.

Úvod do zdrojových seznamů

Jak je uvedeno výše, zdrojový seznam je speciální typ zobrazení obrysu, který slouží k zobrazení zdroje akce, jako je boční panel ve Finderu nebo iTunes. Zdrojový seznam je typ Tabulky, který uživateli umožňuje rozbalit nebo sbalit řádky hierarchických dat. Na rozdíl od zobrazení tabulky nejsou položky ve zdrojovém seznamu v plochém seznamu, jsou uspořádané v hierarchii, jako jsou soubory a složky na pevném disku. Pokud položka ve zdrojovém seznamu obsahuje další položky, může ji uživatel rozbalit nebo sbalit.

Zdrojový seznam je speciálně seřazené zobrazení obrysu ( ), které je samo o sobě podtřídou zobrazení tabulky ( ), a proto dědí velkou část svého chování ze své NSOutlineViewNSTableView nadřazené třídy. V důsledku toho seznam zdrojů podporuje mnoho operací podporovaných zobrazením osnovy. Aplikace Xamarin.Mac má nad těmito funkcemi kontrolu a může nakonfigurovat parametry zdrojového seznamu (buď v kódu, nebo Interface Builder), aby se určité operace povolují nebo zak povolují.

Zdrojový seznam neukládá vlastní data, místo toho spoléhá na zdroj dat ( ) a podle potřeby poskytuje požadované řádky i NSOutlineViewDataSource sloupce.

Chování zdrojového seznamu lze přizpůsobit poskytnutím podtřídy delegáta zobrazení osnovy ( ) pro podporu typu obrysu pro výběr funkcí, výběru a úprav položek, vlastního sledování a vlastních zobrazení pro NSOutlineViewDelegate jednotlivé položky.

Vzhledem k tomu, že seznam zdrojů sdílí velkou část jeho chování a funkcí se zobrazením tabulky a zobrazením osnovy, možná budete chtít před pokračováním v tomto článku projít naši dokumentaci Zobrazení tabulek a Zobrazení osnov.

Práce se zdrojovými seznamy

Zdrojový seznam je speciální typ zobrazení obrysu, který slouží k zobrazení zdroje akce, jako je boční panel ve Finderu nebo iTunes. Na rozdíl od zobrazení obrysů si před definováním zdrojového seznamu Interface Builder vytvoříme v Xamarin.Macu základní třídy.

Nejprve vytvoříme novou SourceListItem třídu, která bude obsahovat data pro náš seznam zdrojů. V Průzkumník řešeníklikněte pravým tlačítkem na ikonu Project vyberte Přidatnový soubor... Vyberte Obecná prázdná třída, jako Název zadejte a klikněte na tlačítko Nová:

Přidání prázdné třídy

Aby SourceListItem.cs soubor vypadal takto:

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

V Průzkumník řešeníklikněte pravým tlačítkem na ikonu Project vyberte Přidatnový soubor... Vyberte Obecná prázdná třída, jako Název zadejte a klikněte na tlačítko Nová. Aby SourceListDataSource.cs soubor vypadal takto:

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

Tím poskytnete data pro náš seznam zdrojů.

V Průzkumník řešeníklikněte pravým tlačítkem na ikonu Project vyberte Přidatnový soubor... Vyberte Obecná prázdná třída, jako Název zadejte a klikněte na tlačítko Nová. Aby SourceListDelegate.cs soubor vypadal takto:

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

Tím se zajistí chování našeho seznamu zdrojů.

Nakonec v okně Průzkumník řešeníklikněte pravým tlačítkem na Project a vyberte Přidatnový soubor... Vyberte Obecná prázdná třída, jako Název zadejte a klikněte na tlačítko Nová. Aby SourceListView.cs soubor vypadal takto:

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

Tím se vytvoří vlastní, znovu použitelný podtřída ( ), kterou můžeme použít k řízení zdrojového seznamu v libovolné aplikaci NSOutlineViewSourceListView Xamarin.Mac, kterou vytvoříme.

Vytváření a údržba zdrojových seznamů v Xcode

Teď navrhněme seznam zdrojů v Interface Builder. Poklikejte na soubor a otevřete ho pro úpravy v nástroji Interface Builder a přetáhněte rozdělené zobrazení z inspektoru knihovny, přidejte ho do kontroleru zobrazení a nastavte jeho velikost pomocí zobrazení v editoru Main.storyboardomezení:Main.storyboard

Úprava omezení v Interface Builder.

V dalším kroku přetáhněte zdrojový seznam z inspektoru knihovny,přidejte ho na levou stranu Rozdělené zobrazení a nastavte jeho změnu velikosti pomocí zobrazení v editoru omezení:

Úpravy omezení přetažením zdrojového seznamu do rozdělených zobrazení

Pak přepněte do zobrazení identity,vyberte seznam zdrojů a změňte jeho třídu na :

Nastavení názvu třídy

Nakonec v souboru vytvořte výstup pro zdrojový seznam s názvem ViewController.h :

Konfigurace výstupu

Uložte změny a vraťte se do Visual Studio pro Mac synchronizaci s Xcode.

Naplnění zdrojového seznamu

Upravte soubor v souboru Visual Studio pro Mac aby jeho metoda RotationWindow.csAwakeFromNib vypadala takto:

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

}

Před přidáním položek do výstupu našeho seznamu zdrojů je potřeba volat Initialize () metodu . Initialize () Pro každou skupinu položek vytvoříme nadřazenou položku a pak do této položky skupiny přidáme dílčí položky. Každá skupina se pak přidá do kolekce zdrojového seznamu SourceList.AddItem (...) . Poslední dva řádky načtou data pro seznam zdrojů a rozbalí všechny skupiny:

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

Nakonec upravte AppDelegate.cs soubor a na zajistěte, aby DidFinishLaunching metoda vypadala takto:

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

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

Pokud aplikaci spustíme, zobrazí se toto:

Příklad spuštění aplikace

Souhrn

Tento článek podrobně popisuje práci se zdrojovými seznamy v aplikaci Xamarin.Mac. Viděli jsme, jak vytvářet a udržovat zdrojové seznamy v nástroji Xcode Interface Builder jak pracovat se zdrojovými seznamy v kódu jazyka C#.