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

Использование DataForm в Silverlight 4. Часть 4

Теперь мы такой важный элемент как DataForm. DataForm в Silverlight отвечает за редактирование, добавление, удаление и навигацию по данным.

Интересно то, что реализовать все эти операции без использования DataForm достаточно сложно, постоянно будут появляться ошибки (на самом деле и DataForm они будут появляться, но об этом потом).

Кстати, в прошлый раз мы не добавили строку подключения в Web.config.

Сделаем это сейчас, Web.config будет выглядеть примерно так:

<?xml version="1.0"?>

<configuration>

<configSections>

<sectionGroup name="system.serviceModel">

<section name="domainServices" type="System.ServiceModel.DomainServices.Hosting.DomainServicesSection, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" allowDefinition="MachineToApplication" requirePermission="false" />

</sectionGroup>

</configSections>

<system.web>

<httpModules>

<add name="DomainServiceModule" type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

</httpModules>

<compilation debug="true" targetFramework="4.0" />

<roleManager enabled="true"/>

<authentication mode="Forms">

<forms name=".BusinessApplication1_ASPXAUTH" />

</authentication>

<profile>

<properties>

<add name="FriendlyName"/>

</properties>

</profile>

</system.web>

<connectionStrings>

<add name="MySamplerDBEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.\SQLEXPRESS;Initial Catalog=MySamplerDB;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />

</connectionStrings>

<system.webServer>

<validation validateIntegratedModeConfiguration="false"/>

<modules runAllManagedModulesForAllRequests="true">

<add name="DomainServiceModule" preCondition="managedHandler"

type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

</modules>

</system.webServer>

<system.serviceModel>

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

</system.serviceModel>

</configuration>

При запуске получится примерно такая форма:

clip_image002

Пока у нас отображается ненужная колонка ID и хотелось бы видеть вместо идентификатора города видеть его название, но об этом дальше.

Сейчас добавим на нашу форму элемент DataForm:

<UserControl

x:Class="BusinessApplication1.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"

xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:my="clr-namespace:BusinessApplication1.Web.Services" xmlns:my1="clr-namespace:BusinessApplication.BLL">

<Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}">

<Border x:Name="ContentBorder" Style="{StaticResource ContentBorderStyle}">

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}"

Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">

<navigation:Frame.UriMapper>

<uriMapper:UriMapper>

<uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>

<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>

</uriMapper:UriMapper>

</navigation:Frame.UriMapper>

</navigation:Frame>

</Border>

<Grid Style="{StaticResource NavigationOuterGridStyle}">

<Grid x:Name="NavigationGrid" Style="{StaticResource NavigationGridStyle}">

<Border x:Name="BrandingBorder" Style="{StaticResource BrandingBorderStyle}">

<StackPanel x:Name="BrandingStackPanel" Style="{StaticResource BrandingStackPanelStyle}">

<ContentControl Style="{StaticResource LogoIcon}"/>

<TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}"

Text="{Binding ApplicationStrings.ApplicationName, Source={StaticResource ResourceWrapper}}"/>

</StackPanel>

</Border>

<Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}">

<StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}">

<HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}"

NavigateUri="/Home" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.HomePageTitle, Source={StaticResource ResourceWrapper}}"/>

<Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>

<HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}"

NavigateUri="/About" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.AboutPageTitle, Source={StaticResource ResourceWrapper}}"/>

</StackPanel>

</Border>

</Grid>

<Border x:Name="loginContainer" Style="{StaticResource LoginContainerStyle}">

<!-- LoginStatus will be added here in code behind. This is required for the designer view to work -->

</Border>

<StackPanel>

<sdk:DataGrid AutoGenerateColumns="False" HorizontalAlignment="Left" ItemsSource="{Binding ElementName=customerDomainDataSource, Path=Data}" Margin="0,123,0,0" Name="customerDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="400">

<sdk:DataGrid.Columns>

<sdk:DataGridTextColumn x:Name="iDColumn" Binding="{Binding Path=ID, Mode=OneWay}" Header="ID" IsReadOnly="True" Width="SizeToHeader" />

<sdk:DataGridTextColumn x:Name="nameColumn" Binding="{Binding Path=Name}" Header="Name" Width="SizeToHeader" />

<sdk:DataGridTextColumn x:Name="townIDColumn" Binding="{Binding Path=TownID}" Header="Town ID" Width="SizeToHeader" />

</sdk:DataGrid.Columns>

</sdk:DataGrid>

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

AutoGenerateFields="True"

AutoCommit="False"

AutoEdit="False"

Width="400">

</dataControls:DataForm>

</StackPanel>

</Grid>

<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my1:Customer, CreateList=true}" Height="0" LoadedData="customerDomainDataSource_LoadedData" Name="customerDomainDataSource" QueryName="GetCustomersQuery" Width="0">

<riaControls:DomainDataSource.DomainContext>

<my:DomainService1 />

</riaControls:DomainDataSource.DomainContext>

</riaControls:DomainDataSource>

</Grid>

</UserControl>

Наиболее важный параметр – это CurrentItem, мы связываем его с текущим элементом в гриде.

clip_image004

Можно заметить, что сейчас можно изменить что-либо в форме, но данные не сохранятся в базе данных!

Исправить это легко, например, следующим образом (это, наверно, неправильный вариант, но он работает). Добавим обработчик события EditEnded и в нем отправим изменения на веб-сервис.

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

AutoGenerateFields="True"

AutoCommit="False"

AutoEdit="False"

Width="400"

EditEnded="DataForm_EditEnded">

</dataControls:DataForm>

private void DataForm_EditEnded(object sender, DataFormEditEndedEventArgs e)

{

if (e.EditAction == DataFormEditAction.Commit)

{

customerDomainDataSource.SubmitChanges();

}

}

Теперь нужно убрать ненужные поля из формы. Обратите внимание на то, что явно поля для формы не задаются и Visual Studio не умеет их генерировать. Можно либо полностью создать все форму, то есть указать каждое поле (что проблематично, когда полей порядка 100) либо указать, что нужно автоматически генерировать все поля.

Перейдем к файлу с метаданными и отредактируем его следующим образом:

// 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; }

public string Name { get; set; }

public Town Town { get; set; }

[Display(AutoGenerateField = false)]

public int TownID { get; set; }

}

}

Таким образом мы указали, что поля ID и TownID не должны включаться в форму с данными. Форма будет выглядеть следующим образом:

clip_image006

Можно также изменить некоторые параметры отображения данных в форме. Тут опять есть проблема с тем, что некоторые параметры почему-то не работают… об этом тоже дальше.

Например, часто важно определить порядок отображения полей в форме, а в Silverlight приложении все поля просто будут отсортированы по алфавиту (не очень жизненный сценарий).

Изменим порядок отображения колонок и их заголовки:

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

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

public Town Town { get; set; }

[Display(AutoGenerateField = false)]

public int TownID { get; set; }

}

}

Теперь форма будем иметь следующий вид:

clip_image008

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

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