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

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

Еще раз немного поговорим о возможностях DataForm. Сейчас у нас нет способа добавлять или удалять записи.

Исправим эту недоработку.

Сначала рассмотрим еще раз код нашего сгенерированного сервиса.

namespace BusinessApplication1.Web.Services

{

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.ComponentModel.DataAnnotations;

using System.Data;

using System.Linq;

using System.ServiceModel.DomainServices.EntityFramework;

using System.ServiceModel.DomainServices.Hosting;

using System.ServiceModel.DomainServices.Server;

using BusinessApplication.BLL;

// Implements application logic using the MySamplerDBEntities context.

// TODO: Add your application logic to these methods or in additional methods.

// TODO: Wire up authentication (Windows/ASP.NET Forms) and uncomment the following to disable anonymous access

// Also consider adding roles to restrict access as appropriate.

// [RequiresAuthentication]

[EnableClientAccess()]

public class DomainService1 : LinqToEntitiesDomainService<MySamplerDBEntities>

{

// TODO:

// Consider constraining the results of your query method. If you need additional input you can

// add parameters to this method or create additional query methods with different names.

// To support paging you will need to add ordering to the 'Customers' query.

public IQueryable<Customer> GetCustomers()

{

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

}

public void InsertCustomer(Customer customer)

{

if ((customer.EntityState != EntityState.Detached))

{

this.ObjectContext.ObjectStateManager.ChangeObjectState(customer, EntityState.Added);

}

else

{

this.ObjectContext.Customers.AddObject(customer);

}

}

public void UpdateCustomer(Customer currentCustomer)

{

this.ObjectContext.Customers.AttachAsModified(currentCustomer, this.ChangeSet.GetOriginal(currentCustomer));

}

public void DeleteCustomer(Customer customer)

{

if ((customer.EntityState == EntityState.Detached))

{

this.ObjectContext.Customers.Attach(customer);

}

this.ObjectContext.Customers.DeleteObject(customer);

}

// TODO:

// Consider constraining the results of your query method. If you need additional input you can

// add parameters to this method or create additional query methods with different names.

// To support paging you will need to add ordering to the 'Towns' query.

public IQueryable<Town> GetTowns()

{

return this.ObjectContext.Towns;

}

public void InsertTown(Town town)

{

if ((town.EntityState != EntityState.Detached))

{

this.ObjectContext.ObjectStateManager.ChangeObjectState(town, EntityState.Added);

}

else

{

this.ObjectContext.Towns.AddObject(town);

}

}

public void UpdateTown(Town currentTown)

{

this.ObjectContext.Towns.AttachAsModified(currentTown, this.ChangeSet.GetOriginal(currentTown));

}

public void DeleteTown(Town town)

{

if ((town.EntityState == EntityState.Detached))

{

this.ObjectContext.Towns.Attach(town);

}

this.ObjectContext.Towns.DeleteObject(town);

}

}

}

Можно заметить, что для каждой сущности у нас имеется ряд методов: Get, Insert, Update, Delete. Руками вызвать их, похоже, нельзя (по крайней мере, врядли это нужно делать), зато DataForm умеет авточатически работать с ними.

Изменим немного xaml код формы.

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

AutoGenerateFields="True"

AutoCommit="False"

AutoEdit="False"

Width="400"

EditEnded="DataForm_EditEnded"

AutoGeneratingField="DataForm_AutoGeneratingField"

Name="customerDataForm"

CommandButtonsVisibility="All">

</dataControls:DataForm>

clip_image002

Появилось множество новых кнопок, но все они пока не работают.

Нужно еще связать Itemssource формы с данными с источником данных.

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

AutoGenerateFields="True"

AutoCommit="False"

AutoEdit="False"

Width="400"

EditEnded="DataForm_EditEnded"

AutoGeneratingField="DataForm_AutoGeneratingField"

Name="customerDataForm"

CommandButtonsVisibility="All"

ItemsSource="{Binding Path=Data, ElementName=customerDomainDataSource}">

</dataControls:DataForm>

И вот тут возникает забавная вещь: кнопки работают, при добавлении и удалении данных, информация в гриде меняется, а вот из базы данных строки не удаляются (зато добавление работает). Крайне забавное поведение, но опять-таки исправить это можно.

Для этого добавим следующий обработчик события DeletingItem к форме.

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

AutoGenerateFields="True"

AutoCommit="False"

AutoEdit="False"

Width="400"

EditEnded="DataForm_EditEnded"

AutoGeneratingField="DataForm_AutoGeneratingField"

Name="customerDataForm"

CommandButtonsVisibility="All"

ItemsSource="{Binding Path=Data, ElementName=customerDomainDataSource}"

DeletingItem="customerDataForm_DeletingItem">

</dataControls:DataForm>

private void customerDataForm_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)

{

var ctx = customerDomainDataSource.DomainContext as DomainService1;

DataForm form = sender as DataForm;

form.CommitEdit();

Customer ev = form.CurrentItem as Customer;

ctx.Customers.Remove(ev);

ctx.SubmitChanges((op) =>

{

if (op.HasError)

{

MessageBox.Show(op.Error.Message);

}

}, null);

}

Теперь все работает и данные удаляются.

clip_image004

Весь код MainWindow.xaml.cs:

namespace BusinessApplication1

{

using System.Windows;

using System.Windows.Controls;

using System.Windows.Navigation;

using BusinessApplication1.LoginUI;

using System.Windows.Data;

using BusinessApplication1.Web.Services;

using System.ServiceModel.DomainServices.Client;

using System;

using BusinessApplication.BLL;

/// <summary>

/// <see cref="UserControl"/> class providing the main UI for the application.

/// </summary>

public partial class MainPage : UserControl

{

/// <summary>

/// Creates a new <see cref="MainPage"/> instance.

/// </summary>

public MainPage()

{

InitializeComponent();

this.loginContainer.Child = new LoginStatus();

}

/// <summary>

/// After the Frame navigates, ensure the <see cref="HyperlinkButton"/> representing the current page is selected

/// </summary>

private void ContentFrame_Navigated(object sender, NavigationEventArgs e)

{

foreach (UIElement child in LinksStackPanel.Children)

{

HyperlinkButton hb = child as HyperlinkButton;

if (hb != null && hb.NavigateUri != null)

{

if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))

{

VisualStateManager.GoToState(hb, "ActiveLink", true);

}

else

{

VisualStateManager.GoToState(hb, "InactiveLink", true);

}

}

}

}

/// <summary>

/// If an error occurs during navigation, show an error window

/// </summary>

private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)

{

e.Handled = true;

ErrorWindow.CreateNew(e.Exception);

}

private void customerDomainDataSource_LoadedData(object sender, System.Windows.Controls.LoadedDataEventArgs e)

{

if (e.HasError)

{

System.Windows.MessageBox.Show(e.Error.ToString(), "Load Error", System.Windows.MessageBoxButton.OK);

e.MarkErrorAsHandled();

}

}

private void DataForm_EditEnded(object sender, DataFormEditEndedEventArgs e)

{

if (e.EditAction == DataFormEditAction.Commit)

{

customerDomainDataSource.SubmitChanges();

}

}

private void DataForm_AutoGeneratingField(object sender, DataFormAutoGeneratingFieldEventArgs e)

{

if (e.PropertyName == "Town")

{

var ctx = (customerDomainDataSource.DomainContext as DomainService1);

var townsQ = ctx.Load(ctx.GetTownsQuery(), OnGetTowns, e.Field);

}

}

public void OnGetTowns(LoadOperation loadOperation)

{

Binding binding = new Binding("Town");

binding.Mode = BindingMode.TwoWay;

var townCombobox = new ComboBox();

townCombobox.ItemsSource = loadOperation.Entities;

townCombobox.DisplayMemberPath = "Name";

townCombobox.SetBinding(ComboBox.SelectedItemProperty, binding);

var item = loadOperation.UserState as DataField;

item.Content = townCombobox;

Binding editableBinding = new Binding() { ElementName = "customerDataForm", Path = new PropertyPath("Mode"), Converter = new InverseModeConvertor() };

item.SetBinding(DataField.IsReadOnlyProperty, editableBinding);

}

private void customerDataForm_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)

{

var ctx = customerDomainDataSource.DomainContext as DomainService1;

DataForm form = sender as DataForm;

form.CommitEdit();

Customer ev = form.CurrentItem as Customer;

ctx.Customers.Remove(ev);

ctx.SubmitChanges((op) =>

{

if (op.HasError)

{

MessageBox.Show(op.Error.Message);

}

}, null);

}

}

class InverseModeConvertor : IValueConverter

{

#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{

var mode = ((DataFormMode)value);

return !(mode == DataFormMode.Edit || mode == DataFormMode.AddNew);

}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{

return value;

}

#endregion

}

}

MainWindow.xaml

<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" 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"

EditEnded="DataForm_EditEnded"

AutoGeneratingField="DataForm_AutoGeneratingField"

Name="customerDataForm"

CommandButtonsVisibility="All"

ItemsSource="{Binding Path=Data, ElementName=customerDomainDataSource}"

DeletingItem="customerDataForm_DeletingItem">

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

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

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