Xamarin.Mac의 원본 목록

이 문서에서는 Xamarin.Mac 애플리케이션의 원본 목록 작업에 대해 설명합니다. Xcode 및 Interface Builder에서 원본 목록을 만들고 기본 C# 코드에서 상호 작용하는 방법에 대해 설명합니다.

Xamarin.Mac 애플리케이션에서 C# 및 .NET을 사용하는 경우 개발자가 작업하고 Xcode에서 Objective-C 수행하는 것과 동일한 원본 목록에 액세스할 수 있습니다. Xamarin.Mac은 Xcode와 직접 통합되므로 Xcode의 인터페이스 작성기를 사용하여 원본 목록을 만들고 기본(또는 필요에 따라 C# 코드로 직접 만들 수 있습니다).

원본 목록은 Finder 또는 iTunes의 사이드바와 같이 작업의 원본을 표시하는 데 사용되는 특수한 유형의 개요 보기입니다.

An example source list

이 문서에서는 Xamarin.Mac 애플리케이션에서 원본 목록을 사용하는 기본 사항을 설명합니다. 이 문서에서 사용할 주요 개념과 기술을 다루므로 Hello, Mac 문서, 특히 Xcode 및 인터페이스 작성기 및 콘센트 및 작업 소개 섹션을 통해 작업하는 것이 좋습니다.

Xamarin.Mac Internals 문서의 섹션에 Objective-C 대한 C# 클래스/메서드 노출을 살펴보고, Register C# 클래스 Objective-C 를 개체 및 UI 요소에 연결하는 데 사용되는 명령과 Export 설명합니다.

원본 목록 소개

위에서 설명한 것처럼 원본 목록은 Finder 또는 iTunes의 사이드바와 같이 작업의 원본을 표시하는 데 사용되는 특수한 유형의 개요 보기입니다. 원본 목록은 사용자가 계층적 데이터의 행을 확장하거나 축소할 수 있는 테이블 형식입니다. 테이블 뷰와 달리 원본 목록의 항목은 플랫 목록에 있지 않으며 하드 드라이브의 파일 및 폴더와 같은 계층 구조로 구성됩니다. 원본 목록의 항목에 다른 항목이 포함된 경우 사용자가 해당 항목을 확장하거나 축소할 수 있습니다.

원본 목록은 특별히 스타일이 지정된 개요 뷰(NSOutlineView)이며, 테이블 뷰(NSTableView)의 하위 클래스이므로 부모 클래스에서 대부분의 동작을 상속합니다. 따라서 개요 보기에서 지원하는 많은 작업도 원본 목록에서 지원됩니다. Xamarin.Mac 애플리케이션은 이러한 기능을 제어하며 특정 작업을 허용하거나 허용하지 않도록 소스 목록의 매개 변수(코드 또는 인터페이스 작성기)를 구성할 수 있습니다.

원본 목록은 자체 데이터를 저장하지 않고 필요에 따라 필요한 행과 열을 모두 제공하기 위해 데이터 원본(NSOutlineViewDataSource)에 의존합니다.

개요 유형에서 기능, 항목 선택 및 편집, 사용자 지정 추적 및 개별 항목에 대한 사용자 지정 보기를 선택할 수 있도록 개요 보기 대리자(NSOutlineViewDelegate)의 하위 클래스를 제공하여 원본 목록의 동작을 사용자 지정할 수 있습니다.

원본 목록은 테이블 뷰 및 개요 뷰와 대부분의 동작 및 기능을 공유하므로 이 문서를 계속 진행하기 전에 테이블 뷰개요 뷰 설명서를 살펴보는 것이 좋습니다.

원본 목록 작업

원본 목록은 Finder 또는 iTunes의 사이드바와 같이 작업의 원본을 표시하는 데 사용되는 특수한 유형의 개요 보기입니다. 개요 뷰와 달리 인터페이스 작성기에서 원본 목록을 정의하기 전에 Xamarin.Mac에서 지원 클래스를 만들어 보겠습니다.

먼저 원본 목록에 대한 데이터를 저장할 새 SourceListItem 클래스를 만들어 보겠습니다. 솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>를 선택합니다. 일반>빈 클래스선택하고 이름을 입력 SourceListItem 한 다음 새로 만들기 단추를 클릭합니다.

Adding an empty class

SourceListItem.cs 파일을 다음과 같이 표시합니다.

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

솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>를 선택합니다. 일반>빈 클래스선택하고 이름을 입력 SourceListDataSource 한 다음 새로 만들기 단추를 클릭합니다. SourceListDataSource.cs 파일을 다음과 같이 표시합니다.

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

그러면 원본 목록에 대한 데이터가 표시됩니다.

솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>를 선택합니다. 일반>빈 클래스선택하고 이름을 입력 SourceListDelegate 한 다음 새로 만들기 단추를 클릭합니다. SourceListDelegate.cs 파일을 다음과 같이 표시합니다.

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

이렇게 하면 원본 목록의 동작이 표시됩니다.

마지막으로 솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>를 선택합니다. 일반>빈 클래스선택하고 이름을 입력 SourceListView 한 다음 새로 만들기 단추를 클릭합니다. SourceListView.cs 파일을 다음과 같이 표시합니다.

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

이렇게 하면 만드는 모든 Xamarin.Mac 애플리케이션에서 원본 목록을 구동하는 데 사용할 수 있는 (SourceListView)의 NSOutlineView 사용자 지정 재사용 가능한 하위 클래스가 만들어집니다.

Xcode에서 원본 목록 만들기 및 유지 관리

이제 인터페이스 작성기에서 원본 목록을 디자인해 보겠습니다. 인터페이스 작성기에서 편집할 파일을 두 번 클릭하고 Main.storyboard 라이브러리 검사기에서 분할 보기를 끌어서 보기 컨트롤러에 추가하고 제약 조건 편집기의 보기로 크기를 조정하도록 설정합니다.

Editing constraints in the Interface Builder.

그런 다음, 라이브러리 검사기에서 원본 목록을 끌어 분할 보기의 왼쪽에 추가하고 제약 조건 편집기의 보기로 크기를 조정하도록 설정합니다.

Editing constraints by dragging a Source List to the Split View.

다음으로 ID 보기로 전환하고 원본 목록을 선택한 다음 클래스다음으로 SourceListView변경합니다.

Setting the class name

마지막으로, 파일에서 호출 SourceList원본 목록에 대한 출선 만들기ViewController.h:

Configuring an Outlet

변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.

원본 목록 채우기

Mac용 Visual Studio 파일을 편집 RotationWindow.cs 하고 메서드를 AwakeFromNib 다음과 같이 만들어 보겠습니다.

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

}

항목이 Initialize () 추가되기 전에 소스 목록의 콘센트에 대해 메서드를 호출해야 합니다. 각 항목 그룹에 대해 부모 항목을 만든 다음 해당 그룹 항목에 하위 항목을 추가합니다. 그런 다음 각 그룹이 원본 목록의 컬렉션 SourceList.AddItem (...)에 추가됩니다. 마지막 두 줄은 원본 목록에 대한 데이터를 로드하고 모든 그룹을 확장합니다.

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

마지막으로 파일을 편집 AppDelegate.cs 하고 메서드를 DidFinishLaunching 다음과 같이 만듭니다.

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

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

애플리케이션을 실행하는 경우 다음이 표시됩니다.

An example app run

요약

이 문서에서는 Xamarin.Mac 애플리케이션에서 원본 목록 작업을 자세히 살펴보았습니다. Xcode의 인터페이스 작성기에서 원본 목록을 만들고 기본 방법 및 C# 코드에서 원본 목록을 작업하는 방법을 알아보았습니다.