Vinculação de dados e codificação de chave-valor no Xamarin.Mac

Este artigo aborda o uso da codificação de chave-valor e da observação de valor-chave para permitir a vinculação de dados a elementos da interface do usuário no Construtor de Interfaces do Xcode.

Visão geral

Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, você tem acesso às mesmas técnicas de codificação de chave-valor e vinculação de dados que um desenvolvedor que trabalha no Objective-C Xcode faz. Como o Xamarin.Mac se integra diretamente ao Xcode, você pode usar o Construtor de Interfaces do Xcode para Vincular Dados com elementos da interface do usuário em vez de escrever código.

Usando técnicas de codificação de chave-valor e vinculação de dados em seu aplicativo Xamarin.Mac, você pode diminuir consideravelmente a quantidade de código que precisa escrever e manter para preencher e trabalhar com elementos da interface do usuário. Você também tem o benefício de desacoplar ainda mais seus dados de suporte (Modelo de Dados) da Interface do Usuário front-end (Model-View-Controller), levando a um design de aplicativo mais fácil de manter e mais flexível.

An example of the running app

Neste artigo, abordaremos os conceitos básicos de como trabalhar com codificação de chave-valor e vinculação de dados em um aplicativo Xamarin.Mac. É altamente recomendável que você trabalhe primeiro no artigo Olá, Mac, especificamente nas seções Introdução ao Xcode e ao Construtor de Interface e Saídas e Ações, pois ele aborda os principais conceitos e técnicas que usaremos neste artigo.

Você pode querer dar uma olhada na seção Expondo classes C# / métodos paraObjective-Cdo documento Xamarin.Mac Internals também, ele explica os Register atributos e Export usados para conectar suas classes C# a Objective-C objetos e elementos da interface do usuário.

O que é codificação chave-valor

A codificação de chave-valor (KVC) é um mecanismo para acessar as propriedades de um objeto indiretamente, usando chaves (cadeias de caracteres especialmente formatadas) para identificar propriedades em vez de acessá-las por meio de variáveis de instância ou métodos de acessador (get/set). Ao implementar acessadores compatíveis com codificação de chave-valor em seu aplicativo Xamarin.Mac, você obtém acesso a outros recursos do macOS (anteriormente conhecido como OS X), como observação de chave-valor (KVO), vinculação de dados, dados principais, ligações de cacau e scriptabilidade.

Usando técnicas de codificação de chave-valor e vinculação de dados em seu aplicativo Xamarin.Mac, você pode diminuir consideravelmente a quantidade de código que precisa escrever e manter para preencher e trabalhar com elementos da interface do usuário. Você também tem o benefício de desacoplar ainda mais seus dados de suporte (Modelo de Dados) da Interface do Usuário front-end (Model-View-Controller), levando a um design de aplicativo mais fácil de manter e mais flexível.

Por exemplo, vamos examinar a seguinte definição de classe de um objeto compatível com KVC:

using System;
using Foundation;

namespace MacDatabinding
{
    [Register("PersonModel")]
    public class PersonModel : NSObject
    {
        private string _name = "";

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

        public PersonModel ()
        {
        }
    }
}

Primeiro, o [Register("PersonModel")] atributo registra a classe e a expõe a Objective-C. Em seguida, a classe precisa herdar de NSObject (ou uma subclasse que herda de NSObject), isso adiciona vários métodos base que permitem que a classe seja compatível com KVC. Em seguida, o [Export("Name")] atributo expõe a Name propriedade e define o valor Key que será usado posteriormente para acessar a propriedade por meio das técnicas KVC e KVO.

Finalmente, para poder ser Key-Value Observed alterações no valor da propriedade, o acessador deve encapsular as alterações em seu valor em WillChangeValue e DidChangeValue chamadas de método (especificando a mesma chave que o Export atributo). Por exemplo:

set {
    WillChangeValue ("Name");
    _name = value;
    DidChangeValue ("Name");
}

Essa etapa é muito importante para a vinculação de dados no Construtor de Interface do Xcode (como veremos mais adiante neste artigo).

Para obter mais informações, consulte o Guia de Programação de Codificação de Chave-Valor da Apple.

Chaves e caminhos-chave

Uma chave é uma cadeia de caracteres que identifica uma propriedade específica de um objeto. Normalmente, uma chave corresponde ao nome de um método acessador em um objeto compatível com codificação chave-valor. As chaves devem usar codificação ASCII, geralmente começam com uma letra minúscula e não podem conter espaço em branco. Assim, dado o exemplo acima, Name seria um Valor Chave da Name propriedade da PersonModel classe. A chave e o nome do imóvel que eles expõem não precisam ser os mesmos, mas na maioria dos casos são.

Um Caminho de Chave é uma cadeia de caracteres de Chaves separadas por pontos usada para especificar uma hierarquia de propriedades de objeto a serem percorridas. A propriedade da primeira chave na sequência é relativa ao receptor, e cada chave subsequente é avaliada em relação ao valor da propriedade anterior. Da mesma forma, você usa a notação de ponto para percorrer um objeto e suas propriedades em uma classe C#.

Por exemplo, se você expandiu a classe e adicionou Child a PersonModel propriedade:

using System;
using Foundation;

namespace MacDatabinding
{
    [Register("PersonModel")]
    public class PersonModel : NSObject
    {
        private string _name = "";
        private PersonModel _child = new PersonModel();

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

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

        public PersonModel ()
        {
        }
    }
}

O Caminho da Chave para o nome da criança seria self.Child.Name ou simplesmente Child.Name (com base em como o Valor da Chave estava sendo usado).

Obtendo valores usando codificação chave-valor

O ValueForKey método retorna o valor para a chave especificada (como um NSString), relativo à instância da classe KVC que recebe a solicitação. Por exemplo, se Person for uma instância da PersonModel classe definida acima:

// Read value
var name = Person.ValueForKey (new NSString("Name"));

Isso retornaria o valor da Name propriedade para essa instância de PersonModel.

Definindo valores usando codificação chave-valor

Da mesma forma, o SetValueForKey define o valor para a chave especificada (como um NSString), relativo à instância da classe KVC que recebe a solicitação. Então, novamente, usando uma instância da PersonModel classe, como mostrado abaixo:

// Write value
Person.SetValueForKey(new NSString("Jane Doe"), new NSString("Name"));

Alteraria o Name valor da propriedade para Jane Doe.

Observando mudanças de valor

Usando a observação de chave-valor (KVO), você pode anexar um observador a uma chave específica de uma classe compatível com KVC e ser notificado sempre que o valor dessa chave for modificado (usando técnicas KVC ou acessando diretamente a propriedade dada no código C#). Por exemplo:

// Watch for the name value changing
Person.AddObserver ("Name", NSKeyValueObservingOptions.New, (sender) => {
    // Inform caller of selection change
    Console.WriteLine("New Name: {0}", Person.Name)
});

Agora, sempre que a Name propriedade da Person instância da PersonModel classe é modificada, o novo valor é gravado no console.

Para obter mais informações, consulte o Guia de Programação de Observação de Valor-Chave da Apple.

Vinculação de dados

As seções a seguir mostrarão como você pode usar uma codificação de chave-valor e uma classe compatível de observação de chave-valor para vincular dados a elementos da interface do usuário no Construtor de Interface do Xcode, em vez de ler e gravar valores usando código C#. Dessa forma, você separa seu Modelo de Dados das exibições que são usadas para exibi-los, tornando o aplicativo Xamarin.Mac mais flexível e fácil de manter. Você também diminui muito a quantidade de código que precisa ser gravado.

Definindo seu modelo de dados

Antes de poder vincular dados a um elemento de interface do usuário no Construtor de Interfaces, você deve ter uma classe compatível com KVC/KVO definida em seu aplicativo Xamarin.Mac para atuar como o Modelo de Dados para a associação. O Modelo de Dados fornece todos os dados que serão exibidos na Interface do Usuário e recebe quaisquer modificações nos dados que o usuário faz na interface do usuário durante a execução do aplicativo.

Por exemplo, se você estivesse escrevendo um aplicativo que gerenciava um grupo de funcionários, poderia usar a seguinte classe para definir o Modelo de Dados:

using System;
using Foundation;
using AppKit;

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

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

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

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

        [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;
        }
        #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
    }
}

A maioria dos recursos dessa classe foi abordada na seção O que é codificação de chave-valor acima. No entanto, vamos examinar alguns elementos específicos e algumas adições que foram feitas para permitir que essa classe atue como um Modelo de Dados para Controladores de Matriz e Controladores de Árvore (que usaremos posteriormente para Exibições de Árvore de Vinculação de Dados, Exibições de Estrutura de Tópicos e Exibições de Coleção).

Primeiro, como um funcionário pode ser um gerente, usamos um NSArray (especificamente um NSMutableArray para que os valores possam ser modificados) para permitir que os funcionários que eles gerenciaram sejam anexados a eles:

private NSMutableArray _people = new NSMutableArray();
...

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

Duas coisas a observar aqui:

  1. Usamos uma NSMutableArray matriz ou coleção C# em vez de uma padrão, pois esse é um requisito para a Vinculação de Dados a controles do AppKit, como Exibições de Tabela, Exibições de Estrutura de Tópicos e Coleções.
  2. Expusemos a matriz de funcionários convertendo-a em um NSArray para fins de vinculação de dados e alteramos seu nome formatado em C#, People, para um que a vinculação de dados espera, personModelArray no formato {class_name}Array (observe que o primeiro caractere foi tornado minúsculo).

Em seguida, precisamos adicionar alguns métodos públicos de nome especial para suportar Controladores de Array e Controladores de Árvore:

[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");
}

Eles permitem que os controladores solicitem e modifiquem os dados que eles exibem. Como o exposto NSArray acima, eles têm uma convenção de nomenclatura muito específica (que difere das convenções de nomenclatura C# típicas):

  • addObject: - Adiciona um objeto à matriz.
  • insertObject:in{class_name}ArrayAtIndex: - Onde {class_name} está o nome da sua turma. Esse método insere um objeto na matriz em um determinado índice.
  • removeObjectFrom{class_name}ArrayAtIndex: - Onde {class_name} está o nome da sua turma. Esse método remove o objeto na matriz em um determinado índice.
  • set{class_name}Array: - Onde {class_name} está o nome da sua turma. Este método permite que você substitua o transporte existente por um novo.

Dentro desses métodos, encapsulamos as alterações na WillChangeValue matriz e DidChangeValue as mensagens para conformidade com o KVO.

Finalmente, como a Icon propriedade depende do valor da isManager propriedade, as isManager alterações na propriedade podem não ser refletidas nos elementos da interface do usuário vinculados a dados (durante o Icon KVO):

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

Para corrigir isso, usamos o seguinte código:

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

Observe que, além de sua própria Chave, o isManager acessador também está enviando as WillChangeValue mensagens e DidChangeValue para a Icon Chave para que ele veja a alteração também.

Usaremos o PersonModel Modelo de Dados durante o restante deste artigo.

Vinculação de dados simples

Com nosso Modelo de Dados definido, vejamos um exemplo simples de vinculação de dados no Construtor de Interfaces do Xcode. Por exemplo, vamos adicionar um formulário ao nosso aplicativo Xamarin.Mac que pode ser usado para editar o PersonModel que definimos acima. Adicionaremos alguns campos de texto e uma caixa de seleção para exibir e editar as propriedades do nosso modelo.

Primeiro, vamos adicionar um novo View Controller ao nosso arquivo Main.storyboard no Interface Builder e nomear sua classeSimpleViewController:

Adding a new view controller with a class named SimpleViewController.

Em seguida, retorne ao Visual Studio para Mac, edite o arquivo de SimpleViewController.cs (que foi adicionado automaticamente ao nosso projeto) e exponha uma instância do PersonModel que vincularemos nossos dados ao formulário. Adicione os códigos a seguir:

private PersonModel _person = new PersonModel();
...

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

Em seguida, quando a exibição for carregada, vamos criar uma instância nossa PersonModel e preenchê-la com este código:

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

    // Set a default person
    var Craig = new PersonModel ("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"));
    Person = Craig;

}

Agora precisamos criar nosso formulário, clique duas vezes no arquivo Main.storyboard para abri-lo para edição no Construtor de Interfaces. Esquematize o formulário para ter a seguinte aparência:

Editing the storyboard in Xcode

Para vincular o formulário ao PersonModel formulário que expusemos por meio da chave, faça o Person seguinte:

  1. Selecione o Campo de Texto Nome do Funcionário e alterne para o Inspetor de Ligações.

  2. Marque a caixa Vincular a e selecione Controlador de Exibição Simples na lista suspensa. Em seguida, insira self.Person.Name para o Caminho da chave:

    Entering self dot person dot name for the Key Path.

  3. Selecione o Campo de texto de ocupação, marque a caixa Vincular a e selecione Controlador de exibição simples na lista suspensa. Em seguida, insira self.Person.Occupation para o Caminho da chave:

    Entering self dot Person dot Occupation for the Key Path.

  4. Marque a caixa de seleção Funcionário é um gerente , marque a caixa Vincular a e selecione Controlador de exibição simples na lista suspensa. Em seguida, insira self.Person.isManager para o Caminho da chave:

    Entering self dot Person dot isManager for the Key Path.

  5. Selecione o campo Número de Funcionários Texto Gerenciado e marque a caixa Vincular a e selecione Controlador de Exibição Simples na lista suspensa. Em seguida, insira self.Person.NumberOfEmployees para o Caminho da chave:

    Entering self dot Person dot NumberOfEmployees for the Key Path.

  6. Se o funcionário não for um gerente, desejamos ocultar o Rótulo e o Campo de Texto Número de Funcionários Gerenciados.

  7. Selecione o Rótulo Número de Funcionários Gerenciados , expanda o turndown Oculto e marque a caixa Vincular a e selecione Controlador de Exibição Simples na lista suspensa. Em seguida, insira self.Person.isManager para o Caminho da chave:

    Entering self dot Person dot isManager for the Key Path for non-managers.

  8. Selecione NSNegateBoolean na lista suspensa Value Transformer :

    Selecting the NSNegateBoolean key transformation

  9. Isso informa à vinculação de dados que o rótulo ficará oculto se o valor da isManager propriedade for false.

  10. Repita as etapas 7 e 8 para o campo de texto Número de funcionários gerenciados .

  11. Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar com o Xcode.

Se você executar o aplicativo, os valores da Person propriedade preencherão automaticamente nosso formulário:

Showing an auto-populated form

Quaisquer alterações feitas pelos usuários no formulário serão gravadas de volta Person na propriedade no View Controller. Por exemplo, desmarcar Funcionário é um Gerente atualiza a Person instância de nosso PersonModel e o Número de Funcionários Gerenciado Rótulo e Campo de Texto ficam ocultos automaticamente (por meio de associação de dados):

Hiding the number of employees for non-managers

Vinculação de dados do modo de exibição de tabela

Agora que temos os conceitos básicos de vinculação de dados fora do caminho, vamos examinar uma tarefa de vinculação de dados mais complexa usando um Controlador de Matriz e vinculação de dados a uma Exibição de Tabela . Para obter mais informações sobre como trabalhar com Exibições de Tabela, consulte nossa documentação de Exibições de Tabela .

Primeiro, vamos adicionar um novo View Controller ao nosso arquivo Main.storyboard no Interface Builder e nomear sua classeTableViewController:

Adding a new view controller with a class named TableViewController.

Em seguida, vamos editar o arquivo TableViewController.cs (que foi adicionado automaticamente ao nosso projeto) e expor uma matriz (NSArray) de PersonModel classes às quais vincularemos nosso formulário. Adicione os códigos a seguir:

private NSMutableArray _people = new NSMutableArray();
...

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

[Export("addObject:")]
public void AddPerson(PersonModel person) {
    WillChangeValue ("personModelArray");
    _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");
}

Assim como fizemos na PersonModel classe acima na seção Definindo seu Modelo de Dados, expomos quatro métodos públicos especialmente nomeados para que o Controlador de Matriz e os dados de leitura e gravação de nossa coleção de PersonModels.

Em seguida, quando a exibição for carregada, precisamos preencher nossa matriz com este código:

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

    // Build list of employees
    AddPerson (new PersonModel ("Craig Dunn", "Documentation Manager", true));
    AddPerson (new PersonModel ("Amy Burns", "Technical Writer"));
    AddPerson (new PersonModel ("Joel Martinez", "Web & Infrastructure"));
    AddPerson (new PersonModel ("Kevin Mullins", "Technical Writer"));
    AddPerson (new PersonModel ("Mark McLemore", "Technical Writer"));
    AddPerson (new PersonModel ("Tom Opgenorth", "Technical Writer"));
    AddPerson (new PersonModel ("Larry O'Brien", "API Documentation Manager", true));
    AddPerson (new PersonModel ("Mike Norman", "API Documenter"));

}

Agora precisamos criar nossa Exibição de Tabela, clique duas vezes no arquivo Main.storyboard para abri-lo para edição no Construtor de Interfaces. Esquematize a tabela para ter a seguinte aparência:

Laying out a new table view

Precisamos adicionar um controlador de matriz para fornecer dados vinculados à nossa tabela, faça o seguinte:

  1. Arraste um controlador de matriz do Inspetor de biblioteca para o Editor de interface:

    Selecting an Array Controller from the Library

  2. Selecione Controlador de Matriz na Hierarquia de Interface e alterne para o Inspetor de Atributos:

    Selecting the Attributes Inspector

  3. Digite PersonModel o Nome da classe, clique no botão de adição e adicione três chaves. Nomeie-os Namee OccupationisManager:

    Adding the required key paths to the Object Controller.

  4. Isso informa ao Controlador de Matriz do que ele está gerenciando uma matriz e quais propriedades ele deve expor (via Chaves).

  5. Alterne para o Inspetor de Ligações e, em Matriz de Conteúdo , selecione Vincular a e Controlador de Exibição de Tabela. Insira um caminho de chave de modelo de self.personModelArray:

    Entering a key path

  6. Isso vincula o Controlador de Matriz à matriz que expomos em nosso Controlador de PersonModels Exibição.

Agora precisamos vincular nossa Exibição de Tabela ao Controlador de Matriz, faça o seguinte:

  1. Selecione o Modo de Exibição de Tabela e o Inspetor de Vinculação:

    Selecting the Table View and Binding Inspector.

  2. Na lista de ativação Conteúdo da Tabela, selecione Vincular a e Controlador de Matriz. Insira arrangedObjects o campo Chave do controlador:

    Defining the controller key

  3. Selecione a Célula de Exibição de Tabela na coluna Funcionário . No Inspetor de Ligações, sob obotão de ativação Valor, selecione Vincular a e Modo de Exibição de Célula da Tabela. Digite objectValue.Name para o caminho da chave do modelo:

    Setting the model key path for the Employee column.

  4. objectValue é a corrente PersonModel na matriz que está sendo gerenciada pelo Controlador de Matriz.

  5. Selecione a Célula de Exibição de Tabela na coluna Ocupação . No Inspetor de Ligações, sob obotão de ativação Valor, selecione Vincular a e Modo de Exibição de Célula da Tabela. Digite objectValue.Occupation para o caminho da chave do modelo:

    Setting the model key path for the Occupation column.

  6. Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar com o Xcode.

Se executarmos o aplicativo, a tabela será preenchida com nossa matriz de PersonModels:

Running the application, which populates the array of PersonModels.

Vinculação de dados do modo de exibição de estrutura de tópicos

a vinculação de dados em um Modo de Exibição de Estrutura de Tópicos é muito semelhante à vinculação em um Modo de Exibição de Tabela. A principal diferença é que usaremos um Controlador de Árvore em vez de um Controlador de Matriz para fornecer os dados vinculados à Exibição de Estrutura de Tópicos. Para obter mais informações sobre como trabalhar com Modos de Exibição de Estrutura de Tópicos, consulte nossa documentação de Modos de Exibição de Estrutura de Tópicos .

Primeiro, vamos adicionar um novo View Controller ao nosso arquivo Main.storyboard no Interface Builder e nomear sua classeOutlineViewController:

Adding a new view controller with a class named OutlineViewController.

Em seguida, vamos editar o arquivo OutlineViewController.cs (que foi adicionado automaticamente ao nosso projeto) e expor uma matriz (NSArray) de PersonModel classes às quais vincularemos nossos dados. Adicione os códigos a seguir:

private NSMutableArray _people = new NSMutableArray();
...

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

[Export("addObject:")]
public void AddPerson(PersonModel person) {
    WillChangeValue ("personModelArray");
    _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");
}

Assim como fizemos na PersonModel classe acima na seção Definindo seu Modelo de Dados, expomos quatro métodos públicos especialmente nomeados para que o Controlador de Árvore e os dados de leitura e gravação de nossa coleção de PersonModels.

Em seguida, quando a exibição for carregada, precisamos preencher nossa matriz com este código:

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

    // Build list of employees
    var Craig = new PersonModel ("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"));
    AddPerson (Craig);

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

}

Agora precisamos criar nossa Exibição de Estrutura de Tópicos, clique duas vezes no arquivo Main.storyboard para abri-lo para edição no Construtor de Interfaces. Esquematize a tabela para ter a seguinte aparência:

Creating the outline view

Precisamos adicionar um controlador de árvore para fornecer dados vinculados ao nosso esboço, faça o seguinte:

  1. Arraste um controlador de árvore do Inspetor de biblioteca para o Editor de interface:

    Selecting a Tree Controller from the Library

  2. Selecione Controlador de árvore na hierarquia de interface e alterne para o Inspetor de atributos:

    Selecting the Attribute Inspector

  3. Digite PersonModel o Nome da classe, clique no botão de adição e adicione três chaves. Nomeie-os Namee OccupationisManager:

    Adding the required key paths for PersonModel.

  4. Isso informa ao Controlador de Árvore do que ele está gerenciando uma matriz e quais propriedades ele deve expor (via Chaves).

  5. Na seção Controlador de Árvore , insira personModelArray para Filhos, entre NumberOfEmployees em Contagem e entre isEmployee em Folha:

    Setting the Tree Controller key paths

  6. Isso informa ao Controlador de Árvore onde encontrar qualquer nó filho, quantos nós filho existem e se o nó atual tem nós filho.

  7. Alterne para o Inspetor de Ligações e, em Matriz de Conteúdo , selecione Vincular ao Proprietário do Arquivo. Insira um caminho de chave de modelo de self.personModelArray:

    Editing the key path

  8. Isso vincula o Controlador de Árvore à matriz que expomos em nosso Controlador de PersonModels Exibição.

Agora precisamos vincular nossa Exibição de Estrutura de Tópicos ao Controlador de Árvore, faça o seguinte:

  1. Selecione o Modo de Exibição de Estrutura de Tópicos e, no Inspetor de Vinculação, selecione :

    Selecting the Outline View and Binding Inspector.

  2. No turndown Conteúdo da Exibição de Estrutura de Tópicos, selecione Vincular a e Controlador de Árvore. Insira arrangedObjects o campo Chave do controlador:

    Setting the controller key

  3. Selecione a Célula de Exibição de Tabela na coluna Funcionário . No Inspetor de Ligações, sob obotão de ativação Valor, selecione Vincular a e Modo de Exibição de Célula da Tabela. Digite objectValue.Name para o caminho da chave do modelo:

    Entering the model key path value objectValue dot Name.

  4. objectValue é a corrente PersonModel na matriz que está sendo gerenciada pelo Controlador de Árvore.

  5. Selecione a Célula de Exibição de Tabela na coluna Ocupação . No Inspetor de Ligações, sob obotão de ativação Valor, selecione Vincular a e Modo de Exibição de Célula da Tabela. Digite objectValue.Occupation para o caminho da chave do modelo:

    Entering the model key path value objectValue dot Occupation.

  6. Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar com o Xcode.

Se executarmos o aplicativo, a estrutura de tópicos será preenchida com nossa matriz de PersonModels:

Running the application, which populates our array of PersonModels.

Vinculação de dados do modo de exibição de coleta

A vinculação de dados com um Modo de Exibição de Coleta é muito semelhante à vinculação com um Modo de Exibição de Tabela, pois um Controlador de Matriz é usado para fornecer dados para a coleção. Como o modo de exibição de coleção não tem um formato de exibição predefinido, é necessário mais trabalho para fornecer feedback de interação do usuário e acompanhar a seleção do usuário.

Importante

Devido a um problema no Xcode 7 e no macOS 10.11 (e superiores), as Visualizações de coleção não podem ser usadas dentro de arquivos de Storyboard (.storyboard). Como resultado, você precisará continuar a usar arquivos .xib para definir suas Visualizações de Coleção para seus aplicativos Xamarin.Mac. Consulte nossa documentação de Visualizações de coleção para obter mais informações.

Depurando falhas nativas

Cometer um erro em suas ligações de dados pode resultar em uma falha nativa no código não gerenciado e fazer com que seu aplicativo Xamarin.Mac falhe completamente com um SIGABRT erro:

Example of a native crash dialog box

Normalmente, há quatro causas principais para falhas nativas durante a vinculação de dados:

  1. Seu Modelo de Dados não herda de NSObject ou uma subclasse de NSObject.
  2. Você não expôs sua propriedade ao Objective-C uso do [Export("key-name")] atributo.
  3. Você não encapsulava as alterações no valor do acessador em WillChangeValue e DidChangeValue chamadas de método (especificando a mesma chave que o Export atributo).
  4. Você tem uma chave errada ou digitada incorretamente no Inspetor de vinculação no Construtor de Interfaces.

Decodificando uma falha

Vamos causar uma falha nativa em nossa vinculação de dados para que possamos mostrar como localizá-la e corrigi-la. No Construtor de Interfaces, vamos alterar nossa associação do primeiro Rótulo no exemplo de Modo de Exibição de Coleção de Name para Title:

Editing the binding key

Vamos salvar a alteração, voltar para o Visual Studio para Mac para sincronizar com o Xcode e executar nosso aplicativo. Quando o Modo de Exibição de Coleção é exibido, o aplicativo falhará momentaneamente com um SIGABRT erro (conforme mostrado na Saída do Aplicativo no Visual Studio para Mac), uma vez que o PersonModel não expõe uma propriedade com a Chave Title:

Example of a binding error

Se rolarmos até o topo do erro na saída do aplicativo, podemos ver a chave para resolver o problema:

Finding the issue in the error log

Essa linha está nos dizendo que a chave Title não existe no objeto ao qual estamos vinculando. Se alterarmos a vinculação de volta para Name no Construtor de Interfaces, salvar, sincronizar, reconstruir e executar, o aplicativo será executado conforme o esperado sem problemas.

Resumo

Este artigo deu uma olhada detalhada no trabalho com vinculação de dados e codificação de chave-valor em um aplicativo Xamarin.Mac. Primeiro, ele analisou a exposição de uma classe C# usando Objective-C codificação de chave-valor (KVC) e observação de valor-chave (KVO). Em seguida, ele mostrou como usar uma classe compatível com KVO e vinculá-la a elementos da interface do usuário no Construtor de Interface do Xcode. Finalmente, mostrou a complexa vinculação de dados usando Controladores de Matriz e Controladores de Árvore.