понедельник, 30 августа 2010 г.

Readonly поля в DataForm в Silverlight 4

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

Существует два различных аттрибута для того, чтобы сделать поля Readonly. Первый – это аттрибут Editable. Второй – Readonly. Не знаю, какой использовать более правильно, но на форумах собетуют использовать Readonly, с чем это связано не было времени выяснять.

Пометим все нередактируемые поля аттирбутом Readonly. Так примерно будет выглядеть описание класса метаданных:

// The MetadataTypeAttribute identifies LoanMetadata as the class

// that carries additional metadata for the Loan class.

[MetadataTypeAttribute(typeof(Loan.LoanMetadata))]

public partial class Loan

{

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

// of the Loan 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; }

// First Group ends at 20

internal sealed class LoanMetadata

{

// Metadata classes are not meant to be instantiated.

private LoanMetadata()

{

}

[Display(Name = "Borrower ID", Order = 0)]

[ReadOnly(true)]

public string Borrower_ID { get; set; }

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

[ReadOnly(true)]

public string Short_Name { get; set; }

[Display(Name = "Long Name", Order = 2)]

[ReadOnly(true)]

public string Long_Name { get; set; }

[Display(Name = "Address Line 1", Order = 3)]

[ReadOnly(true)]

public string Address_line_1 { get; set; }

[RoundtripOriginal]

[Display(Name = "Address Line 2", Order = 4)]

[ReadOnly(true)]

public string Address_line_2 { get; set; }

[RoundtripOriginal]

[Display(Name = "Address Line 3", Order = 5)]

[ReadOnly(true)]

public string Address_line_3 { get; set; }

[Display(Order = 6)]

[ReadOnly(true)]

public string City { get; set; }

[Display(Order = 7)]

[ReadOnly(true)]

public string State { get; set; }

[Display(Name = "Zip Code", Order = 8)]

[ReadOnly(true)]

public string Zip_Code { get; set; }

[Display(Name = "Taxpayer ID", Order = 11)]

[ReadOnly(true)]

public string Taxpayer_ID { get; set; }

[Display(Name = "Business Type", Order = 12)]

[ReadOnly(true)]

public string Business_Type { get; set; }

[Display(Name = "Relationship Name", Order = 13)]

[ReadOnly(true)]

public string Relationship_Name { get; set; }

[Display(Name = "Relationship ID", Order = 14)]

[ReadOnly(true)]

public string Relationship_ID { get; set; }

[Display(Name = "Credit Score", Order = 15)]

[ReadOnly(true)]

public Nullable<int> Credit_Score { get; set; }

[Display(Name = "Stock Symbol", Order = 16)]

[ReadOnly(true)]

public string Stock_symbol { get; set; }

[Display(Name = "Block Numbering Area or Census Tract", Order = 17)]

[ReadOnly(true)]

public string Block_Numbering_Area_or_Census_Tract { get; set; }

[Display(Name = "MSA Code", Order = 18)]

[ReadOnly(true)]

public string MSA_Code { get; set; }

[Display(Name = "Dealer Code", Order = 19)]

[ReadOnly(true)]

public string Dealer_Code { get; set; }

[Display(Name = "Note", Order = 20)]

public string Note { get; set; }

}

}

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

При попытке отладить приложение можно заметить, что методе set Readonly полей данных текущее значение равно null, а передаваемое значение (value) – пустая строка.

/// <summary>

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

/// </summary>

[DataMember()]

[Display(Name="Address Line 1", Order=3)]

[Editable(false)]

[ReadOnly(true)]

[StringLength(40)]

public string Address_line_1

{

get

{

return this._address_line_1;

}

set

{

if ((this._address_line_1 != value))

{

this.OnAddress_line_1Changing(value);

this.ValidateProperty("Address_line_1", value);

this._address_line_1 = value;

this.RaisePropertyChanged("Address_line_1");

this.OnAddress_line_1Changed();

}

}

}

Проблема в том, что Silverlight считает, что значение незаполненных строковых полей – это пустая строка, а не null. Соответственно, при попытке сохранения не проходит валидация.

Нам нужно разработать новый конвертер данных такой, чтобы пустые строки автоматически преобразовывались в null. Конвертер может выглядеть так:

namespace MyApplication

{

using System;

using System.Windows.Data;

public class NullStringConverter : IValueConverter

{

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

{

return value == null ? "" : value.ToString();

}

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

{

if (targetType != typeof(string))

return value;

if (value == null)

return null;

return string.IsNullOrEmpty(value.ToString()) ? null : value.ToString();

}

}

}

Теперь нам нужно снова модифицировать обработчик события AutoGenerateField и привязать в нем к строковым полям новый конвертер:

private void customDataForm1_AutoGeneratingField(object sender, DataFormAutoGeneratingFieldEventArgs e)

{

if (e.Field.Content.GetType() == typeof(TextBox))

{

if (e.PropertyType == typeof(Nullable<double>))

{

// should be currency

var format = (customDataForm1.CurrentItem).GetType().GetProperty(e.PropertyName).GetCustomAttributes(typeof(DisplayFormatAttribute), true).Cast<DisplayFormatAttribute>().FirstOrDefault();

if (format != null)

{

if (format.DataFormatString == "{0:C}")

{

(e.Field.Content as TextBox).TextAlignment = TextAlignment.Right;

}

Binding binding = e.Field.Content.GetBindingExpression(TextBox.TextProperty).ParentBinding.CreateCopy();

binding.Converter = new DataFormatConverter(format.DataFormatString);

e.Field.Content.SetBinding(TextBox.TextProperty, binding);

}

}

else

{

Binding binding = e.Field.Content.GetBindingExpression(TextBox.TextProperty).ParentBinding.CreateCopy();

binding.Converter = new NullStringConverter();

e.Field.Content.SetBinding(TextBox.TextProperty, binding);

}

}

}

private void customDataForm1_EditEnded(object sender, DataFormEditEndedEventArgs e)

{

if (e.EditAction == DataFormEditAction.Commit)

{

Context.SubmitChanges();

}

}

Код страницы с формой данных:

<fw:FloatableWindow x:Class="MyApplication.Views.LoanView"

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

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

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

xmlns:fw="clr-namespace:System.Windows.Controls;assembly=FloatableWindow"

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

Width="650" Height="600"

Title="Loan Edit" xmlns:my="clr-namespace:MyApplication.Controls" Loaded="ChildWindow_Loaded" mc:Ignorable="d"

xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"

xmlns:my1="clr-namespace:MyApplication.Web.Services"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:my2="clr-namespace:MyApplication.BLL"

xmlns:converter="clr-namespace:MyApplication"

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

HorizontalAlignment="Left" VerticalAlignment="Top">

<fw:FloatableWindow.Resources>

<converter:NullStringConverter x:Key="NullValueConverter" />

</fw:FloatableWindow.Resources>

<Grid x:Name="LayoutRoot">

<ScrollViewer>

<StackPanel>

<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">

<TextBlock Text="Quarter" Margin="0,0,10,0"/>

<ComboBox Width="120" HorizontalAlignment="Right" Name="cbQuarter" SelectionChanged="cbQuarter_SelectionChanged"/>

</StackPanel>

<sdk:TabControl Name="tabControl">

<sdk:TabItem Header="Main">

<StackPanel>

<StackPanel Orientation="Horizontal" Margin="10,10,10,10">

<TextBlock Text="Status:" Margin="10,0,10,0" VerticalAlignment="Center"/>

<TextBlock Name="txtStatus" Margin="0,0,10,0" VerticalAlignment="Center"/>

</StackPanel>

<Grid Margin="10,10,10,10">

<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my2:Rule, CreateList=true}" Height="0" Name="violationsDomainDataSource" QueryName="GetViolationsForLoan" Width="0" LoadedData="violationsDomainDataSource_LoadedData">

<riaControls:DomainDataSource.QueryParameters>

<riaControls:Parameter ParameterName="loanID" />

</riaControls:DomainDataSource.QueryParameters>

<riaControls:DomainDataSource.DomainContext>

<my1:MyApplicationDomainContext />

</riaControls:DomainDataSource.DomainContext>

</riaControls:DomainDataSource>

<ListBox ItemsSource="{Binding ElementName=violationsDomainDataSource, Path=Data}" DisplayMemberPath="Name" Background="LightPink" IsEnabled="False" SelectedIndex="-1" SelectionMode="Multiple" BorderBrush="{x:Null}">

</ListBox>

</Grid>

<StackPanel Orientation="Horizontal" Margin="10,10,10,10">

<my:BusyIndicator x:Name="biStatus">

<StackPanel Orientation="Vertical">

<Button Content="Attention" Width="140" Height="23" Margin="0,0,0,10" Name="btnAttention" Click="btnAttention_Click"/>

<Button Content="Review" Width="140" Height="23" Margin="0,0,0,10" Name="btnReview" Click="btnReview_Click"/>

<Button Content="Approve" Width="140" Height="23" Margin="0,0,0,10" Name="btnApprove" Click="btnApprove_Click"/>

<Button Content="Review is not required" Width="140" Margin="0,0,0,10" Height="23" Name="btnReviewNotReq" Click="btnReviewNotReq_Click" />

<Button Content="Cancel Review" Width="140" Height="23" Name="btnCancelReview" Click="btnCancelReview_Click" />

</StackPanel>

</my:BusyIndicator>

</StackPanel>

<my:CustomDataForm HorizontalAlignment="Center" x:Name="customDataForm1"

VerticalAlignment="Top" Width="450"

AutoEdit="True" AutoCommit="False"

AutoGenerateFields="True"

AutoGeneratingField="customDataForm1_AutoGeneratingField" Style="{StaticResource DataFormWithGroups}"

EditEnded="customDataForm1_EditEnded"

>

</my:CustomDataForm>

</StackPanel>

</sdk:TabItem>

</sdk:TabControl>

</StackPanel>

</ScrollViewer>

</Grid>

</fw:FloatableWindow>

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

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