Xamarin.Mac의 데이터베이스Databases in Xamarin.Mac

이 문서에서는 키-값 코딩 및 관찰 SQLite 데이터베이스와 Xcode의 Interface Builder에서 UI 요소 간의 데이터 바인딩을 허용 하는 키-값을 사용 하 여 설명 합니다. SQLite.NET ORM을 사용 하 여 SQLite 데이터에 액세스할 수 있도록 대해서도 설명 합니다.This article covers using key-value coding and key-value observing to allow for data binding between SQLite databases and UI elements in Xcode's Interface Builder. It also covers using the SQLite.NET ORM to provide access to SQLite data.

개요Overview

Xamarin.Mac 응용 프로그램에서 C# 및.NET을 사용 하 여 작업을 하는 경우는 Xamarin.iOS 또는 Xamarin.Android 응용 프로그램에 액세스할 수 있는 동일한 SQLite 데이터베이스에 액세스할을 수 있습니다.When working with C# and .NET in a Xamarin.Mac application, you have access to the same SQLite databases that a Xamarin.iOS or Xamarin.Android application can access.

이 문서에서 우리는 주제 SQLite 데이터에 액세스 하는 두 가지 방법:In this article we will be covering two ways to access SQLite data:

  1. 직접 액세스 -SQLite 데이터베이스에 직접 액세스 하 여 키-값 코딩에 대 한 데이터베이스의에서 데이터를 사용할 수 있으며 Xcode의 Interface Builder에서 UI 요소를 사용 하 여 데이터 바인딩이 생성 합니다.Direct Access - By directly accessing a SQLite Database, we can use data from the database for key-value coding and data binding with UI elements created in Xcode's Interface Builder. 키-값 코딩 및 바인딩 기술 Xamarin.Mac 응용 프로그램에서 데이터를 사용 하 여 작성 하 고 채우는 UI 요소를 사용 하 여 작업을 유지 해야 하는 코드의 양을 크게 줄일 수 있습니다.By using key-value coding and data binding techniques in your Xamarin.Mac application, you can greatly decrease the amount of code that you have to write and maintain to populate and work with UI elements. 추가 백업 데이터를 분리의 장점은 수도 있습니다 (데이터 모델)에 처음부터 사용자 인터페이스를 종료 합니다 (모델-뷰-컨트롤러)를 더 쉬워진 유지 관리를 보다 융통성 있는 응용 프로그램 디자인 합니다.You also have the benefit of further decoupling your backing data (Data Model) from your front end User Interface (Model-View-Controller), leading to easier to maintain, more flexible application design.
  2. SQLite.NET ORM -오픈 소스를 사용 하 여 SQLite.NET 개체 관계 관리자 (ORM) SQLite 데이터베이스에서 데이터를 읽고 하는 데 필요한 코드의 양을 크게 줄일 수 있습니다 것입니다.SQLite.NET ORM - By using the open source SQLite.NET Object Relationship Manager (ORM) we can greatly reduce the amount of code required to read and write data from a SQLite database. 그런 다음이 데이터 테이블 뷰 같은 사용자 인터페이스 항목을 채우는 데 사용할 수 있습니다.This data can then be used to populate a user interface item such as a Table View.

실행 중인 앱의 예가An example of the running app

이 문서에서는 키-값 코딩 및 SQLite 데이터베이스를 사용 하 여 Xamarin.Mac 응용 프로그램에서 데이터 바인딩을 사용 하는 기본 사항을 설명 합니다.In this article, we'll cover the basics of working with key-value coding and data binding with SQLite Databases in a Xamarin.Mac application. 것이 가장 좋습니다를 통해 작업 하는 합니다 Hello, Mac 먼저, 특히 문서 합니다 Xcode 및 Interface Builder 소개 하 고 출 선 및 작업 섹션으로 주요 개념 및이 문서를 사용 하는 기술을 설명 합니다.It is highly suggested that you work through the Hello, Mac article first, specifically the Introduction to Xcode and Interface Builder and Outlets and Actions sections, as it covers key concepts and techniques that we'll be using in this article.

키-값 코딩 및 데이터 바인딩을 사용 합니다, 이후 하세요 진행 합니다 데이터 바인딩 및 키-값 코딩 먼저으로 핵심 기술 및 개념을 다룹니다이 설명서에서 해당 샘플에 사용할 응용 프로그램입니다.Since we will be using key-value coding and data binding, please work through the Data binding and key-value coding first, as core techniques and concepts will be covered that will be used in this documentation and its sample application.

참조 하려는 경우는 노출 C# 클래스 / Objective-c 하는 메서드를 의 섹션은 Xamarin.Mac 내부 설명도 문서는 RegisterExport 특성 요소 Objective-C 개체 및 UI에 C# 클래스를 연결 하는 데 사용 합니다.You may want to take a look at the Exposing C# classes / methods to Objective-C section of the Xamarin.Mac Internals document as well, it explains the Register and Export attributes used to wire up your C# classes to Objective-C objects and UI elements.

SQLite에 대 한 직접 액세스Direct SQLite access

Xcode의 Interface Builder에서 UI 요소에 바인딩할 수는 SQLite 데이터에 대 한 것이 가장 좋습니다 직접 (달리 ORM 같은 기술을 사용 하 여) SQLite 데이터베이스를 액세스, 총 방식 제어할 수 없으므로 데이터를 작성 하 고 읽기 데이터베이스입니다.For SQLite data that is going to be bound to UI elements in Xcode's Interface Builder, it is highly suggested that you access the SQLite database directly (as opposed to using a technique such as an ORM), since you have total control over the way the data is written and read from the database.

살펴본 것 처럼 합니다 데이터 바인딩 및 키-값 코딩 문서, 키-값 코딩 및 데이터 바인딩 기술 Xamarin.Mac 응용 프로그램에서 사용 하 여 줄일 수 있습니다 크게 작성 해야 하는 코드의 양 및 채우고 UI 요소를 사용 하는 유지 관리 합니다.As we have seen in the Data Binding and Key-Value Coding documentation, by using key-value coding and data binding techniques in your Xamarin.Mac application, you can greatly decrease the amount of code that you have to write and maintain to populate and work with UI elements. SQLite 데이터베이스에 대 한 직접 액세스를 결합 하면 해당 데이터베이스에 데이터를 읽고 하는 데 필요한 코드의 양을 크게 줄일 수 있습니다.When combined with direct access to a SQLite database, it can also greatly reduce the amount of code required to read and write data to that database.

이 문서에서는 샘플 앱을 백업 원본으로는 SQLite 데이터베이스를 사용 하 여 바인딩에 대 한 데이터 바인딩 및 키-값 코딩 문서를 수정할 예정입니다.In this article, we will be modifying the sample app from the data binding and key-value coding document to use a SQLite Database as the backing source for the binding.

포함 하 여 SQLite 데이터베이스 지원Including SQLite database support

계속 수 전에 몇 가지에 대 한 참조를 포함 하 여 응용 프로그램에 SQLite 데이터베이스 지원을 추가 해야 합니다. Dll 파일입니다.Before we can continue, we need to add SQLite database support to our application by including References to a couple of .DLLs files.

다음을 수행합니다.Do the following:

  1. Solution Pad를 마우스 오른쪽 단추로 클릭 합니다 참조 폴더를 선택 참조 편집합니다.In the Solution Pad, right-click on the References folder and select Edit References.

  2. 둘 다 선택 합니다 Mono.Data.Sqlite 하 고 System.Data 어셈블리:Select both the Mono.Data.Sqlite and System.Data assemblies:

    필요한 참조를 추가Adding the required references

  3. 클릭 합니다 확인 단추 변경 내용을 저장 하 고 참조를 추가 합니다.Click the OK button to save your changes and add the references.

데이터 모델을 수정Modifying the data model

응용 프로그램에 SQLite 데이터베이스에 직접 액세스 하는 것에 대 한 지원을 추가 했지만, 이제는 읽기 및 데이터베이스에서 데이터 쓰기 (뿐만 키-값 코딩 및 데이터 바인딩 제공)은 데이터 모델 개체를 수정 해야 합니다.Now that we have added support for directly accessing a SQLite database to our application, we need to modify our Data Model Object to read and write data from the database (as well as provide key-value coding and data binding). 이 샘플 응용 프로그램의 경우 편집할 수는 PersonModel.cs 클래스 및 다음과 비슷하게 표시:In the case of our sample application, we'll edit the PersonModel.cs class and make it look like the following:

using System;
using System.Data;
using System.IO;
using Mono.Data.Sqlite;
using Foundation;
using AppKit;

namespace MacDatabase
{
    [Register("PersonModel")]
    public class PersonModel : NSObject
    {
        #region Private Variables
        private string _ID = "";
        private string _managerID = "";
        private string _name = "";
        private string _occupation = "";
        private bool _isManager = false;
        private NSMutableArray _people = new NSMutableArray();
        private SqliteConnection _conn = null;
        #endregion

        #region Computed Properties
        public SqliteConnection Conn {
            get { return _conn; }
            set { _conn = value; }
        }

        [Export("ID")]
        public string ID {
            get { return _ID; }
            set {
                WillChangeValue ("ID");
                _ID = value;
                DidChangeValue ("ID");
            }
        }

        [Export("ManagerID")]
        public string ManagerID {
            get { return _managerID; }
            set {
                WillChangeValue ("ManagerID");
                _managerID = value;
                DidChangeValue ("ManagerID");
            }
        }

        [Export("Name")]
        public string Name {
            get { return _name; }
            set {
                WillChangeValue ("Name");
                _name = value;
                DidChangeValue ("Name");

                // Save changes to database?
                if (_conn != null) Update (_conn);
            }
        }

        [Export("Occupation")]
        public string Occupation {
            get { return _occupation; }
            set {
                WillChangeValue ("Occupation");
                _occupation = value;
                DidChangeValue ("Occupation");

                // Save changes to database?
                if (_conn != null) Update (_conn);
            }
        }

        [Export("isManager")]
        public bool isManager {
            get { return _isManager; }
            set {
                WillChangeValue ("isManager");
                WillChangeValue ("Icon");
                _isManager = value;
                DidChangeValue ("isManager");
                DidChangeValue ("Icon");

                // Save changes to database?
                if (_conn != null) Update (_conn);
            }
        }

        [Export("isEmployee")]
        public bool isEmployee {
            get { return (NumberOfEmployees == 0); }
        }

        [Export("Icon")]
        public NSImage Icon {
            get {
                if (isManager) {
                    return NSImage.ImageNamed ("group.png");
                } else {
                    return NSImage.ImageNamed ("user.png");
                }
            }
        }

        [Export("personModelArray")]
        public NSArray People {
            get { return _people; }
        }

        [Export("NumberOfEmployees")]
        public nint NumberOfEmployees {
            get { return (nint)_people.Count; }
        }
        #endregion

        #region Constructors
        public PersonModel ()
        {
        }

        public PersonModel (string name, string occupation)
        {
            // Initialize
            this.Name = name;
            this.Occupation = occupation;
        }

        public PersonModel (string name, string occupation, bool manager)
        {
            // Initialize
            this.Name = name;
            this.Occupation = occupation;
            this.isManager = manager;
        }

        public PersonModel (string id, string name, string occupation)
        {
            // Initialize
            this.ID = id;
            this.Name = name;
            this.Occupation = occupation;
        }

        public PersonModel (SqliteConnection conn, string id)
        {
            // Load from database
            Load (conn, id);
        }
        #endregion

        #region Array Controller Methods
        [Export("addObject:")]
        public void AddPerson(PersonModel person) {
            WillChangeValue ("personModelArray");
            isManager = true;
            _people.Add (person);
            DidChangeValue ("personModelArray");
        }

        [Export("insertObject:inPersonModelArrayAtIndex:")]
        public void InsertPerson(PersonModel person, nint index) {
            WillChangeValue ("personModelArray");
            _people.Insert (person, index);
            DidChangeValue ("personModelArray");
        }

        [Export("removeObjectFromPersonModelArrayAtIndex:")]
        public void RemovePerson(nint index) {
            WillChangeValue ("personModelArray");
            _people.RemoveObject (index);
            DidChangeValue ("personModelArray");
        }

        [Export("setPersonModelArray:")]
        public void SetPeople(NSMutableArray array) {
            WillChangeValue ("personModelArray");
            _people = array;
            DidChangeValue ("personModelArray");
        }
        #endregion

        #region SQLite Routines
        public void Create(SqliteConnection conn) {

            // Clear last connection to prevent circular call to update
            _conn = null;

            // Create new record ID?
            if (ID == "") {
                ID = Guid.NewGuid ().ToString();
            }

            // Execute query
            conn.Open ();
            using (var command = conn.CreateCommand ()) {
                // Create new command
                command.CommandText = "INSERT INTO [People] (ID, Name, Occupation, isManager, ManagerID) VALUES (@COL1, @COL2, @COL3, @COL4, @COL5)";

                // Populate with data from the record
                command.Parameters.AddWithValue ("@COL1", ID);
                command.Parameters.AddWithValue ("@COL2", Name);
                command.Parameters.AddWithValue ("@COL3", Occupation);
                command.Parameters.AddWithValue ("@COL4", isManager);
                command.Parameters.AddWithValue ("@COL5", ManagerID);

                // Write to database
                command.ExecuteNonQuery ();
            }
            conn.Close ();

            // Save children to database as well
            for (nuint n = 0; n < People.Count; ++n) {
                // Grab person
                var Person = People.GetItem<PersonModel>(n);

                // Save manager ID and create the sub record
                Person.ManagerID = ID;
                Person.Create (conn);
            }

            // Save last connection
            _conn = conn;
        }

        public void Update(SqliteConnection conn) {

            // Clear last connection to prevent circular call to update
            _conn = null;

            // Execute query
            conn.Open ();
            using (var command = conn.CreateCommand ()) {
                // Create new command
                command.CommandText = "UPDATE [People] SET Name = @COL2, Occupation = @COL3, isManager = @COL4, ManagerID = @COL5 WHERE ID = @COL1";

                // Populate with data from the record
                command.Parameters.AddWithValue ("@COL1", ID);
                command.Parameters.AddWithValue ("@COL2", Name);
                command.Parameters.AddWithValue ("@COL3", Occupation);
                command.Parameters.AddWithValue ("@COL4", isManager);
                command.Parameters.AddWithValue ("@COL5", ManagerID);

                // Write to database
                command.ExecuteNonQuery ();
            }
            conn.Close ();

            // Save children to database as well
            for (nuint n = 0; n < People.Count; ++n) {
                // Grab person
                var Person = People.GetItem<PersonModel>(n);

                // Update sub record
                Person.Update (conn);
            }

            // Save last connection
            _conn = conn;
        }

        public void Load(SqliteConnection conn, string id) {
            bool shouldClose = false;

            // Clear last connection to prevent circular call to update
            _conn = null;

            // Is the database already open?
            if (conn.State != ConnectionState.Open) {
                shouldClose = true;
                conn.Open ();
            }

            // Execute query
            using (var command = conn.CreateCommand ()) {
                // Create new command
                command.CommandText = "SELECT * FROM [People] WHERE ID = @COL1";

                // Populate with data from the record
                command.Parameters.AddWithValue ("@COL1", id);

                using (var reader = command.ExecuteReader ()) {
                    while (reader.Read ()) {
                        // Pull values back into class
                        ID = (string)reader [0];
                        Name = (string)reader [1];
                        Occupation = (string)reader [2];
                        isManager = (bool)reader [3];
                        ManagerID = (string)reader [4];
                    }
                }
            }

            // Is this a manager?
            if (isManager) {
                // Yes, load children
                using (var command = conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = "SELECT ID FROM [People] WHERE ManagerID = @COL1";

                    // Populate with data from the record
                    command.Parameters.AddWithValue ("@COL1", id);

                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read ()) {
                            // Load child and add to collection
                            var childID = (string)reader [0];
                            var person = new PersonModel (conn, childID);
                            _people.Add (person);
                        }
                    }
                }
            }

            // Should we close the connection to the database
            if (shouldClose) {
                conn.Close ();
            }

            // Save last connection
            _conn = conn;
        }

        public void Delete(SqliteConnection conn) {

            // Clear last connection to prevent circular call to update
            _conn = null;

            // Execute query
            conn.Open ();
            using (var command = conn.CreateCommand ()) {
                // Create new command
                command.CommandText = "DELETE FROM [People] WHERE (ID = @COL1 OR ManagerID = @COL1)";

                // Populate with data from the record
                command.Parameters.AddWithValue ("@COL1", ID);

                // Write to database
                command.ExecuteNonQuery ();
            }
            conn.Close ();

            // Empty class
            ID = "";
            ManagerID = "";
            Name = "";
            Occupation = "";
            isManager = false;
            _people = new NSMutableArray();

            // Save last connection
            _conn = conn;
        }
        #endregion
    }
}

아래에서 자세히 수정에 대해를 살펴보겠습니다.Let's take a look at the modifications in detail below.

첫째, 여러 추가한 SQLite를 사용 하는 데 필요한 문을 사용 하 여 있으며 마지막 연결 SQLite 데이터베이스에 저장할 변수를 추가 했습니다.First, we've added several using statements that are required to use SQLite and we've added a variable to save our last connection to the SQLite database:

using System.Data;
using System.IO;
using Mono.Data.Sqlite;
...

private SqliteConnection _conn = null;

이 저장 된 연결 사용 하 여 사용자 데이터 바인딩을 통해 UI에 콘텐츠를 수정 하는 경우 변경 내용을 데이터베이스에 레코드를 자동으로 저장 하겠습니다.We'll use this saved connection to automatically save any changes to the record to the database when the user modifies the contents in the UI via data binding:

[Export("Name")]
public string Name {
    get { return _name; }
    set {
        WillChangeValue ("Name");
        _name = value;
        DidChangeValue ("Name");

        // Save changes to database?
        if (_conn != null) Update (_conn);
    }
}

[Export("Occupation")]
public string Occupation {
    get { return _occupation; }
    set {
        WillChangeValue ("Occupation");
        _occupation = value;
        DidChangeValue ("Occupation");

        // Save changes to database?
        if (_conn != null) Update (_conn);
    }
}

[Export("isManager")]
public bool isManager {
    get { return _isManager; }
    set {
        WillChangeValue ("isManager");
        WillChangeValue ("Icon");
        _isManager = value;
        DidChangeValue ("isManager");
        DidChangeValue ("Icon");

        // Save changes to database?
        if (_conn != null) Update (_conn);
    }
}

변경 내용을 합니다 이름, 직업 또는 isManager 속성 하기 전에 데이터가 있는 저장 된 경우 데이터베이스에 전송 됩니다 (예를 들어 경우는 _conn 변수가 없는 null).Any changes made to the Name, Occupation or isManager properties will be sent to the database if the data has been saved there before (e.g. if the _conn variable is not null). 다음으로를 추가 하는 방법에 살펴보겠습니다 만들기, 업데이트, 부하 하 고 삭제 데이터베이스에서 사용자.Next, let's look at the methods that we've added to Create, Update, Load and Delete people from the database.

새 레코드 만들기Create a new record

다음 코드는 SQLite 데이터베이스에서 새 레코드를 만드는 추가 되었습니다.The following code was added to create a new record in the SQLite database:

public void Create(SqliteConnection conn) {

    // Clear last connection to prevent circular call to update
    _conn = null;

    // Create new record ID?
    if (ID == "") {
        ID = Guid.NewGuid ().ToString();
    }

    // Execute query
    conn.Open ();
    using (var command = conn.CreateCommand ()) {
        // Create new command
        command.CommandText = "INSERT INTO [People] (ID, Name, Occupation, isManager, ManagerID) VALUES (@COL1, @COL2, @COL3, @COL4, @COL5)";

        // Populate with data from the record
        command.Parameters.AddWithValue ("@COL1", ID);
        command.Parameters.AddWithValue ("@COL2", Name);
        command.Parameters.AddWithValue ("@COL3", Occupation);
        command.Parameters.AddWithValue ("@COL4", isManager);
        command.Parameters.AddWithValue ("@COL5", ManagerID);

        // Write to database
        command.ExecuteNonQuery ();
    }
    conn.Close ();

    // Save children to database as well
    for (nuint n = 0; n < People.Count; ++n) {
        // Grab person
        var Person = People.GetItem<PersonModel>(n);

        // Save manager ID and create the sub record
        Person.ManagerID = ID;
        Person.Create (conn);
    }

    // Save last connection
    _conn = conn;
}

사용 하는 것을 SQLiteCommand 데이터베이스에 새 레코드를 만들어야 합니다.We are using a SQLiteCommand to create the new record in the database. 새 명령을 가져오는 합니다 SQLiteConnection 는 전달 된 메서드를 호출 하 여 (연결) CreateCommand합니다.We get a new command from the SQLiteConnection (conn) that we passed into the method by calling CreateCommand. 다음으로 설정 새 레코드를 실제로 쓸 SQL 명령 실제 값에 대 한 매개 변수를 제공 합니다.Next, we set the SQL instruction to actually write the new record, providing parameters for the actual values:

command.CommandText = "INSERT INTO [People] (ID, Name, Occupation, isManager, ManagerID) VALUES (@COL1, @COL2, @COL3, @COL4, @COL5)";

나중에 사용 하 여 매개 변수 값 설정 합니다 Parameters.AddWithValue 메서드는 SQLiteCommand합니다.Later we set the values for the parameters using the Parameters.AddWithValue method on the SQLiteCommand. 매개 변수를 사용 하 여 값 (예: 작은따옴표) SQLite에 보내지기 전에 제대로 인코딩 있는지 확인 합니다.By using parameters, we ensure that values (such as a single quote) get properly encoded before being sent to SQLite. 예제:Example:

command.Parameters.AddWithValue ("@COL1", ID);

마지막으로, 사용자는 관리자 및 해당 직원의 컬렉션을 사용할 수, 있으므로 재귀적으로 호출 된 Create 메서드는 물론 데이터베이스에 저장 하는 사람들에:Finally, since a person can be a manager and have a collection of employees under them, we are recursively calling the Create method on those people to save them to the database as well:

// Save children to database as well
for (nuint n = 0; n < People.Count; ++n) {
    // Grab person
    var Person = People.GetItem<PersonModel>(n);

    // Save manager ID and create the sub record
    Person.ManagerID = ID;
    Person.Create (conn);
}

레코드 업데이트Updating a record

다음 코드는 SQLite 데이터베이스의 기존 레코드 업데이트에 추가 되었습니다.The following code was added to update an existing record in the SQLite database:

public void Update(SqliteConnection conn) {

    // Clear last connection to prevent circular call to update
    _conn = null;

    // Execute query
    conn.Open ();
    using (var command = conn.CreateCommand ()) {
        // Create new command
        command.CommandText = "UPDATE [People] SET Name = @COL2, Occupation = @COL3, isManager = @COL4, ManagerID = @COL5 WHERE ID = @COL1";

        // Populate with data from the record
        command.Parameters.AddWithValue ("@COL1", ID);
        command.Parameters.AddWithValue ("@COL2", Name);
        command.Parameters.AddWithValue ("@COL3", Occupation);
        command.Parameters.AddWithValue ("@COL4", isManager);
        command.Parameters.AddWithValue ("@COL5", ManagerID);

        // Write to database
        command.ExecuteNonQuery ();
    }
    conn.Close ();

    // Save children to database as well
    for (nuint n = 0; n < People.Count; ++n) {
        // Grab person
        var Person = People.GetItem<PersonModel>(n);

        // Update sub record
        Person.Update (conn);
    }

    // Save last connection
    _conn = conn;
}

와 같은 만들기 얻게 위의 SQLiteCommand 전달 된에서 SQLiteConnection, (매개 변수를 제공) 하는 레코드를 업데이트 하는 SQL 설정:Like Create above, we get a SQLiteCommand from the passed in SQLiteConnection, and set our SQL to update our record (providing parameters):

command.CommandText = "UPDATE [People] SET Name = @COL2, Occupation = @COL3, isManager = @COL4, ManagerID = @COL5 WHERE ID = @COL1";

매개 변수 값을 입력 했습니다 (예: command.Parameters.AddWithValue ("@COL1", ID);) 다시 재귀적으로 호출 업데이트 모든 자식 레코드:We fill in the parameter values (example: command.Parameters.AddWithValue ("@COL1", ID);) and again, recursively call update on any child records:

// Save children to database as well
for (nuint n = 0; n < People.Count; ++n) {
    // Grab person
    var Person = People.GetItem<PersonModel>(n);

    // Update sub record
    Person.Update (conn);
}

레코드를 로드합니다.Loading a record

다음 코드는 SQLite 데이터베이스에서 기존 레코드를 로드에 추가 되었습니다.The following code was added to load an existing record from the SQLite database:

public void Load(SqliteConnection conn, string id) {
    bool shouldClose = false;

    // Clear last connection to prevent circular call to update
    _conn = null;

    // Is the database already open?
    if (conn.State != ConnectionState.Open) {
        shouldClose = true;
        conn.Open ();
    }

    // Execute query
    using (var command = conn.CreateCommand ()) {
        // Create new command
        command.CommandText = "SELECT * FROM [People] WHERE ID = @COL1";

        // Populate with data from the record
        command.Parameters.AddWithValue ("@COL1", id);

        using (var reader = command.ExecuteReader ()) {
            while (reader.Read ()) {
                // Pull values back into class
                ID = (string)reader [0];
                Name = (string)reader [1];
                Occupation = (string)reader [2];
                isManager = (bool)reader [3];
                ManagerID = (string)reader [4];
            }
        }
    }

    // Is this a manager?
    if (isManager) {
        // Yes, load children
        using (var command = conn.CreateCommand ()) {
            // Create new command
            command.CommandText = "SELECT ID FROM [People] WHERE ManagerID = @COL1";

            // Populate with data from the record
            command.Parameters.AddWithValue ("@COL1", id);

            using (var reader = command.ExecuteReader ()) {
                while (reader.Read ()) {
                    // Load child and add to collection
                    var childID = (string)reader [0];
                    var person = new PersonModel (conn, childID);
                    _people.Add (person);
                }
            }
        }
    }

    // Should we close the connection to the database
    if (shouldClose) {
        conn.Close ();
    }

    // Save last connection
    _conn = conn;
}

루틴 (예: 해당 직원 개체를 로드 하는 관리자 개체)는 부모 개체에서 재귀적으로 호출할 수 있습니다, 되므로 특수 코드 열기 및 닫기는 데이터베이스로 연결을 처리 하도록 추가 되었습니다.Because the routine can be called recursively from a parent object (such as a manager object loading their employees object), special code was added to handle opening and closing the connection to the database:

bool shouldClose = false;
...

// Is the database already open?
if (conn.State != ConnectionState.Open) {
    shouldClose = true;
    conn.Open ();
}
...

// Should we close the connection to the database
if (shouldClose) {
    conn.Close ();
}

늘 그렇듯이 레코드를 검색 하 여 매개 변수를 사용 하 여 SQL 설정 했습니다.As always, we set our SQL to retrieve the record and use parameters:

// Create new command
command.CommandText = "SELECT ID FROM [People] WHERE ManagerID = @COL1";

// Populate with data from the record
command.Parameters.AddWithValue ("@COL1", id);

마지막으로를 사용 하 여 데이터 판독기 쿼리가 실행 되 고 레코드 필드를 반환 (의 인스턴스로 복사 하는 PersonModel 클래스).Finally, we use a Data Reader to execute the query and return the record fields (which we copy into the instance of the PersonModel class):

using (var reader = command.ExecuteReader ()) {
    while (reader.Read ()) {
        // Pull values back into class
        ID = (string)reader [0];
        Name = (string)reader [1];
        Occupation = (string)reader [2];
        isManager = (bool)reader [3];
        ManagerID = (string)reader [4];
    }
}

또한 모든 직원을 로드 해야이 사용자가 관리자 인 경우 (재귀적으로 호출 하 여 다시 해당 Load 메서드).If this person is a manager, we need to also load all of their employees (again, by recursively calling their Load method):

// Is this a manager?
if (isManager) {
    // Yes, load children
    using (var command = conn.CreateCommand ()) {
        // Create new command
        command.CommandText = "SELECT ID FROM [People] WHERE ManagerID = @COL1";

        // Populate with data from the record
        command.Parameters.AddWithValue ("@COL1", id);

        using (var reader = command.ExecuteReader ()) {
            while (reader.Read ()) {
                // Load child and add to collection
                var childID = (string)reader [0];
                var person = new PersonModel (conn, childID);
                _people.Add (person);
            }
        }
    }
}

레코드 삭제Deleting a record

다음 코드는 SQLite 데이터베이스에서 기존 레코드를 삭제 하려면 추가 되었습니다.The following code was added to delete an existing record from the SQLite database:

public void Delete(SqliteConnection conn) {

    // Clear last connection to prevent circular call to update
    _conn = null;

    // Execute query
    conn.Open ();
    using (var command = conn.CreateCommand ()) {
        // Create new command
        command.CommandText = "DELETE FROM [People] WHERE (ID = @COL1 OR ManagerID = @COL1)";

        // Populate with data from the record
        command.Parameters.AddWithValue ("@COL1", ID);

        // Write to database
        command.ExecuteNonQuery ();
    }
    conn.Close ();

    // Empty class
    ID = "";
    ManagerID = "";
    Name = "";
    Occupation = "";
    isManager = false;
    _people = new NSMutableArray();

    // Save last connection
    _conn = conn;
}

여기에 SQL 관리자 레코드와 해당 관리자 (매개 변수 사용)에서 모든 직원의 레코드를 삭제를 제공 합니다.Here we provide the SQL to delete both the managers record and the records of any employees under that manager (using parameters):

// Create new command
command.CommandText = "DELETE FROM [People] WHERE (ID = @COL1 OR ManagerID = @COL1)";

// Populate with data from the record
command.Parameters.AddWithValue ("@COL1", ID);

현재 인스턴스를 취소 레코드 제거 된 후에 PersonModel 클래스:After the record has been removed, we clear out the current instance of the PersonModel class:

// Empty class
ID = "";
ManagerID = "";
Name = "";
Occupation = "";
isManager = false;
_people = new NSMutableArray();

데이터베이스를 초기화합니다.Initializing the database

읽기 및 쓰기 데이터베이스를 지원 하기 위해 현재 위치에서 데이터 모델 변경 내용을 사용 하 여 데이터베이스에 대 한 연결을 열고 첫 번째 실행에서 초기화 해야 합니다.With the changes to our Data Model in place to support reading and writing to the database, we need to open a connection to the database and initialize it on the first run. 다음 코드를 추가 해 보겠습니다 우리의 MainWindow.cs 파일:Let's add the following code to our MainWindow.cs file:

using System.Data;
using System.IO;
using Mono.Data.Sqlite;
...

private SqliteConnection DatabaseConnection = null;
...

private SqliteConnection GetDatabaseConnection() {
    var documents = Environment.GetFolderPath (Environment.SpecialFolder.Desktop);
    string db = Path.Combine (documents, "People.db3");

    // Create the database if it doesn't already exist
    bool exists = File.Exists (db);
    if (!exists)
        SqliteConnection.CreateFile (db);

    // Create connection to the database
    var conn = new SqliteConnection("Data Source=" + db);

    // Set the structure of the database
    if (!exists) {
        var commands = new[] {
            "CREATE TABLE People (ID TEXT, Name TEXT, Occupation TEXT, isManager BOOLEAN, ManagerID TEXT)"
        };
        conn.Open ();
        foreach (var cmd in commands) {
            using (var c = conn.CreateCommand()) {
                c.CommandText = cmd;
                c.CommandType = CommandType.Text;
                c.ExecuteNonQuery ();
            }
        }
        conn.Close ();

        // Build list of employees
        var Craig = new PersonModel ("0","Craig Dunn", "Documentation Manager");
        Craig.AddPerson (new PersonModel ("Amy Burns", "Technical Writer"));
        Craig.AddPerson (new PersonModel ("Joel Martinez", "Web & Infrastructure"));
        Craig.AddPerson (new PersonModel ("Kevin Mullins", "Technical Writer"));
        Craig.AddPerson (new PersonModel ("Mark McLemore", "Technical Writer"));
        Craig.AddPerson (new PersonModel ("Tom Opgenorth", "Technical Writer"));
        Craig.Create (conn);

        var Larry = new PersonModel ("1","Larry O'Brien", "API Documentation Manager");
        Larry.AddPerson (new PersonModel ("Mike Norman", "API Documentor"));
        Larry.Create (conn);
    }

    // Return new connection
    return conn;
}

위의 코드에 대해 좀 더 자세히 살펴보겠습니다.Let's take a closer look at the code above. 첫째, 새 데이터베이스 (이 예제에서 사용자의 데스크톱), 데이터베이스가 있는지 확인 하 고 그렇지 않은 경우 만듭니다 위치 선택:First, we pick a location for the new database (in this example, the user's Desktop), look to see if the database exists, and if it doesn't, create it:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.Desktop);
string db = Path.Combine (documents, "People.db3");

// Create the database if it doesn't already exist
bool exists = File.Exists (db);
if (!exists)
    SqliteConnection.CreateFile (db);

다음으로, 위에서 만든 경로 사용 하 여 데이터베이스에 연결 설정:Next, we establish the connect to the database using the path we created above:

var conn = new SqliteConnection("Data Source=" + db);

그런 다음 모든 SQL 테이블 필요한 데이터베이스에 만듭니다.Then we create all the SQL tables in the database that we require:

var commands = new[] {
    "CREATE TABLE People (ID TEXT, Name TEXT, Occupation TEXT, isManager BOOLEAN, ManagerID TEXT)"
};
conn.Open ();
foreach (var cmd in commands) {
    using (var c = conn.CreateCommand()) {
        c.CommandText = cmd;
        c.CommandType = CommandType.Text;
        c.ExecuteNonQuery ();
    }
}
conn.Close ();

마지막으로 사용 하 여 데이터 모델 (PersonModel) 응용 프로그램 실행은 첫 번째 데이터베이스 시간 또는 데이터베이스가 없는 경우에 대 한 레코드의 기본 집합을 만들려면:Finally, we use our Data Model (PersonModel) to create a default set of records for the database the first time the application is run or if the database is missing:

// Build list of employees
var Craig = new PersonModel ("0","Craig Dunn", "Documentation Manager");
Craig.AddPerson (new PersonModel ("Amy Burns", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Joel Martinez", "Web & Infrastructure"));
Craig.AddPerson (new PersonModel ("Kevin Mullins", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Mark McLemore", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Tom Opgenorth", "Technical Writer"));
Craig.Create (conn);

var Larry = new PersonModel ("1","Larry O'Brien", "API Documentation Manager");
Larry.AddPerson (new PersonModel ("Mike Norman", "API Documentor"));
Larry.Create (conn);

응용 프로그램이 시작 되 고 주 창을 시작, 우리가 위에 추가한 코드를 사용 하 여 데이터베이스에 연결:When the application starts and opens the Main Window, we make a connection to the database using the code we added above:

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

    // Get access to database
    DatabaseConnection = GetDatabaseConnection ();
}

바인딩된 데이터 로드Loading bound data

해당 위치에서 SQLite 데이터베이스에서 바인딩된 데이터를 직접 액세스 하기 위한 모든 구성 요소를 사용 하 여 응용 프로그램을 제공 하 고 UI에 자동으로 표시 되는 다양 한 보기의 데이터를 로드할 수 있습니다.With all the components for directly accessing bound data from a SQLite database in place, we can load the data in the different views that our application provides and it will automatically be displayed in our UI.

단일 레코드를 로드합니다.Loading a single record

여기서 ID는 단일 레코드 알고, 로드 하려면 다음 코드를 사용할 수 있습니다.To load a single record where the ID is know, we can use the following code:

Person = new PersonModel (Conn, "0");

모든 레코드를 로드합니다.Loading all records

경우 관리자는 여부에 관계 없이 모든 사용자를 로드 하려면 다음 코드를 사용 합니다.To load all people, regardless if they are a manager or not, use the following code:

// Load all employees
_conn.Open ();
using (var command = _conn.CreateCommand ()) {
    // Create new command
    command.CommandText = "SELECT ID FROM [People]";

    using (var reader = command.ExecuteReader ()) {
        while (reader.Read ()) {
            // Load child and add to collection
            var childID = (string)reader [0];
            var person = new PersonModel (_conn, childID);
            AddPerson (person);
        }
    }
}
_conn.Close ();

여기에서 사용 하 여 생성자의 오버 로드는 PersonModel 사용자를 메모리로 로드 하는 클래스:Here, we are using an overload of the constructor for the PersonModel class to load the person into memory:

var person = new PersonModel (_conn, childID);

사용자 컬렉션에 사용자를 추가할 데이터 바인딩된 클래스도 이라고 AddPerson (person), 이렇게 하면 UI 변경 내용을 인식 하 고 표시 합니다.We are also calling the Data Bound class to add the person to our collection of people AddPerson (person), this ensures that our UI recognizes the change and displays it:

[Export("addObject:")]
public void AddPerson(PersonModel person) {
    WillChangeValue ("personModelArray");
    isManager = true;
    _people.Add (person);
    DidChangeValue ("personModelArray");
}

최상위 수준 레코드만 로드Loading top level records only

유일한 관리자 (예: 개요 보기에서 데이터를 표시)를 로드 하려면 다음 코드를 사용 했습니다.To load only managers (for example, to display data in an Outline View), we use the following code:

// Load only managers employees
_conn.Open ();
using (var command = _conn.CreateCommand ()) {
    // Create new command
    command.CommandText = "SELECT ID FROM [People] WHERE isManager = 1";

    using (var reader = command.ExecuteReader ()) {
        while (reader.Read ()) {
            // Load child and add to collection
            var childID = (string)reader [0];
            var person = new PersonModel (_conn, childID);
            AddPerson (person);
        }
    }
}
_conn.Close ();

SQL 문의 차이점 (관리자만 로드 command.CommandText = "SELECT ID FROM [People] WHERE isManager = 1") 위의 섹션과 그렇지 않은 경우 동일 하 게 작동 하지만 합니다.The only real difference in the in SQL statement (which loads only managers command.CommandText = "SELECT ID FROM [People] WHERE isManager = 1") but works the same as the section above otherwise.

데이터베이스 및 콤보 상자Databases and comboboxes

(하거나 실행할 수 있는 Interface Builder에서 미리 정의 된 코드를 통해 채울) 내부 목록에서 드롭다운 목록을 채우는 데 사용할 수 있습니다 (예: 콤보 상자) macOS 메뉴 컨트롤을 설정할 수 있습니다 하거나 사용자 고유의 사용자 지정, 외부 데이터 소스를 제공 합니다.The Menu Controls available to macOS (such as the Combo Box) can be set to populate the dropdown list either from an internal list (that can be pre-defined in Interface Builder or populated via code) or by providing your own custom, external data source. 참조 제공 메뉴 컨트롤 데이터 대 한 자세한 내용은 합니다.See Providing Menu Control Data for more details.

예를 들어, 콤보 상자를 추가 Interface Builder에서 위의 단순 바인딩 예제를 편집 하 고 라는 콘센트를 사용 하 여 노출 EmployeeSelector:As an example, edit the Simple Binding example above in Interface Builder, add a Combo Box and expose it using an outlet named EmployeeSelector:

콤보 상자 콘센트 노출Exposing a combo box outlet

특성 검사기를 확인 합니다 자동 완성데이터 원본을 사용 하 여 속성:In the Attributes Inspector, check the Autocompletes and Uses Data Source properties:

콤보 상자 특성 구성Configuring the combo box attributes

변경 내용을 저장 하 고 동기화 하는 Mac 용 Visual Studio로 돌아갑니다.Save your changes and return to Visual Studio for Mac to sync.

콤보 상자 데이터를 제공합니다.Providing combobox data

다음으로 프로젝트에 새 클래스를 추가 ComboBoxDataSource 다음과 같을 수 있도록 합니다.Next, add a new class to the project called ComboBoxDataSource and make it look like the following:

using System;
using System.Data;
using System.IO;
using Mono.Data.Sqlite;
using Foundation;
using AppKit;

namespace MacDatabase
{
    public class ComboBoxDataSource : NSComboBoxDataSource
    {
        #region Private Variables
        private SqliteConnection _conn = null;
        private string _tableName = "";
        private string _IDField = "ID";
        private string _displayField = "";
        private nint _recordCount = 0;
        #endregion

        #region Computed Properties
        public SqliteConnection Conn {
            get { return _conn; }
            set { _conn = value; }
        }

        public string TableName {
            get { return _tableName; }
            set { 
                _tableName = value;
                _recordCount = GetRecordCount ();
            }
        }

        public string IDField {
            get { return _IDField; }
            set {
                _IDField = value; 
                _recordCount = GetRecordCount ();
            }
        }

        public string DisplayField {
            get { return _displayField; }
            set { 
                _displayField = value; 
                _recordCount = GetRecordCount ();
            }
        }

        public nint RecordCount {
            get { return _recordCount; }
        }
        #endregion

        #region Constructors
        public ComboBoxDataSource (SqliteConnection conn, string tableName, string displayField)
        {
            // Initialize
            this.Conn = conn;
            this.TableName = tableName;
            this.DisplayField = displayField;
        }

        public ComboBoxDataSource (SqliteConnection conn, string tableName, string idField, string displayField)
        {
            // Initialize
            this.Conn = conn;
            this.TableName = tableName;
            this.IDField = idField;
            this.DisplayField = displayField;
        }
        #endregion

        #region Private Methods
        private nint GetRecordCount ()
        {
            bool shouldClose = false;
            nint count = 0;

            // Has a Table, ID and display field been specified?
            if (TableName !="" && IDField != "" && DisplayField != "") {
                // Is the database already open?
                if (Conn.State != ConnectionState.Open) {
                    shouldClose = true;
                    Conn.Open ();
                }

                // Execute query
                using (var command = Conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = $"SELECT count({IDField}) FROM [{TableName}]";

                    // Get the results from the database
                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read ()) {
                            // Read count from query
                            var result = (long)reader [0];
                            count = (nint)result;
                        }
                    }
                }

                // Should we close the connection to the database
                if (shouldClose) {
                    Conn.Close ();
                }
            }

            // Return the number of records
            return count;
        }
        #endregion

        #region Public Methods
        public string IDForIndex (nint index)
        {
            NSString value = new NSString ("");
            bool shouldClose = false;

            // Has a Table, ID and display field been specified?
            if (TableName != "" && IDField != "" && DisplayField != "") {
                // Is the database already open?
                if (Conn.State != ConnectionState.Open) {
                    shouldClose = true;
                    Conn.Open ();
                }

                // Execute query
                using (var command = Conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = $"SELECT {IDField} FROM [{TableName}] ORDER BY {DisplayField} ASC LIMIT 1 OFFSET {index}";

                    // Get the results from the database
                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read ()) {
                            // Read the display field from the query
                            value = new NSString ((string)reader [0]);
                        }
                    }
                }

                // Should we close the connection to the database
                if (shouldClose) {
                    Conn.Close ();
                }
            }

            // Return results
            return value;
        }

        public string ValueForIndex (nint index)
        {
            NSString value = new NSString ("");
            bool shouldClose = false;

            // Has a Table, ID and display field been specified?
            if (TableName != "" && IDField != "" && DisplayField != "") {
                // Is the database already open?
                if (Conn.State != ConnectionState.Open) {
                    shouldClose = true;
                    Conn.Open ();
                }

                // Execute query
                using (var command = Conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] ORDER BY {DisplayField} ASC LIMIT 1 OFFSET {index}";

                    // Get the results from the database
                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read ()) {
                            // Read the display field from the query
                            value = new NSString ((string)reader [0]);
                        }
                    }
                }

                // Should we close the connection to the database
                if (shouldClose) {
                    Conn.Close ();
                }
            }

            // Return results
            return value;
        }

        public string IDForValue (string value)
        {
            NSString result = new NSString ("");
            bool shouldClose = false;

            // Has a Table, ID and display field been specified?
            if (TableName != "" && IDField != "" && DisplayField != "") {
                // Is the database already open?
                if (Conn.State != ConnectionState.Open) {
                    shouldClose = true;
                    Conn.Open ();
                }

                // Execute query
                using (var command = Conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = $"SELECT {IDField} FROM [{TableName}] WHERE {DisplayField} = @VAL";

                    // Populate parameters
                    command.Parameters.AddWithValue ("@VAL", value);

                    // Get the results from the database
                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read ()) {
                            // Read the display field from the query
                            result = new NSString ((string)reader [0]);
                        }
                    }
                }

                // Should we close the connection to the database
                if (shouldClose) {
                    Conn.Close ();
                }
            }

            // Return results
            return result;
        }
        #endregion 

        #region Override Methods
        public override nint ItemCount (NSComboBox comboBox)
        {
            return RecordCount;
        }

        public override NSObject ObjectValueForItem (NSComboBox comboBox, nint index)
        {
            NSString value = new NSString ("");
            bool shouldClose = false;

            // Has a Table, ID and display field been specified?
            if (TableName != "" && IDField != "" && DisplayField != "") {
                // Is the database already open?
                if (Conn.State != ConnectionState.Open) {
                    shouldClose = true;
                    Conn.Open ();
                }

                // Execute query
                using (var command = Conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] ORDER BY {DisplayField} ASC LIMIT 1 OFFSET {index}";

                    // Get the results from the database
                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read ()) {
                            // Read the display field from the query
                            value = new NSString((string)reader [0]);
                        }
                    }
                }

                // Should we close the connection to the database
                if (shouldClose) {
                    Conn.Close ();
                }
            }

            // Return results
            return value;
        }

        public override nint IndexOfItem (NSComboBox comboBox, string value)
        {
            bool shouldClose = false;
            bool found = false;
            string field = "";
            nint index = NSRange.NotFound;

            // Has a Table, ID and display field been specified?
            if (TableName != "" && IDField != "" && DisplayField != "") {
                // Is the database already open?
                if (Conn.State != ConnectionState.Open) {
                    shouldClose = true;
                    Conn.Open ();
                }

                // Execute query
                using (var command = Conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] ORDER BY {DisplayField} ASC";

                    // Get the results from the database
                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read () && !found) {
                            // Read the display field from the query
                            field = (string)reader [0];
                            ++index;

                            // Is this the value we are searching for?
                            if (value == field) {
                                // Yes, exit loop
                                found = true;
                            }
                        }
                    }
                }

                // Should we close the connection to the database
                if (shouldClose) {
                    Conn.Close ();
                }
            }

            // Return results
            return index;
        }

        public override string CompletedString (NSComboBox comboBox, string uncompletedString)
        {
            bool shouldClose = false;
            bool found = false;
            string field = "";

            // Has a Table, ID and display field been specified?
            if (TableName != "" && IDField != "" && DisplayField != "") {
                // Is the database already open?
                if (Conn.State != ConnectionState.Open) {
                    shouldClose = true;
                    Conn.Open ();
                }

                // Escape search string
                uncompletedString = uncompletedString.Replace ("'", "");

                // Execute query
                using (var command = Conn.CreateCommand ()) {
                    // Create new command
                    command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] WHERE {DisplayField} LIKE @VAL";

                    // Populate parameters
                    command.Parameters.AddWithValue ("@VAL", uncompletedString + "%");

                    // Get the results from the database
                    using (var reader = command.ExecuteReader ()) {
                        while (reader.Read ()) {
                            // Read the display field from the query
                            field = (string)reader [0];
                        }
                    }
                }

                // Should we close the connection to the database
                if (shouldClose) {
                    Conn.Close ();
                }
            }

            // Return results
            return field;
        }
        #endregion
    }
}

이 예에서 만들었습니다 새 NSComboBoxDataSource SQLite 데이터 소스에서 콤보 상자 항목을 제공할 수는 있습니다.In this example, we are creating a new NSComboBoxDataSource that can present Combo Box Items from any SQLite Data Source. 먼저, 다음 속성을 정의합니다.First, we define the following properties:

  • Conn -SQLite 데이터베이스에 대 한 연결을 설정 하거나 가져옵니다.Conn - Gets or sets a connection to the SQLite database.
  • TableName -테이블 이름을 가져오거나 설정 합니다를 가져옵니다.TableName - Gets or sets the table name.
  • IDField -지정된 된 테이블에 대 한 고유 ID를 제공 하는 필드를 설정 하거나 가져옵니다.IDField - Gets or sets the field that provides the unique ID for the given Table. 기본값은 ID입니다.The default value is ID.
  • DisplayField -드롭다운 목록에 표시 되는 필드를 설정 하거나 가져옵니다.DisplayField - Gets or sets the field that is displayed in the dropdown list.
  • RecordCount -지정된 된 테이블의 레코드 수를 가져옵니다.RecordCount - Gets the number of records in the given Table.

개체의 새 인스턴스를 만들 때 연결, 테이블, 필요에 따라 ID 필드 이름과 표시 필드에 전달 합니다.When we create a new instance of the object, we pass in the connection, table name, optionally the ID field and the display field:

public ComboBoxDataSource (SqliteConnection conn, string tableName, string displayField)
{
    // Initialize
    this.Conn = conn;
    this.TableName = tableName;
    this.DisplayField = displayField;
}

GetRecordCount 메서드는 지정된 된 테이블에서 레코드의 수를 반환 합니다.The GetRecordCount method returns the number of records in the given Table:

private nint GetRecordCount ()
{
    bool shouldClose = false;
    nint count = 0;

    // Has a Table, ID and display field been specified?
    if (TableName !="" && IDField != "" && DisplayField != "") {
        // Is the database already open?
        if (Conn.State != ConnectionState.Open) {
            shouldClose = true;
            Conn.Open ();
        }

        // Execute query
        using (var command = Conn.CreateCommand ()) {
            // Create new command
            command.CommandText = $"SELECT count({IDField}) FROM [{TableName}]";

            // Get the results from the database
            using (var reader = command.ExecuteReader ()) {
                while (reader.Read ()) {
                    // Read count from query
                    var result = (long)reader [0];
                    count = (nint)result;
                }
            }
        }

        // Should we close the connection to the database
        if (shouldClose) {
            Conn.Close ();
        }
    }

    // Return the number of records
    return count;
}

언제 든 지 라고 합니다 TableName, IDField 또는 DisplayField 속성 값이 변경 합니다.It is called any time the TableName, IDField or DisplayField properties value is changed.

합니다 IDForIndex 고유 ID를 반환 하는 메서드 (IDField) 지정된 드롭다운 목록 항목 인덱스에 있는 레코드에 대 한 합니다.The IDForIndex method returns the unique ID (IDField) for the record at the given dropdown list item index:

public string IDForIndex (nint index)
{
    NSString value = new NSString ("");
    bool shouldClose = false;

    // Has a Table, ID and display field been specified?
    if (TableName != "" && IDField != "" && DisplayField != "") {
        // Is the database already open?
        if (Conn.State != ConnectionState.Open) {
            shouldClose = true;
            Conn.Open ();
        }

        // Execute query
        using (var command = Conn.CreateCommand ()) {
            // Create new command
            command.CommandText = $"SELECT {IDField} FROM [{TableName}] ORDER BY {DisplayField} ASC LIMIT 1 OFFSET {index}";

            // Get the results from the database
            using (var reader = command.ExecuteReader ()) {
                while (reader.Read ()) {
                    // Read the display field from the query
                    value = new NSString ((string)reader [0]);
                }
            }
        }

        // Should we close the connection to the database
        if (shouldClose) {
            Conn.Close ();
        }
    }

    // Return results
    return value;
}

합니다 ValueForIndex 메서드 값을 반환 합니다 (DisplayField) 지정된 드롭다운 목록 인덱스의 항목에 대 한 합니다.The ValueForIndex method returns the value (DisplayField) for the item at the given dropdown list index:

public string ValueForIndex (nint index)
{
    NSString value = new NSString ("");
    bool shouldClose = false;

    // Has a Table, ID and display field been specified?
    if (TableName != "" && IDField != "" && DisplayField != "") {
        // Is the database already open?
        if (Conn.State != ConnectionState.Open) {
            shouldClose = true;
            Conn.Open ();
        }

        // Execute query
        using (var command = Conn.CreateCommand ()) {
            // Create new command
            command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] ORDER BY {DisplayField} ASC LIMIT 1 OFFSET {index}";

            // Get the results from the database
            using (var reader = command.ExecuteReader ()) {
                while (reader.Read ()) {
                    // Read the display field from the query
                    value = new NSString ((string)reader [0]);
                }
            }
        }

        // Should we close the connection to the database
        if (shouldClose) {
            Conn.Close ();
        }
    }

    // Return results
    return value;
}

합니다 IDForValue 고유 ID를 반환 하는 메서드 (IDField) 지정 된 값 (DisplayField):The IDForValue method returns the unique ID (IDField) for the given value (DisplayField):

public string IDForValue (string value)
{
    NSString result = new NSString ("");
    bool shouldClose = false;

    // Has a Table, ID and display field been specified?
    if (TableName != "" && IDField != "" && DisplayField != "") {
        // Is the database already open?
        if (Conn.State != ConnectionState.Open) {
            shouldClose = true;
            Conn.Open ();
        }

        // Execute query
        using (var command = Conn.CreateCommand ()) {
            // Create new command
            command.CommandText = $"SELECT {IDField} FROM [{TableName}] WHERE {DisplayField} = @VAL";

            // Populate parameters
            command.Parameters.AddWithValue ("@VAL", value);

            // Get the results from the database
            using (var reader = command.ExecuteReader ()) {
                while (reader.Read ()) {
                    // Read the display field from the query
                    result = new NSString ((string)reader [0]);
                }
            }
        }

        // Should we close the connection to the database
        if (shouldClose) {
            Conn.Close ();
        }
    }

    // Return results
    return result;
}

ItemCount 계산 하는 경우 목록에서 항목의 사전 계산된 수를 반환 합니다 TableName, IDField 또는 DisplayField 속성이 변경 된:The ItemCount returns the precomputed number of items in the list as calculated when the TableName, IDField or DisplayField properties are changed:

public override nint ItemCount (NSComboBox comboBox)
{
    return RecordCount;
}

합니다 ObjectValueForItem 값을 제공 하는 메서드 (DisplayField) 지정된 드롭다운 목록 항목 인덱스:The ObjectValueForItem method provides the value (DisplayField) for the given dropdown list item index:

public override NSObject ObjectValueForItem (NSComboBox comboBox, nint index)
{
    NSString value = new NSString ("");
    bool shouldClose = false;

    // Has a Table, ID and display field been specified?
    if (TableName != "" && IDField != "" && DisplayField != "") {
        // Is the database already open?
        if (Conn.State != ConnectionState.Open) {
            shouldClose = true;
            Conn.Open ();
        }

        // Execute query
        using (var command = Conn.CreateCommand ()) {
            // Create new command
            command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] ORDER BY {DisplayField} ASC LIMIT 1 OFFSET {index}";

            // Get the results from the database
            using (var reader = command.ExecuteReader ()) {
                while (reader.Read ()) {
                    // Read the display field from the query
                    value = new NSString((string)reader [0]);
                }
            }
        }

        // Should we close the connection to the database
        if (shouldClose) {
            Conn.Close ();
        }
    }

    // Return results
    return value;
}

사용 하는 LIMITOFFSET 필요 함을에서는 하나의 레코드를 제한 하는 SQLite 명령에서 문.Notice that we are using the LIMIT and OFFSET statements in our SQLite command to limit to the one record that we are needed.

합니다 IndexOfItem 메서드 반환 값의 드롭다운 항목 인덱스 (DisplayField) 지정 합니다.The IndexOfItem method returns dropdown item index of the value (DisplayField) given:

public override nint IndexOfItem (NSComboBox comboBox, string value)
{
    bool shouldClose = false;
    bool found = false;
    string field = "";
    nint index = NSRange.NotFound;

    // Has a Table, ID and display field been specified?
    if (TableName != "" && IDField != "" && DisplayField != "") {
        // Is the database already open?
        if (Conn.State != ConnectionState.Open) {
            shouldClose = true;
            Conn.Open ();
        }

        // Execute query
        using (var command = Conn.CreateCommand ()) {
            // Create new command
            command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] ORDER BY {DisplayField} ASC";

            // Get the results from the database
            using (var reader = command.ExecuteReader ()) {
                while (reader.Read () && !found) {
                    // Read the display field from the query
                    field = (string)reader [0];
                    ++index;

                    // Is this the value we are searching for?
                    if (value == field) {
                        // Yes, exit loop
                        found = true;
                    }
                }
            }
        }

        // Should we close the connection to the database
        if (shouldClose) {
            Conn.Close ();
        }
    }

    // Return results
    return index;
}

값을 찾을 수 없는 경우는 NSRange.NotFound 값이 반환 하 고 드롭다운 목록에서 모든 항목이 선택 취소 합니다.If the value cannot be found, the NSRange.NotFound value is return and all items are deselected in the dropdown list.

CompletedString 첫 번째 일치 하는 값을 반환 하는 메서드 (DisplayField) 부분적으로 형식화 된 항목에 대 한 합니다.The CompletedString method returns the first matching value (DisplayField) for a partially typed entry:

public override string CompletedString (NSComboBox comboBox, string uncompletedString)
{
    bool shouldClose = false;
    bool found = false;
    string field = "";

    // Has a Table, ID and display field been specified?
    if (TableName != "" && IDField != "" && DisplayField != "") {
        // Is the database already open?
        if (Conn.State != ConnectionState.Open) {
            shouldClose = true;
            Conn.Open ();
        }

        // Escape search string
        uncompletedString = uncompletedString.Replace ("'", "");

        // Execute query
        using (var command = Conn.CreateCommand ()) {
            // Create new command
            command.CommandText = $"SELECT {DisplayField} FROM [{TableName}] WHERE {DisplayField} LIKE @VAL";

            // Populate parameters
            command.Parameters.AddWithValue ("@VAL", uncompletedString + "%");

            // Get the results from the database
            using (var reader = command.ExecuteReader ()) {
                while (reader.Read ()) {
                    // Read the display field from the query
                    field = (string)reader [0];
                }
            }
        }

        // Should we close the connection to the database
        if (shouldClose) {
            Conn.Close ();
        }
    }

    // Return results
    return field;
}

데이터를 표시 하 고 이벤트에 응답Displaying data and responding to events

모든 부분이 함께 표시 하려면 편집을 SubviewSimpleBindingController 수 있으며 다음과 같은 모양:To bring all of the pieces together, edit the SubviewSimpleBindingController and make it look like the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.IO;
using Mono.Data.Sqlite;
using Foundation;
using AppKit;

namespace MacDatabase
{
    public partial class SubviewSimpleBindingController : AppKit.NSViewController
    {
        #region Private Variables
        private PersonModel _person = new PersonModel();
        private SqliteConnection Conn;
        #endregion

        #region Computed Properties
        //strongly typed view accessor
        public new SubviewSimpleBinding View {
            get {
                return (SubviewSimpleBinding)base.View;
            }
        }

        [Export("Person")]
        public PersonModel Person {
            get {return _person; }
            set {
                WillChangeValue ("Person");
                _person = value;
                DidChangeValue ("Person");
            }
        }

        public ComboBoxDataSource DataSource {
            get { return EmployeeSelector.DataSource as ComboBoxDataSource; }
        }
        #endregion

        #region Constructors
        // Called when created from unmanaged code
        public SubviewSimpleBindingController (IntPtr handle) : base (handle)
        {
            Initialize ();
        }

        // Called when created directly from a XIB file
        [Export ("initWithCoder:")]
        public SubviewSimpleBindingController (NSCoder coder) : base (coder)
        {
            Initialize ();
        }

        // Call to load from the XIB/NIB file
        public SubviewSimpleBindingController (SqliteConnection conn) : base ("SubviewSimpleBinding", NSBundle.MainBundle)
        {
            // Initialize
            this.Conn = conn;
            Initialize ();
        }

        // Shared initialization code
        void Initialize ()
        {
        }
        #endregion

        #region Private Methods
        private void LoadSelectedPerson (string id)
        {

            // Found?
            if (id != "") {
                // Yes, load requested record
                Person = new PersonModel (Conn, id);
            }
        }
        #endregion

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

            // Configure Employee selector dropdown
            EmployeeSelector.DataSource = new ComboBoxDataSource (Conn, "People", "Name");

            // Wireup events
            EmployeeSelector.Changed += (sender, e) => {
                // Get ID
                var id = DataSource.IDForValue (EmployeeSelector.StringValue);
                LoadSelectedPerson (id);
            };

            EmployeeSelector.SelectionChanged += (sender, e) => {
                // Get ID
                var id = DataSource.IDForIndex (EmployeeSelector.SelectedIndex);
                LoadSelectedPerson (id);
            };

            // Auto select the first person
            EmployeeSelector.StringValue = DataSource.ValueForIndex (0);
            Person = new PersonModel (Conn, DataSource.IDForIndex(0));
    
        }
        #endregion
    }
}

합니다 DataSource 속성에 대 한 바로 가기를 제공 합니다 ComboBoxDataSource (위에서 만든) 콤보 상자에 연결 합니다.The DataSource property provides a shortcut to the ComboBoxDataSource (created above) attached to the Combo Box.

LoadSelectedPerson 메서드는 데이터베이스에서 지정된 된 고유 ID에 대 한 사용자를 로드 합니다.The LoadSelectedPerson method loads the person from the database for the given Unique ID:

private void LoadSelectedPerson (string id)
{

    // Found?
    if (id != "") {
        // Yes, load requested record
        Person = new PersonModel (Conn, id);
    }
}

AwakeFromNib 메서드 재정의 먼저 사용자 지정 콤보 상자의 데이터 원본 인스턴스에 연결:In the AwakeFromNib method override, first we attach an instance of our custom Combo Box Data Source:

EmployeeSelector.DataSource = new ComboBoxDataSource (Conn, "People", "Name");

연결 된 고유 ID를 검색 하 여 콤보 상자는 텍스트 값을 편집 하는 사용자에 대응 어 (IDField) 데이터의 표시 하는 경우 지정 된 사용자를 로드 및 찾을:Next, we respond to the user editing the text value of the Combo Box by finding the associated unique ID (IDField) of the data presenting and loading the given person if found:

EmployeeSelector.Changed += (sender, e) => {
    // Get ID
    var id = DataSource.IDForValue (EmployeeSelector.StringValue);
    LoadSelectedPerson (id);
};

또한 사용자가 드롭다운 목록에서 새 항목을 선택 하면 새 사용자를 로드:We also load a new person if the user selects a new item from the dropdown list:

EmployeeSelector.SelectionChanged += (sender, e) => {
    // Get ID
    var id = DataSource.IDForIndex (EmployeeSelector.SelectedIndex);
    LoadSelectedPerson (id);
};

마지막으로,에서는 자동으로 채우려면 콤보 상자 및 목록에서 첫 번째 항목을 사용 하 여 표시 된 사용자:Finally, we auto-populate the Combo Box and displayed person with the first item in the list:

// Auto select the first person
EmployeeSelector.StringValue = DataSource.ValueForIndex (0);
Person = new PersonModel (Conn, DataSource.IDForIndex(0));

SQLite.NET ORMSQLite.NET ORM

오픈 소스를 사용 하 여 설명한 것 처럼 SQLite.NET 개체 관계 관리자 (ORM) SQLite 데이터베이스에서 데이터를 읽고 하는 데 필요한 코드의 양을 크게 줄일 수 있습니다 것입니다.As stated above, by using the open source SQLite.NET Object Relationship Manager (ORM) we can greatly reduce the amount of code required to read and write data from a SQLite database. 다양 한 키-값 코딩 및 데이터 바인딩 개체에 배치 하는 요구 사항으로 인해 데이터를 바인딩할 때 수행할 최적의 경로 아닐 수 있습니다.This may not be the best route to take when binding data because of several of the requirements that key-value coding and data binding place on an object.

SQLite.Net 웹 사이트에 따라 "SQLite는 자체 포함 된, 서버 리스, 0-구성, 트랜잭션 SQL 데이터베이스 엔진을 구현 하는 소프트웨어 라이브러리입니다. SQLite는 전 세계에서 가장 광범위 하 게 배포 된 데이터베이스 엔진입니다. SQLite에 대 한 소스 코드에 공용 도메인. "According to the SQLite.Net website, "SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine. SQLite is the most widely deployed database engine in the world. The source code for SQLite is in the public domain."

다음 섹션에서는 SQLite.Net를 사용 하 여 표 보기에 대 한 데이터를 제공 하는 방법을 살펴보겠습니다.In the following sections, we'll show how to use SQLite.Net to provide data for a Table View.

SQLite.net NuGet을 포함 하 여Including the SQLite.net NuGet

SQLite.NET 응용 프로그램에서 사용 하는 NuGet 패키지로 제공 됩니다.SQLite.NET is presented as a NuGet Package that you include in your application. SQLite.NET를 사용 하 여 데이터베이스 지원을 추가할 수 있습니다, 전에이 패키지를 포함 해야 합니다.Before we can add database support using SQLite.NET, we need to include this package.

패키지를 추가 하려면 다음을 수행 합니다.Do the following to add the package:

  1. Solution Pad를 마우스 오른쪽 단추로 클릭 합니다 패키지 폴더를 선택 패키지 추가...In the Solution Pad, right-click the Packages folder and select Add Packages...

  2. 입력 SQLite.net검색 상자 선택 합니다 sqlite net 항목:Enter SQLite.net in the Search Box and select the sqlite-net entry:

    SQLite NuGet 패키지 추가Adding the SQLite NuGet package

  3. 클릭 합니다 패키지 추가 완료 단추입니다.Click the Add Package button to finish.

데이터 모델 만들기Creating the data model

프로젝트에 새 클래스를 추가 하 고 호출 하겠습니다 OccupationModel합니다.Let's add a new class to the project and call in OccupationModel. 다음으로, 편집할를 OccupationModel.cs 파일을 다음과 같이 표시 되도록 합니다.Next, let's edit the OccupationModel.cs file and make it look like the following:

using System;
using SQLite;

namespace MacDatabase
{
    public class OccupationModel
    {
        #region Computed Properties
        [PrimaryKey, AutoIncrement]
        public int ID { get; set; }

        public string Name { get; set;}
        public string Description { get; set;}
        #endregion

        #region Constructors
        public OccupationModel ()
        {
        }

        public OccupationModel (string name, string description)
        {

            // Initialize
            this.Name = name;
            this.Description = description;

        }
        #endregion
    }
}

SQLite.NET 포함 먼저 (using Sqlite), 다음 몇 가지 속성을 노출 했습니다 각각 쓰입니다 데이터베이스로이 레코드를 저장할 때.First, we include SQLite.NET (using Sqlite), then we expose several Properties, each of which will be written to the database when this record is saved. 첫 번째 속성의 기본 키로 확인 하 고 다음과 같이 자동 증가를 설정 합니다.The first property we make as the primary key and set it to auto increment as follows:

[PrimaryKey, AutoIncrement]
public int ID { get; set; }

데이터베이스를 초기화합니다.Initializing the database

읽기 및 쓰기 데이터베이스를 지원 하기 위해 현재 위치에서 데이터 모델 변경 내용을 사용 하 여 데이터베이스에 대 한 연결을 열고 첫 번째 실행에서 초기화 해야 합니다.With the changes to our Data Model in place to support reading and writing to the database, we need to open a connection to the database and initialize it on the first run. 다음 코드를 추가 해 보겠습니다.Let's add the following code:

using SQLite;
...

public SQLiteConnection Conn { get; set; }
...

private SQLiteConnection GetDatabaseConnection() {
    var documents = Environment.GetFolderPath (Environment.SpecialFolder.Desktop);
    string db = Path.Combine (documents, "Occupation.db3");
    OccupationModel Occupation;

    // Create the database if it doesn't already exist
    bool exists = File.Exists (db);

    // Create connection to database
    var conn = new SQLiteConnection (db);

    // Initially populate table?
    if (!exists) {
        // Yes, build table
        conn.CreateTable<OccupationModel> ();

        // Add occupations
        Occupation = new OccupationModel ("Documentation Manager", "Manages the Documentation Group");
        conn.Insert (Occupation);

        Occupation = new OccupationModel ("Technical Writer", "Writes technical documentation and sample applications");
        conn.Insert (Occupation);

        Occupation = new OccupationModel ("Web & Infrastructure", "Creates and maintains the websites that drive documentation");
        conn.Insert (Occupation);

        Occupation = new OccupationModel ("API Documentation Manager", "Manages the API Documentation Group");
        conn.Insert (Occupation);

        Occupation = new OccupationModel ("API Documenter", "Creates and maintains API documentation");
        conn.Insert (Occupation);
    }

    return conn;
}

먼저 데이터베이스 (이 경우 사용자의 데스크톱)에 대 한 경로 가져옵니다 하 고 데이터베이스가 이미 있는 경우 참조:First, we get a path to the database (the User's Desktop in this case) and see if the database already exists:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.Desktop);
string db = Path.Combine (documents, "Occupation.db3");
OccupationModel Occupation;

// Create the database if it doesn't already exist
bool exists = File.Exists (db);

다음으로, 위에서 만든 경로에 데이터베이스 연결을 설정 합니다.Next, we establish a connection to the database at the path we created above:

var conn = new SQLiteConnection (db);

마지막으로 테이블을 만들 하 고 일부 기본 레코드를 추가 합니다.Finally, we create the table and add some default records:

// Yes, build table
conn.CreateTable<OccupationModel> ();

// Add occupations
Occupation = new OccupationModel ("Documentation Manager", "Manages the Documentation Group");
conn.Insert (Occupation);

Occupation = new OccupationModel ("Technical Writer", "Writes technical documentation and sample applications");
conn.Insert (Occupation);

Occupation = new OccupationModel ("Web & Infrastructure", "Creates and maintains the websites that drive documentation");
conn.Insert (Occupation);

Occupation = new OccupationModel ("API Documentation Manager", "Manages the API Documentation Group");
conn.Insert (Occupation);

Occupation = new OccupationModel ("API Documenter", "Creates and maintains API documentation");
conn.Insert (Occupation);

테이블 뷰 추가Adding a table view

사용 예제와 Xcode의 Interface builder에서 ui 테이블 뷰를 추가 하겠습니다.As an example usage, we'll add a Table View to our UI in Xcode's Interface builder. 이 테이블 뷰의 콘센트를 통해 노출 됩니다 (OccupationTable) C# 코드를 통해 액세스할 수 있도록 합니다.We'll expose this Table View via an outlet (OccupationTable) so we can access it via C# code:

테이블 보기 콘센트 노출Exposing a table view outlet

다음으로, SQLite.NET 데이터베이스에서 데이터를 사용 하 여이 테이블을 채우는 데 사용자 지정 클래스를 추가 하겠습니다.Next, we'll add the custom classes to populate this table with data from the SQLite.NET database.

테이블 데이터 원본 만들기Creating the table data source

테이블에 대 한 데이터를 제공 하는 사용자 지정 데이터 소스를 만들어 보겠습니다.Let's create a custom Data Source to provide data for our table. 라는 새 클래스를 먼저 추가 TableORMDatasource 다음과 같을 수 있도록 합니다.First, add a new class called TableORMDatasource and make it look like the following:

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

namespace MacDatabase
{
    public class TableORMDatasource : NSTableViewDataSource
    {
        #region Computed Properties
        public List<OccupationModel> Occupations { get; set;} = new List<OccupationModel>();
        public SQLiteConnection Conn { get; set; }
        #endregion

        #region Constructors
        public TableORMDatasource (SQLiteConnection conn)
        {
            // Initialize
            this.Conn = conn;
            LoadOccupations ();
        }
        #endregion

        #region Public Methods
        public void LoadOccupations() {

            // Get occupations from database
            var query = Conn.Table<OccupationModel> ();

            // Copy into table collection
            Occupations.Clear ();
            foreach (OccupationModel occupation in query) {
                Occupations.Add (occupation);
            }

        }
        #endregion

        #region Override Methods
        public override nint GetRowCount (NSTableView tableView)
        {
            return Occupations.Count;
        }
        #endregion
    }
}

나중에이 클래스의 인스턴스를 만들 때이 열린 SQLite.NET 데이터베이스 연결에 전달 합니다.When we create an instance of this class later, we'll pass in our open SQLite.NET database connection. 합니다 LoadOccupations 메서드는 데이터베이스에 쿼리하고 찾은 레코드 메모리로 복사 (사용 하 여이 OccupationModel 데이터 모델).The LoadOccupations method queries the database and copies the found records into memory (using our OccupationModel data model).

테이블 대리자 만들기Creating the table delegate

필요한 최종 클래스는 SQLite.NET 데이터베이스에서 로드 정보를 표시 하는 사용자 지정 테이블 대리자입니다.The final class we need is a custom Table Delegate to display the information that we have loaded from the SQLite.NET database. 새를 추가 해 보겠습니다 TableORMDelegate 프로젝트에 다음과 같을 수 있도록 합니다.Let's add a new TableORMDelegate to our project and make it look like the following:

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

namespace MacDatabase
{
    public class TableORMDelegate : NSTableViewDelegate
    {
        #region Constants 
        private const string CellIdentifier = "OccCell";
        #endregion

        #region Private Variables
        private TableORMDatasource DataSource;
        #endregion

        #region Constructors
        public TableORMDelegate (TableORMDatasource dataSource)
        {
            // Initialize
            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 "Occupation":
                view.StringValue = DataSource.Occupations [(int)row].Name;
                break;
            case "Description":
                view.StringValue = DataSource.Occupations [(int)row].Description;
                break;
            }

            return view;
        }
        #endregion
    }
}

여기서 사용 하 여 데이터 원본의 Occupations 컬렉션 (즉 SQLite.NET 데이터베이스에서 로드)를 통해 테이블의 열에 맞게는 GetViewForItem 메서드 재정의 합니다.Here we use the Data Source's Occupations collection (that we loaded from the SQLite.NET database) to fill in the columns of our table via the GetViewForItem method override.

테이블 채우기Populating the table

모든 위치에서 요소를 사용 하 여 보겠습니다 테이블 때 채울.xib 파일에서 재정의 하 여 팽창 됩니다는 AwakeFromNib 메서드와 보이게 하 여 다음과 같은:With all of the pieces in place, let's populate our table when it is inflated from the .xib file by overriding the AwakeFromNib method and making it look like the following:

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

    // Get database connection
    Conn = GetDatabaseConnection ();

    // Create the Occupation Table Data Source and populate it
    var DataSource = new TableORMDatasource (Conn);

    // Populate the Product Table
    OccupationTable.DataSource = DataSource;
    OccupationTable.Delegate = new TableORMDelegate (DataSource);
}

먼저 것에 액세스할 SQLite.NET 데이터베이스를 만들고 이미 존재 하지 않는 경우 채우기 있습니다.First, we gain access to our SQLite.NET database, creating and populating it if it doesn't already exist. 새 인스턴스를 만들겠습니다. 그런 다음 사용자 지정 테이블 데이터 원본에서의 데이터베이스 연결에서 전달 하 고 테이블에 연결 했습니다.Next, we create a new instance of our custom Table Data Source, pass in our database connection and we attach it to the Table. 마지막으로,이 사용자 지정 테이블 대리자의 새 인스턴스를 만들고, 데이터 원본에 전달 하 고 테이블에 연결 합니다.Finally, we create a new instance of our custom Table Delegate, pass in our Data Source and attach it to the table.

요약Summary

이 문서에서는 자세히 살펴보고 데이터 바인딩 및 Xamarin.Mac 응용 프로그램에서 SQLite 데이터베이스를 사용 하 여 키-값 코딩 작업을 수행 했습니다.This article has taken a detailed look at working with data binding and key-value coding with SQLite databases in a Xamarin.Mac application. 첫째,이 키-값 관찰 (KVO) 및 키-값 코딩 (KVC)를 사용 하 여 Objective-c를 C# 클래스를 노출 살펴보았습니다.First, it looked at exposing a C# class to Objective-C by using key-value coding (KVC) and key-value observing (KVO). 다음으로 KVO 호환 클래스를 사용 하는 방법에 알아보았습니다 하 고 데이터 Xcode의 Interface Builder에서 UI 요소에 바인딩합니다.Next, it showed how to use a KVO compliant class and Data Bind it to UI elements in Xcode's Interface Builder. 문서는 SQLite.NET ORM 통해 SQLite 데이터로 작업 하 고 해당 데이터를 테이블 보기로 표시에 대해서도 다룹니다.The article also covered working with SQLite data via the SQLite.NET ORM and displaying that data in a Table View.