Предположим, что нам нужно сделать некоторые в 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>
Комментариев нет:
Отправить комментарий