Udostępnij za pośrednictwem


Listy źródłowe na platformie Xamarin.Mac

W tym artykule opisano pracę z listami źródłowymi w aplikacji platformy Xamarin.Mac. W tym artykule opisano tworzenie i konserwowanie list źródłowych w programie Xcode i narzędziu Interface Builder oraz interakcję z nimi w kodzie języka C#.

Podczas pracy z językami C# i .NET w aplikacji platformy Xamarin.Mac masz dostęp do tych samych list źródłowych, w Objective-C których pracuje deweloper i czy program Xcode . Ponieważ platforma Xamarin.Mac integruje się bezpośrednio z programem Xcode, możesz użyć narzędzia Interface Builder środowiska Xcode do tworzenia i obsługi list źródłowych (lub opcjonalnie tworzenia ich bezpośrednio w kodzie języka C#).

Lista źródłowa to specjalny typ widoku konspektu używany do wyświetlania źródła akcji, takiego jak pasek boczny w programie Finder lub iTunes.

Przykładowa lista źródłowa

W tym artykule omówimy podstawy pracy z listami źródłowymi w aplikacji platformy Xamarin.Mac. Zdecydowanie zaleca się, aby najpierw zapoznać się z artykułem Hello, Mac , w szczególności wprowadzenie do narzędzi Xcode i Interface Builder i Outlet and Actions , ponieważ obejmuje ona kluczowe pojęcia i techniki, których będziemy używać w tym artykule.

Warto zapoznać się również z sekcją Uwidacznianie klas/ metod Objective-C języka C# w dokumencie Xamarin.Mac Internals . Register Objaśnienie poleceń i Export używanych do podłączania klas języka C# do Objective-C obiektów i elementów interfejsu użytkownika.

Wprowadzenie do list źródłowych

Jak wspomniano powyżej, lista źródłowa jest specjalnym typem widoku konspektu używanym do wyświetlania źródła akcji, na przykład paska bocznego w programie Finder lub iTunes. Lista źródłowa to typ tabeli, który umożliwia użytkownikowi rozwijanie lub zwijanie wierszy danych hierarchicznych. W przeciwieństwie do widoku tabeli elementy na liście źródłowej nie znajdują się na płaskiej liście, są zorganizowane w hierarchii, takich jak pliki i foldery na dysku twardym. Jeśli element na liście źródłowej zawiera inne elementy, można go rozwinąć lub zwinąć przez użytkownika.

Lista źródłowa jest specjalnie stylizowanym widokiem konspektu (NSOutlineView), który sam jest podklasą widoku tabeli (NSTableView), a w związku z tym dziedziczy znaczną część swojego zachowania z klasy nadrzędnej. W związku z tym wiele operacji obsługiwanych przez widok konspektu jest również obsługiwanych przez listę źródłową. Aplikacja Xamarin.Mac ma kontrolę nad tymi funkcjami i może skonfigurować parametry listy źródłowej (w kodzie lub konstruktorze interfejsu), aby zezwalać na niektóre operacje lub nie zezwalać na nie.

Lista źródłowa nie przechowuje własnych danych, a zamiast tego opiera się na źródle danych (NSOutlineViewDataSource), aby zapewnić wymagane zarówno wiersze, jak i kolumny zgodnie z potrzebami.

Zachowanie listy źródłowej można dostosować, udostępniając podklasę delegata widoku konspektu (NSOutlineViewDelegate), aby obsługiwać typ konspektu w celu wybrania funkcji, zaznaczenia i edytowania elementów, śledzenia niestandardowego i widoków niestandardowych dla poszczególnych elementów.

Ponieważ lista źródłowa udostępnia większość jej zachowań i funkcji w widoku tabeli i widoku konspektu, warto zapoznać się z naszą dokumentacją widoków tabel i widoków konspektu przed kontynuowaniem tego artykułu.

Praca z listami źródłowymi

Lista źródłowa to specjalny typ widoku konspektu używany do wyświetlania źródła akcji, takiego jak pasek boczny w programie Finder lub iTunes. W przeciwieństwie do widoków konspektu, zanim zdefiniujemy naszą listę źródłową w narzędziu Interface Builder, utwórzmy klasy zapasowe na platformie Xamarin.Mac.

Najpierw utwórzmy nową SourceListItem klasę do przechowywania danych dla naszej listy źródłowej. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj>nowy plik... Wybierz pozycję Ogólna>Pusta klasa, wprowadź SourceListItem nazwęi kliknij przycisk Nowy:

Dodawanie pustej klasy

SourceListItem.cs Utwórz plik w następujący sposób:

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

W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj>nowy plik... Wybierz pozycję Ogólna>Pusta klasa, wprowadź SourceListDataSource nazwęi kliknij przycisk Nowy. SourceListDataSource.cs Utwórz plik w następujący sposób:

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

Zapewni to dane dla naszej listy źródłowej.

W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj>nowy plik... Wybierz pozycję Ogólna>Pusta klasa, wprowadź SourceListDelegate nazwęi kliknij przycisk Nowy. SourceListDelegate.cs Utwórz plik w następujący sposób:

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

Zapewni to zachowanie naszej listy źródłowej.

Na koniec w Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt i wybierz polecenie Dodaj>nowy plik... Wybierz pozycję Ogólna>Pusta klasa, wprowadź SourceListView nazwęi kliknij przycisk Nowy. SourceListView.cs Utwórz plik w następujący sposób:

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

Spowoduje to utworzenie niestandardowej podklasy wielokrotnego NSOutlineView użytku (SourceListView), której możemy użyć do kierowania listą źródłową w dowolnej aplikacji platformy Xamarin.Mac, którą tworzymy.

Tworzenie i obsługa list źródłowych w programie Xcode

Teraz zaprojektujmy naszą listę źródłową w narzędziu Interface Builder. Kliknij dwukrotnie plik, aby otworzyć go do edycji w narzędziu Main.storyboard Interface Builder i przeciągnij widok podzielony z Inspektora biblioteki, dodaj go do kontrolera widoku i ustaw jego rozmiar za pomocą widoku w Edytorze ograniczeń:

Edytowanie ograniczeń w narzędziu Interface Builder.

Następnie przeciągnij listę źródłową z Inspektora biblioteki, dodaj ją do lewej strony widoku podziału i ustaw jej rozmiar na widok w Edytorze ograniczeń:

Edytowanie ograniczeń przez przeciągnięcie listy źródłowej do widoku podzielonego.

Następnie przejdź do widoku tożsamości, wybierz listę źródłową i zmień ją na Klasa na SourceListView:

Ustawianie nazwy klasy

Na koniec utwórz punkt wylotowy dla naszej listy źródłowej ViewController.h o nazwie SourceList w pliku :

Konfigurowanie gniazda

Zapisz zmiany i wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację z programem Xcode.

Wypełnianie listy źródłowej

Zmodyfikujmy RotationWindow.cs plik w Visual Studio dla komputerów Mac i ustawmyAwakeFromNib, że jest to metoda podobna do następującej:

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

}

Przed Initialize () dodaniu do niej wszystkich elementów należy wywołać metodę ujścia listy źródłowej. Dla każdej grupy elementów utworzymy element nadrzędny, a następnie dodamy elementy podrzędne do tego elementu grupy. Każda grupa jest następnie dodawana do kolekcji SourceList.AddItem (...)listy źródłowej . Ostatnie dwa wiersze ładują dane dla listy źródłowej i rozszerza wszystkie grupy:

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

Na koniec zmodyfikuj AppDelegate.cs plik i utwórz metodę DidFinishLaunching podobną do następującej:

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

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

Jeśli uruchomimy aplikację, zostaną wyświetlone następujące elementy:

Uruchamianie przykładowej aplikacji

Podsumowanie

W tym artykule szczegółowo przedstawiono pracę z listami źródłowymi w aplikacji platformy Xamarin.Mac. Zobaczyliśmy, jak tworzyć i obsługiwać listy źródłowe w narzędziu Interface Builder programu Xcode oraz jak pracować z listami źródłowymi w kodzie języka C#.