Вот мой сценарий:
У меня есть DataGrid
с двумя столбцами, представляющими широту и долготу.ItemsSource
связан с коллекцией класса модели CustomVertex
, которая содержит два свойства: X
и Y
, которые double
с.
У меня есть три формата, в которых эти значения могут быть: Десятичные градусы, градусы, минуты и градусы, минуты, секунды.У меня есть три RadioButtons
, соответствующих этим, и в зависимости от того, какой из них выбран, он изменит значение ячеек DataGrid
на правильный формат через конвертер.
Я использую Xceed's MaskedTextBox
в ячейках DataGrid
, и я связал свойство Mask
со строковым свойством в моем ViewModel
, чтобы изменить Mask
при каждом изменении формата.Это меняет маску, за исключением того, что она очищает любые значения от самого MaskedTextBox
вместо их преобразования.Любой пользовательский ввод в MaskedTextBox
тоже ничего не вызывает.
Во-первых, вот модель CustomVertex
:
public class CustomVertex : ValidatableModel
{
/// <summary>
/// Represents the X-Coordinate (Longitude)
/// </summary>
private double _x;
public double X
{
get { return _x; }
set
{
if (_x != value)
{
_x = value;
RaisePropertyChanged("X");
}
}
}
/// <summary>
/// Represents the Y-Coordinate (Latitude)
/// </summary>
private double _y;
public double Y
{
get { return _y; }
set
{
if (_y != value)
{
_y = value;
RaisePropertyChanged("Y");
}
}
}
public CustomVertex(double latitude, double longitude)
{
Y = latitude;
X = longitude;
}
public CustomVertex()
{
Y = 0;
X = 0;
}
}
Вот DataGrid
в .xaml моего окна (здесь отображается только столбец широты, но столбец долготы точно такой же).Я включил RadioButton
s, а также просто incase:
<RadioButton Name="degRB" Checked="VertexFormatRB_Checked" IsChecked="{Binding Path=SelectedVertexType, Converter={StaticResource EBConverter}, ConverterParameter={x:Static enums:VertexType.DDD}}" GroupName="UnitType" Content="Deg" />
<RadioButton Name="degMinRB" Checked="VertexFormatRB_Checked" IsChecked="{Binding Path=SelectedVertexType, Converter={StaticResource EBConverter}, ConverterParameter={x:Static enums:VertexType.DDDMM}}" GroupName="UnitType" Content="Deg Min"/>
<RadioButton Name="degMinSecRB" Checked="VertexFormatRB_Checked" IsChecked="{Binding Path=SelectedVertexType, Converter={StaticResource EBConverter}, ConverterParameter={x:Static enums:VertexType.DDDMMSS}}" GroupName="UnitType" Content="Deg Min Sec"/>
<DataGrid ItemsSource="{Binding Vertices}" AutoGenerateColumns="False" IsReadOnly="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Latitude" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<xctk:MaskedTextBox Mask="{Binding DataContext.Mask, RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
<xctk:MaskedTextBox.Text>
<MultiBinding Converter="{StaticResource VertexConverter}" UpdateSourceTrigger="Default">
<Binding Path="Y" />
<Binding Path="DataContext.SelectedVertexType" RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}" />
</MultiBinding>
</xctk:MaskedTextBox.Text>
</xctk:MaskedTextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Вот VertexConverter
, который используется для переключения значения Y
в правильный формат.Я оставил некоторые вещи из ConvertBack
, но вы должны получить суть:
public sealed class VertexConverter : IMultiValueConverter
{
private VertexType vertexType = VertexType.DDD;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] == DependencyProperty.UnsetValue)
{
return null;
}
if ((VertexType)values[1] == VertexType.DDD)
{
vertexType = VertexType.DDD;
return Math.Round((double)values[0], 9).ToString();
}
else if ((VertexType)values[1] == VertexType.DDDMM)
{
vertexType = VertexType.DDDMM;
double dd = Math.Truncate((double)values[0]);
double mm = Math.Round(((double)values[0] - dd) * 60, 7);
return dd + "°" + " " + mm + '"';
}
else if ((VertexType)values[1] == VertexType.DDDMMSS)
{
vertexType = VertexType.DDDMMSS;
return DecimalDegreesHelper.GetDegreesMinutesSecondsFromDecimalDegree((double)values[0]).ToString();
}
else
{
return "";
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
object[] ret = new object[2];
if (vertexType == VertexType.DDD)
{
ret[0] = double.Parse(value.ToString());
ret[1] = vertexType;
}
else if (vertexType == VertexType.DDDMM)
{
//more stuff
}
else
{
//more stuff
}
return ret;
}
}
Наконец, необходимые части ViewModel
:
const string DDD_MASK = "####.#########"; //ex: -170.112233440
const string DDDMM_MASK = ""; //todo
const string DDDMMSS_MASK = ""; //todo
public ObservableCollection<CustomVertex> Vertices { get; set; } //DataGrid is bound to this. Set to new collection in vm's constructor
private VertexType _selectedVertexType;
public VertexType SelectedVertexType
{
get => _selectedVertexType;
set
{
_selectedVertexType = value;
if (value == VertexType.DDD)
{
Mask = DDD_MASK;
}
else if (value == VertexType.DDDMM)
{
Mask = DDDMM_MASK;
}
else if (value == VertexType.DDDMMSS)
{
Mask = DDDMMSS_MASK;
}
OnPropertyChanged();
}
}
public string Mask { get; set; }
И сам перечислитель:
public enum VertexType
{
DDD,
DDDMM,
DDDMMSS
}
Я пытался извлечь значение Mask
из ViewModel
и использовать другое Converter
для свойства Mask
, но это вывело ту же ошибку.
Сторона-примечание - если это не лучший способ заставить правильный ввод пользователя, я открыт для других идей.