пятница, 27 августа 2010 г.

Использование форм и метаданных в Silverlight 4. Часть 5

Еще немного поработаем с формами и метаданными.

Как мы помним, сейчас у нас не отображается название города, с которыс ассоциирован данный кастомер.

clip_image002

Это серьезная проблема, нужно ее исправить.

Для начала нужно изменить запрос в веб-сервисе так, чтобы он включал в сущности кастомеров информацию о городах.

public IQueryable<Customer> GetCustomers()

{

return this.ObjectContext.Customers.Include("Town");

}

В файле с метаданными нужно отметить атрибутом Include свойство Town.

// The MetadataTypeAttribute identifies CustomerMetadata as the class

// that carries additional metadata for the Customer class.

[MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]

public partial class Customer

{

// This class allows you to attach custom attributes to properties

// of the Customer class.

//

// For example, the following marks the Xyz property as a

// required property and specifies the format for valid values:

// [Required]

// [RegularExpression("[A-Z][A-Za-z0-9]*")]

// [StringLength(32)]

// public string Xyz { get; set; }

internal sealed class CustomerMetadata

{

// Metadata classes are not meant to be instantiated.

private CustomerMetadata()

{

}

[Display(AutoGenerateField=false)]

public int ID { get; set; }

[Display(Order=1, Name="Customer Name")]

public string Name { get; set; }

[Include]

[Display(Order = 0, Name = "Customer Town")]

public Town Town { get; set; }

[Display(AutoGenerateField = false)]

public int TownID { get; set; }

}

}

Сейчас увидим следующую информацию в форме:

clip_image004

По крайней мере, это не пустое поле, но это все равно не совсем то, что нужно.

Оптимально было бы добавить вместо текстового поля комбобокс с возможностью выбрать город. Стандартных средств для этого в Silverlight нет, но решение досточно легко найти в интернете.

Элемент DataForm поддерживает возможность добавлять поля вручную, используя свойство DataForm.Fields, но это не поможет при большом количестве полей в форме (в таком случае придется описывать каждое поле).

Комбобокс мы добавим чуть позже, а пока просто посмотрим несколько способов, при помощи которых, можно корректно выводить информацию о городе в текстовое поле.

Способ 1. Связывание с данными во время выполнения.

Для этого нам нужно во время создания полей DataForm перехватить поле, отвечающее за название города и изменить для него источник данных.

<dataControls:DataForm CurrentItem="{Binding ElementName=customerDataGrid, Path=SelectedItem, Mode=TwoWay}"

AutoGenerateFields="True"

AutoCommit="False"

AutoEdit="False"

Width="400"

EditEnded="DataForm_EditEnded"

AutoGeneratingField="DataForm_AutoGeneratingField">

</dataControls:DataForm>

private void DataForm_AutoGeneratingField(object sender, DataFormAutoGeneratingFieldEventArgs e)

{

if (e.PropertyName == "Town")

{

var txtBox = (e.Field.Content as TextBox);

Binding binding = new Binding("Town.Name");

txtBox.SetBinding(TextBox.TextProperty, binding);

}

}

Способ 2. Создание дополнительного свойства в сущности из модели данных.

Добавим новое свойство в тип Customer. Для этого нужно изменить метаданные следующим образом (добавляем новое текстоное свойство TownName):

// The MetadataTypeAttribute identifies CustomerMetadata as the class

// that carries additional metadata for the Customer class.

[MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]

public partial class Customer

{

// This class allows you to attach custom attributes to properties

// of the Customer class.

//

// For example, the following marks the Xyz property as a

// required property and specifies the format for valid values:

// [Required]

// [RegularExpression("[A-Z][A-Za-z0-9]*")]

// [StringLength(32)]

// public string Xyz { get; set; }

internal sealed class CustomerMetadata

{

// Metadata classes are not meant to be instantiated.

private CustomerMetadata()

{

}

[Display(AutoGenerateField=false)]

public int ID { get; set; }

[Display(Order=1, Name="Customer Name")]

public string Name { get; set; }

[Include]

[Display(Order = 0, Name = "Customer Town")]

public Town Town { get; set; }

[Display(Order = 2, Name = "Customer Town")]

public string TownName { get; set; }

[Display(AutoGenerateField = false)]

public int TownID { get; set; }

}

}

Этот код не скомпилируется, поскольку тип Customer не имеет свойства TownName. Создадим новый файл с расширением класса Customer.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace BusinessApplication.BLL

{

public partial class Customer

{

public string TownName

{

get

{

return Town.Name;

}

set

{

return;

}

}

}

}

Причем, нужно обязательно назвать этот файл Customer.shared.cs. Расширение shared указывает на то, что класс должен быть также автоматически скопирован в Silverlight приложение.

Кстати, о генерируемых файлах: если сейчас зайти в папку Generated_Code в Silverlight проекте, то мы найдем файл BusinessApplication1.Web.g.cs. Этот файл создается при каждой компиляции и содержит информацию, полученную из метаданных и веб-сервисов. Приведу лишь небольшой кусок этого файла.

namespace BusinessApplication.BLL

{

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.ComponentModel.DataAnnotations;

using System.Linq;

using System.Runtime.Serialization;

using System.ServiceModel.DomainServices;

using System.ServiceModel.DomainServices.Client;

using System.ServiceModel.DomainServices.Client.ApplicationServices;

using System.Xml.Serialization;

/// <summary>

/// The 'Customer' entity class.

/// </summary>

[DataContract(Namespace="http://schemas.datacontract.org/2004/07/BusinessApplication.BLL")]

public sealed partial class Customer : Entity

{

private int _id;

private string _name;

private EntityRef<Town> _town;

private int _townID;

#region Extensibility Method Definitions

/// <summary>

/// This method is invoked from the constructor once initialization is complete and

/// can be used for further object setup.

/// </summary>

partial void OnCreated();

partial void OnIDChanging(int value);

partial void OnIDChanged();

partial void OnNameChanging(string value);

partial void OnNameChanged();

partial void OnTownIDChanging(int value);

partial void OnTownIDChanged();

#endregion

/// <summary>

/// Initializes a new instance of the <see cref="Customer"/> class.

/// </summary>

public Customer()

{

this.OnCreated();

}

/// <summary>

/// Gets or sets the 'ID' value.

/// </summary>

[DataMember()]

[Display(AutoGenerateField=false)]

[Editable(false, AllowInitialValue=true)]

[Key()]

[RoundtripOriginal()]

public int ID

{

get

{

return this._id;

}

set

{

if ((this._id != value))

{

this.OnIDChanging(value);

this.ValidateProperty("ID", value);

this._id = value;

this.RaisePropertyChanged("ID");

this.OnIDChanged();

}

}

}

/// <summary>

/// Gets or sets the 'Name' value.

/// </summary>

[DataMember()]

[Display(Name="Customer Name", Order=1)]

[Required()]

[StringLength(50)]

public string Name

{

get

{

return this._name;

}

set

{

if ((this._name != value))

{

this.OnNameChanging(value);

this.RaiseDataMemberChanging("Name");

this.ValidateProperty("Name", value);

this._name = value;

this.RaiseDataMemberChanged("Name");

this.OnNameChanged();

}

}

}

/// <summary>

/// Gets or sets the associated <see cref="Town"/> entity.

/// </summary>

[Association("Town_Customer", "TownID", "ID", IsForeignKey=true)]

[Display(Name="Customer Town", Order=0)]

[XmlIgnore()]

public Town Town

{

get

{

if ((this._town == null))

{

this._town = new EntityRef<Town>(this, "Town", this.FilterTown);

}

return this._town.Entity;

}

set

{

Town previous = this.Town;

if ((previous != value))

{

this.ValidateProperty("Town", value);

if ((previous != null))

{

this._town.Entity = null;

previous.Customers.Remove(this);

}

if ((value != null))

{

this.TownID = value.ID;

}

else

{

this.TownID = default(int);

}

this._town.Entity = value;

if ((value != null))

{

value.Customers.Add(this);

}

this.RaisePropertyChanged("Town");

}

}

}

/// <summary>

/// Gets or sets the 'TownID' value.

/// </summary>

[DataMember()]

[Display(AutoGenerateField=false)]

[RoundtripOriginal()]

public int TownID

{

get

{

return this._townID;

}

set

{

if ((this._townID != value))

{

this.OnTownIDChanging(value);

this.RaiseDataMemberChanging("TownID");

this.ValidateProperty("TownID", value);

this._townID = value;

this.RaiseDataMemberChanged("TownID");

this.OnTownIDChanged();

}

}

}

private bool FilterTown(Town entity)

{

return (entity.ID == this.TownID);

}

/// <summary>

/// Computes a value from the key fields that uniquely identifies this entity instance.

/// </summary>

/// <returns>An object instance that uniquely identifies this entity instance.</returns>

public override object GetIdentity()

{

return this._id;

}

}

/// <summary>

/// The 'Town' entity class.

/// </summary>

[DataContract(Namespace="http://schemas.datacontract.org/2004/07/BusinessApplication.BLL")]

public sealed partial class Town : Entity

{

private EntityCollection<Customer> _customers;

private int _id;

private string _name;

#region Extensibility Method Definitions

/// <summary>

/// This method is invoked from the constructor once initialization is complete and

/// can be used for further object setup.

/// </summary>

partial void OnCreated();

partial void OnIDChanging(int value);

partial void OnIDChanged();

partial void OnNameChanging(string value);

partial void OnNameChanged();

#endregion

/// <summary>

/// Initializes a new instance of the <see cref="Town"/> class.

/// </summary>

public Town()

{

this.OnCreated();

}

/// <summary>

/// Gets the collection of associated <see cref="Customer"/> entities.

/// </summary>

[Association("Town_Customer", "ID", "TownID")]

[XmlIgnore()]

public EntityCollection<Customer> Customers

{

get

{

if ((this._customers == null))

{

this._customers = new EntityCollection<Customer>(this, "Customers", this.FilterCustomers, this.AttachCustomers, this.DetachCustomers);

}

return this._customers;

}

}

/// <summary>

/// Gets or sets the 'ID' value.

/// </summary>

[DataMember()]

[Editable(false, AllowInitialValue=true)]

[Key()]

[RoundtripOriginal()]

public int ID

{

get

{

return this._id;

}

set

{

if ((this._id != value))

{

this.OnIDChanging(value);

this.ValidateProperty("ID", value);

this._id = value;

this.RaisePropertyChanged("ID");

this.OnIDChanged();

}

}

}

/// <summary>

/// Gets or sets the 'Name' value.

/// </summary>

[DataMember()]

[Required()]

[StringLength(50)]

public string Name

{

get

{

return this._name;

}

set

{

if ((this._name != value))

{

this.OnNameChanging(value);

this.RaiseDataMemberChanging("Name");

this.ValidateProperty("Name", value);

this._name = value;

this.RaiseDataMemberChanged("Name");

this.OnNameChanged();

}

}

}

private void AttachCustomers(Customer entity)

{

entity.Town = this;

}

private void DetachCustomers(Customer entity)

{

entity.Town = null;

}

private bool FilterCustomers(Customer entity)

{

return (entity.TownID == this.ID);

}

/// <summary>

/// Computes a value from the key fields that uniquely identifies this entity instance.

/// </summary>

/// <returns>An object instance that uniquely identifies this entity instance.</returns>

public override object GetIdentity()

{

return this._id;

}

}

}

Форма после наших последних операций будет выглядеть следующим образом:

clip_image006

Комментариев нет:

Отправить комментарий