В моем приложении WPF есть диалог, в котором пользователь может ввести широту и долготу. Я написал IValueConverter
который может обрабатывать значения, введенные как десятичные градусы, так и в градусах-минутах-второй формат:
[ValueConversion( typeof( double? ), typeof( string ) )]
public class CoordinateConverter : IValueConverter {
#region IValueConverter Members
public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) {
double angle = 0.0;
if ( value is string ) {
if ( !double.TryParse( value as string, NumberStyles.Float, CultureInfo.InvariantCulture, out angle ) ) {
return value;
}
} else if ( value is double || value is double? ) {
angle = (double) value;
} else {
return value.ToString();
}
bool isNegative = angle < 0;
if ( isNegative ) angle = -angle;
double degrees = Math.Truncate( angle );
double remainder = ( angle - degrees ) * 60.0;
double minutes = Math.Truncate( remainder );
double seconds = ( remainder - minutes ) * 60.0;
string result = degrees.ToString( "##0", culture.NumberFormat ) + "° " +
minutes.ToString( "#0", culture.NumberFormat ) + "' " +
seconds.ToString( "#0.00", culture.NumberFormat ) + "\" ";
// The parameter contains "NS" for Latitudes and "EW" for Longitudes.
if ( parameter != null ) {
result += ( (string) parameter ).Substring( ( isNegative ? 1 : 0 ), 1 );
} else {
result = ( isNegative ? "-" : string.Empty ) + result;
}
return result;
}
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) {
string strValue = value as string;
if ( string.IsNullOrEmpty( strValue ) ) {
return null;
}
double adjustForSign = 1.0;
if ( strValue.IndexOf( "-" ) >= 0 ) {
adjustForSign = -1.0;
strValue = strValue.Substring( strValue.IndexOf( "-" ) + 1 );
}
// Parse the value in the field. It in three parts: Degrees, minutes & seconds
int degreeSymbol = strValue.IndexOf( "°" );
int minuteSymbol = strValue.IndexOf( "'" );
int secondSymbol = strValue.IndexOf( '"' );
string degrees = null, minutes = null, seconds = null;
double angle, d, m, s;
if ( degreeSymbol < 0 ) {
if ( double.TryParse( strValue, NumberStyles.Number, culture.NumberFormat, out angle ) ) {
return angle;
} else {
return value;
}
} else {
degrees = strValue.Substring( 0, degreeSymbol );
if ( minuteSymbol >= 0 ) {
minutes = strValue.Substring( degreeSymbol + 2, minuteSymbol - degreeSymbol - 2 );
}
if ( secondSymbol < 0 ) {
seconds = "0" + culture.NumberFormat.NumberDecimalSeparator + "0";
} else {
seconds = strValue.Substring( minuteSymbol + 2, secondSymbol - minuteSymbol - 2 );
}
}
if ( !double.TryParse( degrees, NumberStyles.Integer, culture.NumberFormat, out d ) ) return value;
if ( !double.TryParse( minutes, NumberStyles.Integer, culture.NumberFormat, out m ) ) return value;
if ( !double.TryParse( seconds, NumberStyles.Float , culture.NumberFormat, out s ) ) return value;
angle = d + m / 60.0 + s / 3600.0;
if ( parameter != null ) {
if ( strValue.Contains( ( (string) parameter ).Substring( 1, 1 ) ) ) {
angle = -angle;
}
} else {
angle *= adjustForSign;
}
return angle;
}
В диалоговом окне я использую этот ControlTemplate
для отображения ошибок:
<ControlTemplate x:Key="InputErrorTemplate">
<DockPanel LastChildFill="True">
<Image DockPanel.Dock="Right"
Height="20"
Margin="-30,0,0,0"
Source="{StaticResource ErrorImage}"
ToolTip="{x:Static res:Car.Common_InvalidData}"
VerticalAlignment="Center"
Width="20" />
<Border BorderBrush="Red"
BorderThickness="5"
Margin="5,0,30,0">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
Если у одного из TextBoxes
есть строка, например, она не анализируется, никакие исключения не выбрасываются, и отображается мой шаблон ошибки диалога. Когда я наводил указатель мыши на TextBox
, отображается сообщение об ошибке: "Строка ввода не в правильном формате"
У меня есть пара вопросов:
TextBox
не может быть проанализировано. В диалоговом представлении View Model реализуется IDataErrorInfo
, но преобразование из строки в double не выполняется этим объектом. Как это сделать?Поскольку текстовое поле, вероятно, связано с двойным свойством в коде, я думаю, что есть некоторая умная проверка предварительного преобразования, которая дает ошибку проверки, которую вы видите. Чтобы контролировать процесс проверки, я думаю, есть несколько способов сделать это:
Вот несколько ссылок, которые содержат некоторую полезную информацию:
Еще одна вещь, при преобразовании ввода в double, есть ли какая-то конкретная причина использовать инвариантную культуру вместо культуры, переданной как параметр? Это может закончиться не-английской культурой (например, датской), где десятичный разделитель является запятой (что эквивалентно тысячам разделителей в английской культуре).
Поиграв с этим некоторое время, я обнаружил, что привязка, похоже, пытается ConvertBack
любое значение, ConvertBack
методами Convert
или ConvertBack
в правильный тип, основанный на данных направления. Если значение не может быть введено в этот тип, в сборку ошибок для этого элемента управления добавляется сообщение "Строка ввода не в правильном формате". Поэтому, если мой код просто возвращает исходное значение без изменений, если есть проблема, я получаю это сообщение.
Это сообщение достаточно хорошее, по крайней мере на данный момент. Если я хочу другое сообщение, мне придется исследовать создание правила проверки или какого-либо другого механизма.
IValueConverter
, некоторым из которых передаются данные, проанализированные из файла XML, который кодирует все, используя инвариантную культуру. Недавно я добавилvalue is string
регистр для обработки этих данных для всех преобразователей. Однако этому конкретному классу может не понадобиться эта логика. Я должен подумать об этом.IDataError
. Я не упомянул об этом в вопросе, потому что это казалось неуместным - на мой взгляд, вопрос был оIValueConverter
а не о модели представления. Отображение широты и долготы в градусах, минутах и секундах не имеет ничего общего с проверкой допустимости введенных значений. И да, рассматриваемые свойства являются двойными. Чтобы выполнить эту проверку в модели представления, я должен был бы привязать текстовые поля к строковым свойствам и выполнить преобразование, чтобы удвоить их там. Не плохая идея, я мог бы добавить.