Xamarin.Mac의 테이블 뷰
이 문서에서는 Xamarin.Mac 애플리케이션의 테이블 뷰 작업에 대해 설명합니다. Xcode 및 Interface Builder에서 테이블 뷰를 만들고 코드에서 테이블 뷰와 상호 작용하는 방법에 대해 설명합니다.
Xamarin.Mac 애플리케이션에서 C# 및 .NET을 사용하는 경우 개발자가 작업하고 Xcode에서 수행하는 것과 동일한 테이블 뷰에 Objective-C 액세스할 수 있습니다. Xamarin.Mac은 Xcode와 직접 통합되므로 Xcode의 인터페이스 작성기를 사용하여 테이블 뷰를 만들고 유지 관리하거나 필요에 따라 C# 코드에서 직접 만들 수 있습니다.
테이블 뷰는 여러 행에 하나 이상의 정보 열이 포함된 테이블 형식으로 데이터를 표시합니다. 만들 테이블 뷰의 형식에 따라 사용자는 열을 기준으로 정렬하거나, 열을 다시 구성하고, 열을 추가하거나, 열을 제거하거나, 테이블에 포함된 데이터를 편집할 수 있습니다.
이 문서에서는 Xamarin.Mac 애플리케이션에서 테이블 뷰를 사용하는 기본 사항을 설명합니다. 이 문서에서 사용할 주요 개념과 기술을 다루므로 Hello, Mac 문서, 특히 Xcode 및 인터페이스 작성기 및 콘센트 및 작업 소개 섹션을 통해 작업하는 것이 좋습니다.
Xamarin.Mac Internals 문서의 섹션에 Objective-C 대한 C# 클래스/메서드 노출을 살펴보고, Register
C# 클래스 Objective-C 를 개체 및 UI 요소에 연결하는 데 사용되는 명령과 Export
설명합니다.
테이블 뷰 소개
테이블 뷰는 여러 행에 하나 이상의 정보 열이 포함된 테이블 형식으로 데이터를 표시합니다. 테이블 뷰는 스크롤 보기(NSScrollView
) 내부에 표시되며 macOS 10.7부터 셀(NSCell
) 대신 모든 NSView
뷰를 사용하여 행과 열을 모두 표시할 수 있습니다. 즉, 여전히 사용할 NSCell
수 있지만 일반적으로 서브클래스를 NSTableCellView
수행하고 사용자 지정 행과 열을 만듭니다.
테이블 뷰는 자체 데이터를 저장하지 않고 필요에 따라 필요한 행과 열을 모두 제공하기 위해 데이터 원본(NSTableViewDataSource
)을 사용합니다.
테이블 뷰의 동작은 테이블 열 관리를 지원하기 위해 테이블 뷰 대리자(NSTableViewDelegate
)의 하위 클래스를 제공하여 사용자 지정할 수 있으며, 입력하여 기능 선택, 행 선택 및 편집, 사용자 지정 추적 및 개별 열 및 행에 대한 사용자 지정 보기를 선택할 수 있습니다.
테이블 뷰를 만들 때 Apple은 다음을 제안합니다.
- 사용자가 열 머리글을 클릭하여 테이블을 정렬하도록 허용합니다.
- 해당 열에 표시되는 데이터를 설명하는 명사 또는 짧은 명사 구인 열 머리글을 만듭니다.
자세한 내용은 Apple OS X 휴먼 인터페이스 지침의 콘텐츠 뷰 섹션을 참조하세요.
Xcode에서 테이블 뷰 만들기 및 유지 관리
새 Xamarin.Mac Cocoa 애플리케이션을 만들 때 기본적으로 표준 빈 창이 표시됩니다. 이 창은 프로젝트에 자동으로 포함된 파일에 정의 .storyboard
됩니다. 창 디자인을 편집하려면 솔루션 탐색기 파일을 두 번 클릭합니다Main.storyboard
.
그러면 Xcode의 인터페이스 작성기에서 창 디자인이 열립니다.
테이블 뷰 컨트롤을 더 쉽게 찾을 수 있도록 라이브러리 검사기 검색 상자에 입력 table
합니다.
테이블 뷰를 인터페이스 편집기의 뷰 컨트롤러로 끌어와 보기 컨트롤러의 콘텐츠 영역을 채우고 제약 조건 편집기의 창에서 축소 및 증가되는 위치로 설정합니다.
인터페이스 계층에서 테이블 뷰를 선택하면 특성 검사기에서 다음 속성을 사용할 수 있습니다.
- 콘텐츠 모드 - 뷰() 또는 셀(
NSView
NSCell
)을 사용하여 행과 열에 데이터를 표시할 수 있습니다. macOS 10.7부터 보기를 사용해야 합니다. - Floats 그룹 행 - 테이블
true
뷰에서 그룹화된 셀을 부동 셀처럼 그립니다. - 열 - 표시되는 열 수를 정의합니다.
- 헤더 - 열에 헤더가 있는 경우
true
- 다시 정렬 - 이 경우
true
사용자는 테이블의 열 순서를 다시 끌 수 있습니다. - 크기 조정 - 사용자가
true
열 머리글을 끌어 열 크기를 조정할 수 있습니다. - 열 크기 조정 - 테이블이 열 크기를 자동으로 조정하는 방법을 제어합니다.
- 강조 표시 - 셀을 선택할 때 표에서 사용하는 강조 표시 유형을 제어합니다.
- 대체 행 - 다른 행에 다른 배경색이 있는 경우
true
- 가로 눈금 - 셀 사이에 가로로 그려지는 테두리 유형을 선택합니다.
- 세로 눈금 - 셀 사이에 세로로 그려지는 테두리 유형을 선택합니다.
- 눈금 색 - 셀 테두리 색을 설정합니다.
- 배경 - 셀 배경색을 설정합니다.
- 선택 - 사용자가 표에서 셀을 다음과 같이 선택하는 방법을 제어할 수 있습니다.
- 다중 - 사용자가 여러 행과 열을 선택할 수 있는 경우
true
- 열 - 사용자가 열을 선택할 수 있는 경우
true
- Select 입력 - 사용자가 행을 선택할 문자를 입력할 수 있는 경우
true
- 비어 있음 - 사용자가 행 또는 열을 선택할 필요가 없는 경우
true
표에서 선택을 전혀 할 수 없습니다.
- 다중 - 사용자가 여러 행과 열을 선택할 수 있는 경우
- 자동 저장 - 테이블 형식이 자동으로 저장되는 이름입니다.
- 열 정보 - 열의 순서와 너비가 자동으로 저장되는 경우
true
- 줄 바꿈 - 셀에서 줄 바꿈을 처리하는 방법을 선택합니다.
- 마지막 표시줄 자르기 - 데이터가 잘리면
true
셀이 범위 내에 맞지 않습니다.
Important
레거시 Xamarin.Mac 애플리케이션 NSView
을 유지 관리하지 않는 한 기반 테이블 뷰를 기반으로 테이블 뷰를 NSCell
사용해야 합니다. NSCell
는 레거시로 간주되며 앞으로 지원되지 않을 수 있습니다.
인터페이스 계층 구조에서 테이블 열을 선택하면 특성 검사기에서 다음 속성을 사용할 수 있습니다.
- 제목 - 열의 제목을 설정합니다.
- 맞춤 - 셀 내의 텍스트 맞춤을 설정합니다.
- 제목 글꼴 - 셀의 머리글 텍스트에 대한 글꼴을 선택합니다.
- 정렬 키 - 열의 데이터를 정렬하는 데 사용되는 키입니다. 사용자가 이 열을 정렬할 수 없는 경우 비워 둡니다.
- 선택기 - 정렬을 수행하는 데 사용되는 작업 입니다. 사용자가 이 열을 정렬할 수 없는 경우 비워 둡니다.
- 순서 - 열 데이터의 정렬 순서입니다.
- 크기 조정 - 열의 크기 조정 유형을 선택합니다.
- 편집 가능 - 사용자가 셀 기반 테이블의 셀을 편집할 수 있는 경우
true
- Hidden - 열이 숨겨진 경우
true
열의 핸들을 왼쪽이나 오른쪽으로 세로로 가운데로 끌어 열의 크기를 조정할 수도 있습니다.
테이블 뷰에서 각 열을 선택하고 첫 번째 열에 제목 Product
과 두 번째 Details
열을 지정해 보겠습니다.
인터페이스 계층 구조에서 테이블 셀 뷰(NSTableViewCell
)를 선택하면 특성 검사기에서 다음 속성을 사용할 수 있습니다.
이러한 속성은 모두 표준 뷰의 속성입니다. 또한 여기에서 이 열의 행 크기를 조정할 수 있습니다.
인터페이스 계층 구조에서 테이블 뷰 셀(기본적으로 aNSTextField
)을 선택하면 특성 검사기에서 다음 속성을 사용할 수 있습니다.
여기에 설정할 표준 텍스트 필드의 모든 속성이 있습니다. 기본적으로 표준 텍스트 필드는 열의 셀에 대한 데이터를 표시하는 데 사용됩니다.
인터페이스 계층 구조에서 테이블 셀 뷰(NSTableFieldCell
)를 선택하면 특성 검사기에서 다음 속성을 사용할 수 있습니다.
여기서 가장 중요한 설정은 다음과 같습니다.
- 레이아웃 - 이 열의 셀을 배치하는 방법을 선택합니다.
- 한 줄 모드 사용 - 셀이 한 줄로 제한되는 경우
true
- 첫 번째 런타임 레이아웃 너비 - 애플리케이션
true
이 처음 실행될 때 셀에서 너비 집합을 수동으로 또는 자동으로 설정하는 것을 선호합니다. - 작업 - 셀에 대한 편집 동작 이 전송되는 시기를 제어합니다.
- 동작 - 셀을 선택할 수 있는지 편집 가능한지 정의합니다.
- 서식 있는 텍스트 - 셀
true
에 서식이 지정된 텍스트와 스타일이 지정된 텍스트가 표시될 수 있습니다. - 실행 취소 - 셀이 실행 취소 동작에 대한 책임을 맡는 경우
true
인터페이스 계층 구조의 테이블 열 아래쪽에 있는 표 셀 뷰(NSTableFieldCell
)를 선택합니다.
이렇게 하면 지정된 열에 대해 만든 모든 셀의 기본 패턴 으로 사용되는 표 셀 뷰를 편집할 수 있습니다.
작업 및 콘센트 추가
다른 Cocoa UI 컨트롤과 마찬가지로 테이블 뷰를 노출해야 하며 작업 및 출선(필요한 기능에 따라)을 사용하여 C# 코드에 열과 셀을 표시해야 합니다.
이 프로세스는 노출하려는 테이블 뷰 요소에 대해 동일합니다.
도우미 편집기로 전환하고 파일이 선택되어 있는지 확인
ViewController.h
합니다.인터페이스 계층 구조에서 테이블 보기를 선택하고 컨트롤을 클릭하고 파일로
ViewController.h
끕니다.다음과 같은 테이블 뷰에 대한 콘센트를 만듭니다
ProductTable
.테이블 열에 대한 출선도 만들고 다음과 같이 호출
ProductColumn
합니다DetailsColumn
.변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.
다음으로, 애플리케이션을 실행할 때 테이블에 대한 일부 데이터를 표시하는 코드를 작성합니다.
테이블 뷰 채우기
인터페이스 작성기에서 디자인되고 콘센트를 통해 노출되는 테이블 뷰를 사용하여 다음으로 채울 C# 코드를 만들어야 합니다.
먼저 개별 행에 대한 정보를 저장할 새 Product
클래스를 만들어 보겠습니다. 솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>를 선택합니다. 일반>빈 클래스를 선택하고 이름을 입력 Product
한 다음 새로 만들기 단추를 클릭합니다.
Product.cs
파일을 다음과 같이 표시합니다.
using System;
namespace MacTables
{
public class Product
{
#region Computed Properties
public string Title { get; set;} = "";
public string Description { get; set;} = "";
#endregion
#region Constructors
public Product ()
{
}
public Product (string title, string description)
{
this.Title = title;
this.Description = description;
}
#endregion
}
}
다음으로, 요청된 테이블에 대한 데이터를 제공하기 위한 하위 클래스 NSTableDataSource
를 만들어야 합니다. 솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>를 선택합니다. 일반>빈 클래스를 선택하고 이름을 입력 ProductTableDataSource
한 다음 새로 만들기 단추를 클릭합니다.
ProductTableDataSource.cs
파일을 편집하고 다음과 같이 표시합니다.
using System;
using AppKit;
using CoreGraphics;
using Foundation;
using System.Collections;
using System.Collections.Generic;
namespace MacTables
{
public class ProductTableDataSource : NSTableViewDataSource
{
#region Public Variables
public List<Product> Products = new List<Product>();
#endregion
#region Constructors
public ProductTableDataSource ()
{
}
#endregion
#region Override Methods
public override nint GetRowCount (NSTableView tableView)
{
return Products.Count;
}
#endregion
}
}
이 클래스에는 테이블 뷰의 항목에 대한 스토리지가 있으며 테이블의 행 수를 반환하도록 재정 GetRowCount
의합니다.
마지막으로 테이블에 대한 동작을 제공하기 위한 하위 클래스 NSTableDelegate
를 만들어야 합니다. 솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>를 선택합니다. 일반>빈 클래스를 선택하고 이름을 입력 ProductTableDelegate
한 다음 새로 만들기 단추를 클릭합니다.
ProductTableDelegate.cs
파일을 편집하고 다음과 같이 표시합니다.
using System;
using AppKit;
using CoreGraphics;
using Foundation;
using System.Collections;
using System.Collections.Generic;
namespace MacTables
{
public class ProductTableDelegate: NSTableViewDelegate
{
#region Constants
private const string CellIdentifier = "ProdCell";
#endregion
#region Private Variables
private ProductTableDataSource DataSource;
#endregion
#region Constructors
public ProductTableDelegate (ProductTableDataSource datasource)
{
this.DataSource = datasource;
}
#endregion
#region Override Methods
public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
// This pattern allows you reuse existing views when they are no-longer in use.
// If the returned view is null, you instance up a new view
// If a non-null view is returned, you modify it enough to reflect the new data
NSTextField view = (NSTextField)tableView.MakeView (CellIdentifier, this);
if (view == null) {
view = new NSTextField ();
view.Identifier = CellIdentifier;
view.BackgroundColor = NSColor.Clear;
view.Bordered = false;
view.Selectable = false;
view.Editable = false;
}
// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
view.StringValue = DataSource.Products [(int)row].Title;
break;
case "Details":
view.StringValue = DataSource.Products [(int)row].Description;
break;
}
return view;
}
#endregion
}
}
인스턴스 ProductTableDelegate
를 만들 때 테이블에 대한 데이터를 제공하는 인스턴스 ProductTableDataSource
도 전달합니다. 이 GetViewForItem
메서드는 뷰(데이터)를 반환하여 제공 열과 행의 셀을 표시합니다. 가능하면 기존 뷰를 다시 사용하여 셀을 표시합니다(새 뷰가 아닌 경우).
테이블을 채우기 위해 파일을 편집 ViewController.cs
하고 메서드를 AwakeFromNib
다음과 같이 만들어 보겠습니다.
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
// Create the Product Table Data Source and populate it
var DataSource = new ProductTableDataSource ();
DataSource.Products.Add (new Product ("Xamarin.iOS", "Allows you to develop native iOS Applications in C#"));
DataSource.Products.Add (new Product ("Xamarin.Android", "Allows you to develop native Android Applications in C#"));
DataSource.Products.Add (new Product ("Xamarin.Mac", "Allows you to develop Mac native Applications in C#"));
// Populate the Product Table
ProductTable.DataSource = DataSource;
ProductTable.Delegate = new ProductTableDelegate (DataSource);
}
애플리케이션을 실행하는 경우 다음이 표시됩니다.
열별 정렬
사용자가 열 머리글을 클릭하여 테이블의 데이터를 정렬하도록 허용해 보겠습니다. 먼저 파일을 두 번 클릭하여 Main.storyboard
Interface Builder에서 편집할 수 있도록 엽니다. Product
열을 선택하고 정렬 키 compare:
에 대해 선택기를 입력 Title
하고 순서를 선택합니다.Ascending
Details
열을 선택하고 정렬 키 compare:
에 대해 선택기를 입력 Description
하고 순서를 선택합니다.Ascending
변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.
이제 파일을 편집 ProductTableDataSource.cs
하고 다음 메서드를 추가해 보겠습니다.
public void Sort(string key, bool ascending) {
// Take action based on key
switch (key) {
case "Title":
if (ascending) {
Products.Sort ((x, y) => x.Title.CompareTo (y.Title));
} else {
Products.Sort ((x, y) => -1 * x.Title.CompareTo (y.Title));
}
break;
case "Description":
if (ascending) {
Products.Sort ((x, y) => x.Description.CompareTo (y.Description));
} else {
Products.Sort ((x, y) => -1 * x.Description.CompareTo (y.Description));
}
break;
}
}
public override void SortDescriptorsChanged (NSTableView tableView, NSSortDescriptor[] oldDescriptors)
{
// Sort the data
if (oldDescriptors.Length > 0) {
// Update sort
Sort (oldDescriptors [0].Key, oldDescriptors [0].Ascending);
} else {
// Grab current descriptors and update sort
NSSortDescriptor[] tbSort = tableView.SortDescriptors;
Sort (tbSort[0].Key, tbSort[0].Ascending);
}
// Refresh table
tableView.ReloadData ();
}
이 Sort
메서드를 사용하면 지정된 Product
클래스 필드를 기준으로 데이터 원본의 데이터를 오름차순 또는 내림차순으로 정렬할 수 있습니다. 재정의된 SortDescriptorsChanged
메서드는 열 머리글을 클릭할 때마다 호출됩니다. 인터페이스 작성기에서 설정한 키 값과 해당 열의 정렬 순서가 전달됩니다.
애플리케이션을 실행하고 열 머리글을 클릭하면 행이 해당 열별로 정렬됩니다.
행 선택
사용자가 단일 행을 선택할 수 있도록 하려면 파일을 두 번 클릭하여 Main.storyboard
Interface Builder에서 편집할 수 있도록 엽니다. 인터페이스 계층 구조에서 테이블 뷰를 선택하고 특성 검사기에서 여러 확인란의 선택을 취소합니다.
변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.
다음으로 파일을 편집 ProductTableDelegate.cs
하고 다음 메서드를 추가합니다.
public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
return true;
}
이렇게 하면 사용자가 테이블 뷰에서 단일 행을 선택할 수 있습니다. ShouldSelectRow
사용자가 선택할 수 없도록 하거나 사용자가 행을 선택할 false
수 없도록 하려면 모든 행에 대해 반환 false
합니다.
테이블 뷰(NSTableView
)에는 행 선택 작업을 위한 다음 메서드가 포함되어 있습니다.
DeselectRow(nint)
- 테이블에서 지정된 행의 선택을 취소합니다.SelectRow(nint,bool)
- 지정된 행을 선택합니다. 두 번째 매개 변수를 전달false
하여 한 번에 하나의 행만 선택합니다.SelectedRow
- 테이블에서 선택한 현재 행을 반환합니다.IsRowSelected(nint)
- 지정된 행이 선택되었는지를 반환true
합니다.
여러 행 선택
사용자가 여러 행을 선택할 수 있도록 허용하려면 파일을 두 번 클릭하여 Main.storyboard
인터페이스 작성기에서 편집할 수 있도록 엽니다. 인터페이스 계층 구조에서 테이블 뷰를 선택하고 특성 검사기에서 여러 확인란을 선택합니다.
변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.
다음으로 파일을 편집 ProductTableDelegate.cs
하고 다음 메서드를 추가합니다.
public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
return true;
}
이렇게 하면 사용자가 테이블 뷰에서 단일 행을 선택할 수 있습니다. ShouldSelectRow
사용자가 선택할 수 없도록 하거나 사용자가 행을 선택할 false
수 없도록 하려면 모든 행에 대해 반환 false
합니다.
테이블 뷰(NSTableView
)에는 행 선택 작업을 위한 다음 메서드가 포함되어 있습니다.
DeselectAll(NSObject)
- 테이블의 모든 행을 선택 취소합니다. 첫 번째 매개 변수를 사용하여this
선택 작업을 수행하는 개체에 보냅니다.DeselectRow(nint)
- 테이블에서 지정된 행의 선택을 취소합니다.SelectAll(NSobject)
- 테이블의 모든 행을 선택합니다. 첫 번째 매개 변수를 사용하여this
선택 작업을 수행하는 개체에 보냅니다.SelectRow(nint,bool)
- 지정된 행을 선택합니다. 두 번째 매개 변수를 전달false
하여 선택 영역을 지우고 단일 행만 선택하고 선택true
영역을 확장하고 이 행을 포함합니다.SelectRows(NSIndexSet,bool)
- 지정된 행 집합을 선택합니다. 두 번째 매개 변수에 대한 전달false
은 선택 영역을 지우고 이러한 행만 선택하고 선택 영역을 확장하고 이러한 행을 포함하도록 전달true
합니다.SelectedRow
- 테이블에서 선택한 현재 행을 반환합니다.SelectedRows
- 선택한 행의 인덱스를 포함하는 값을 반환NSIndexSet
합니다.SelectedRowCount
- 선택한 행 수를 반환합니다.IsRowSelected(nint)
- 지정된 행이 선택되었는지를 반환true
합니다.
행을 선택할 형식
사용자가 테이블 뷰가 선택된 문자를 입력하도록 허용하고 해당 문자가 있는 첫 번째 행을 선택하려면 파일을 두 번 클릭하여 Main.storyboard
인터페이스 작성기에서 편집할 수 있도록 엽니다. 인터페이스 계층 구조에서 테이블 뷰를 선택하고 특성 검사기에서 형식 선택 확인란을 선택합니다.
변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.
이제 파일을 편집 ProductTableDelegate.cs
하고 다음 메서드를 추가해 보겠습니다.
public override nint GetNextTypeSelectMatch (NSTableView tableView, nint startRow, nint endRow, string searchString)
{
nint row = 0;
foreach(Product product in DataSource.Products) {
if (product.Title.Contains(searchString)) return row;
// Increment row counter
++row;
}
// If not found select the first row
return 0;
}
메서드는 GetNextTypeSelectMatch
지정된 searchString
값을 가져와서 해당 문자열Title
이 있는 첫 번째 Product
행을 반환합니다.
애플리케이션을 실행하고 문자를 입력하면 행이 선택됩니다.
열 순서 다시 지정
사용자가 테이블 뷰에서 열 순서를 다시 정렬하도록 허용하려면 파일을 두 번 클릭하여 Main.storyboard
인터페이스 작성기에서 편집할 수 있도록 엽니다. 인터페이스 계층 구조에서 테이블 뷰를 선택하고 특성 검사기에서 순서 다시 지정 확인란을 선택합니다.
자동 저장 속성에 대한 값을 제공하고 열 정보 필드를 확인하면 테이블 레이아웃에 대한 변경 내용이 자동으로 저장되고 다음에 애플리케이션이 실행될 때 복원됩니다.
변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.
이제 파일을 편집 ProductTableDelegate.cs
하고 다음 메서드를 추가해 보겠습니다.
public override bool ShouldReorder (NSTableView tableView, nint columnIndex, nint newColumnIndex)
{
return true;
}
메서드는 ShouldReorder
순서를 다시 newColumnIndex
정렬할 수 있도록 하려는 열에 대해 반환 true
해야 합니다. 그렇지 않으면 반환false
됩니다.
애플리케이션을 실행하는 경우 열 머리글을 끌어 열의 순서를 변경할 수 있습니다.
셀 편집
사용자가 지정된 셀의 값을 편집하도록 허용하려면 파일을 편집 ProductTableDelegate.cs
하고 다음과 같이 메서드를 GetViewForItem
변경합니다.
public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
// This pattern allows you reuse existing views when they are no-longer in use.
// If the returned view is null, you instance up a new view
// If a non-null view is returned, you modify it enough to reflect the new data
NSTextField view = (NSTextField)tableView.MakeView (tableColumn.Title, this);
if (view == null) {
view = new NSTextField ();
view.Identifier = tableColumn.Title;
view.BackgroundColor = NSColor.Clear;
view.Bordered = false;
view.Selectable = false;
view.Editable = true;
view.EditingEnded += (sender, e) => {
// Take action based on type
switch(view.Identifier) {
case "Product":
DataSource.Products [(int)view.Tag].Title = view.StringValue;
break;
case "Details":
DataSource.Products [(int)view.Tag].Description = view.StringValue;
break;
}
};
}
// Tag view
view.Tag = row;
// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
view.StringValue = DataSource.Products [(int)row].Title;
break;
case "Details":
view.StringValue = DataSource.Products [(int)row].Description;
break;
}
return view;
}
이제 애플리케이션을 실행하면 사용자는 테이블 뷰에서 셀을 편집할 수 있습니다.
테이블 뷰에서 이미지 사용
이미지를 셀의 일부로 포함하려면 테이블 뷰 NSTableViewDelegate's
GetViewForItem
의 메서드에서 NSTableView
데이터를 반환하는 방법을 변경하여 일반적인 NSTextField
대신 사용해야 NSTableCellView
합니다. 예시:
public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
// This pattern allows you reuse existing views when they are no-longer in use.
// If the returned view is null, you instance up a new view
// If a non-null view is returned, you modify it enough to reflect the new data
NSTableCellView view = (NSTableCellView)tableView.MakeView (tableColumn.Title, this);
if (view == null) {
view = new NSTableCellView ();
if (tableColumn.Title == "Product") {
view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
view.AddSubview (view.ImageView);
view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
} else {
view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
}
view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
view.AddSubview (view.TextField);
view.Identifier = tableColumn.Title;
view.TextField.BackgroundColor = NSColor.Clear;
view.TextField.Bordered = false;
view.TextField.Selectable = false;
view.TextField.Editable = true;
view.TextField.EditingEnded += (sender, e) => {
// Take action based on type
switch(view.Identifier) {
case "Product":
DataSource.Products [(int)view.TextField.Tag].Title = view.TextField.StringValue;
break;
case "Details":
DataSource.Products [(int)view.TextField.Tag].Description = view.TextField.StringValue;
break;
}
};
}
// Tag view
view.TextField.Tag = row;
// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
view.ImageView.Image = NSImage.ImageNamed ("tags.png");
view.TextField.StringValue = DataSource.Products [(int)row].Title;
break;
case "Details":
view.TextField.StringValue = DataSource.Products [(int)row].Description;
break;
}
return view;
}
자세한 내용은 이미지 작업 설명서의 테이블 뷰에서 이미지 사용 섹션을 참조하세요.
행에 삭제 단추 추가
앱의 요구 사항에 따라 테이블의 각 행에 대해 작업 단추를 제공해야 하는 경우가 있을 수 있습니다. 예를 들어 각 행에 삭제 단추를 포함하도록 위에서 만든 테이블 뷰 예제를 확장해 보겠습니다.
먼저 Xcode의 인터페이스 작성기에서 편집 Main.storyboard
하고 테이블 뷰를 선택하고 열 수를 3(3)으로 늘립니다. 다음으로 새 열의 제목 을 다음으로 Action
변경합니다.
변경 내용을 Storyboard에 저장하고 Mac용 Visual Studio 돌아가 변경 내용을 동기화합니다.
다음으로 파일을 편집 ViewController.cs
하고 다음 공용 메서드를 추가합니다.
public void ReloadTable ()
{
ProductTable.ReloadData ();
}
동일한 파일에서 다음과 같이 메서드 내에서 새 테이블 뷰 대리자 ViewDidLoad
만들기를 수정합니다.
// Populate the Product Table
ProductTable.DataSource = DataSource;
ProductTable.Delegate = new ProductTableDelegate (this, DataSource);
이제 뷰 컨트롤러에 대한 프라이빗 연결을 포함하고 대리자의 새 인스턴스를 만들 때 컨트롤러를 매개 변수로 사용하도록 파일을 편집 ProductTableDelegate.cs
합니다.
#region Private Variables
private ProductTableDataSource DataSource;
private ViewController Controller;
#endregion
#region Constructors
public ProductTableDelegate (ViewController controller, ProductTableDataSource datasource)
{
this.Controller = controller;
this.DataSource = datasource;
}
#endregion
다음으로 클래스에 다음 새 private 메서드를 추가합니다.
private void ConfigureTextField (NSTableCellView view, nint row)
{
// Add to view
view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
view.AddSubview (view.TextField);
// Configure
view.TextField.BackgroundColor = NSColor.Clear;
view.TextField.Bordered = false;
view.TextField.Selectable = false;
view.TextField.Editable = true;
// Wireup events
view.TextField.EditingEnded += (sender, e) => {
// Take action based on type
switch (view.Identifier) {
case "Product":
DataSource.Products [(int)view.TextField.Tag].Title = view.TextField.StringValue;
break;
case "Details":
DataSource.Products [(int)view.TextField.Tag].Description = view.TextField.StringValue;
break;
}
};
// Tag view
view.TextField.Tag = row;
}
이렇게 하면 이전에 메서드에서 GetViewForItem
수행되었던 모든 텍스트 뷰 구성을 가져와서 호출 가능한 단일 위치에 배치합니다(테이블의 마지막 열에 텍스트 뷰가 아니라 단추가 포함되어 있기 때문에).
마지막으로 메서드를 GetViewForItem
편집하고 다음과 같이 표시합니다.
public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
// This pattern allows you reuse existing views when they are no-longer in use.
// If the returned view is null, you instance up a new view
// If a non-null view is returned, you modify it enough to reflect the new data
NSTableCellView view = (NSTableCellView)tableView.MakeView (tableColumn.Title, this);
if (view == null) {
view = new NSTableCellView ();
// Configure the view
view.Identifier = tableColumn.Title;
// Take action based on title
switch (tableColumn.Title) {
case "Product":
view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
view.AddSubview (view.ImageView);
view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
ConfigureTextField (view, row);
break;
case "Details":
view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
ConfigureTextField (view, row);
break;
case "Action":
// Create new button
var button = new NSButton (new CGRect (0, 0, 81, 16));
button.SetButtonType (NSButtonType.MomentaryPushIn);
button.Title = "Delete";
button.Tag = row;
// Wireup events
button.Activated += (sender, e) => {
// Get button and product
var btn = sender as NSButton;
var product = DataSource.Products [(int)btn.Tag];
// Configure alert
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Informational,
InformativeText = $"Are you sure you want to delete {product.Title}? This operation cannot be undone.",
MessageText = $"Delete {product.Title}?",
};
alert.AddButton ("Cancel");
alert.AddButton ("Delete");
alert.BeginSheetForResponse (Controller.View.Window, (result) => {
// Should we delete the requested row?
if (result == 1001) {
// Remove the given row from the dataset
DataSource.Products.RemoveAt((int)btn.Tag);
Controller.ReloadTable ();
}
});
};
// Add to view
view.AddSubview (button);
break;
}
}
// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
view.ImageView.Image = NSImage.ImageNamed ("tag.png");
view.TextField.StringValue = DataSource.Products [(int)row].Title;
view.TextField.Tag = row;
break;
case "Details":
view.TextField.StringValue = DataSource.Products [(int)row].Description;
view.TextField.Tag = row;
break;
case "Action":
foreach (NSView subview in view.Subviews) {
var btn = subview as NSButton;
if (btn != null) {
btn.Tag = row;
}
}
break;
}
return view;
}
이 코드의 몇 가지 섹션을 자세히 살펴보겠습니다. 첫째, 새로 NSTableViewCell
만드는 경우 열의 이름을 기반으로 작업을 수행합니다. 처음 두 열(제품 및 세부 정보)의 경우 새 ConfigureTextField
메서드가 호출됩니다.
작업 열의 경우 새 NSButton
항목이 만들어지고 셀에 하위 뷰로 추가됩니다.
// Create new button
var button = new NSButton (new CGRect (0, 0, 81, 16));
button.SetButtonType (NSButtonType.MomentaryPushIn);
button.Title = "Delete";
button.Tag = row;
...
// Add to view
view.AddSubview (button);
단추의 Tag
속성은 현재 처리 중인 행의 수를 보유하는 데 사용됩니다. 이 번호는 나중에 사용자가 Button의 Activated
이벤트에서 삭제할 행을 요청할 때 사용됩니다.
// Wireup events
button.Activated += (sender, e) => {
// Get button and product
var btn = sender as NSButton;
var product = DataSource.Products [(int)btn.Tag];
// Configure alert
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Informational,
InformativeText = $"Are you sure you want to delete {product.Title}? This operation cannot be undone.",
MessageText = $"Delete {product.Title}?",
};
alert.AddButton ("Cancel");
alert.AddButton ("Delete");
alert.BeginSheetForResponse (Controller.View.Window, (result) => {
// Should we delete the requested row?
if (result == 1001) {
// Remove the given row from the dataset
DataSource.Products.RemoveAt((int)btn.Tag);
Controller.ReloadTable ();
}
});
};
이벤트 처리기의 시작 부분에 지정된 테이블 행에 있는 단추와 제품이 표시됩니다. 그런 다음 사용자에게 행 삭제를 확인하는 경고가 표시됩니다. 사용자가 행을 삭제하도록 선택하면 지정된 행이 데이터 원본에서 제거되고 테이블이 다시 로드됩니다.
// Remove the given row from the dataset
DataSource.Products.RemoveAt((int)btn.Tag);
Controller.ReloadTable ();
마지막으로 테이블 뷰 셀을 새로 만드는 대신 다시 사용하는 경우 다음 코드는 처리 중인 열에 따라 구성합니다.
// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
view.ImageView.Image = NSImage.ImageNamed ("tag.png");
view.TextField.StringValue = DataSource.Products [(int)row].Title;
view.TextField.Tag = row;
break;
case "Details":
view.TextField.StringValue = DataSource.Products [(int)row].Description;
view.TextField.Tag = row;
break;
case "Action":
foreach (NSView subview in view.Subviews) {
var btn = subview as NSButton;
if (btn != null) {
btn.Tag = row;
}
}
break;
}
작업 열의 경우 모든 하위 뷰가 발견될 때까지 NSButton
검색된 다음 Tag
속성이 현재 행을 가리키도록 업데이트됩니다.
이러한 변경 내용을 적용하면 앱이 실행될 때 각 행에 삭제 단추가 있습니다.
사용자가 삭제 단추를 클릭하면 지정된 행을 삭제하라는 경고가 표시됩니다.
사용자가 삭제를 선택하면 행이 제거되고 테이블이 다시 그려지게 됩니다.
데이터 바인딩 테이블 뷰
Xamarin.Mac 애플리케이션에서 키-값 코딩 및 데이터 바인딩 기술을 사용하면 UI 요소를 채우고 작업하기 위해 작성하고 유지 관리해야 하는 코드의 양을 크게 줄일 수 있습니다. 또한 프런트 엔드 사용자 인터페이스(Model-View-Controller)에서 백업 데이터(데이터 모델)를 추가로 분리하여 보다 쉽고 유연한 애플리케이션 디자인을 유지 관리할 수 있습니다.
KVC(키-값 코딩)는 인스턴스 변수 또는 접근자 메서드get/set
()를 통해 액세스하는 대신 키(특별히 형식이 지정된 문자열)를 사용하여 개체의 속성에 간접적으로 액세스하는 메커니즘입니다. Xamarin.Mac 애플리케이션에서 키-값 코딩 규격 접근자를 구현하면 KVO(키-값 관찰), 데이터 바인딩, 코어 데이터, 코코아 바인딩 및 스크립트 기능과 같은 다른 macOS 기능에 액세스할 수 있습니다.
자세한 내용은 데이터 바인딩 및 키-값 코딩 설명서의 테이블 뷰 데이터 바인딩 섹션을 참조하세요.
요약
이 문서에서는 Xamarin.Mac 애플리케이션에서 테이블 뷰 작업을 자세히 살펴보았습니다. 테이블 뷰의 다양한 형식 및 사용, Xcode의 인터페이스 작성기에서 테이블 뷰를 만들고 유지 관리하는 방법 및 C# 코드에서 테이블 뷰를 사용하는 방법을 알아보았습니다.