Еще немного поработаем с формами и метаданными.
Как мы помним, сейчас у нас не отображается название города, с которыс ассоциирован данный кастомер.
Это серьезная проблема, нужно ее исправить.
Для начала нужно изменить запрос в веб-сервисе так, чтобы он включал в сущности кастомеров информацию о городах.
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; }
}
}
Сейчас увидим следующую информацию в форме:
По крайней мере, это не пустое поле, но это все равно не совсем то, что нужно.
Оптимально было бы добавить вместо текстового поля комбобокс с возможностью выбрать город. Стандартных средств для этого в 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;
}
}
}
Форма после наших последних операций будет выглядеть следующим образом:
Комментариев нет:
Отправить комментарий