Это может быть немного долго, но здесь идет. Я создал небольшое примерное приложение в стиле мастера, используя шаблон MVVM (в основном, дурацкая версия кода в моем «реальном» приложении). В этом приложении главное окно перемещается из списка <..> моделей представлений, причем каждая модель представления отображает свое связанное представление. У меня есть 2 класса модели представления, которые по существу идентичны, и они отображают одно и то же представление.
В представлении находится поле со списком, заполненное массивом с плавающей точкой. SelectedItem привязан к свойству float в модели представления. Я создал шаблон для поля со списком, чтобы отобразить каждый элемент в виде TextBlock с текстом, принимающим значение с плавающей запятой и проходящим через преобразователь значений.
Проблема, когда я переключаюсь между моделями представлений, все работает нормально, пока все модели представлений, на которые я переключаюсь, принадлежат к одному и тому же классу. Как только я изменяю текущую страницу на экземпляр другой модели представления, Convert конвертера значений вызывается с параметром 'value', который является строкой нулевой длины. Вплоть до того времени, как я и ожидал, Конверта вызывали только с плавающей точкой.
Мой вопрос: почему конвертер вызывается ТОЛЬКО с пустой строкой в случае переключения классов моделей представления?
Я прикрепляю главное окно XAML и представление модели, а также просмотр / просмотр моделей, отображаемых для каждой «страницы». Вы заметите, что модель представления основного окна имеет список, содержащий 2 экземпляра PageViewModel и 2 экземпляра OtherViewModel. Я могу переключаться между первыми 2 точными, и преобразователь значений вызывается только с плавающим значением. Как только я переключаюсь на первый экземпляр OtherViewModel, преобразователь получает «дополнительный» вызов с пустой строкой в качестве значения.
Фрагменты кода:
MainWindow
<Grid.Resources>
<DataTemplate DataType="{x:Type local:PageViewModel}">
<local:PageView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:OtherViewModel}">
<local:PageView />
</DataTemplate>
</Grid.Resources>
<!-- Page -->
<ContentControl Margin="5,5,5,35"
Height="100"
IsTabStop="False"
Content="{Binding CurrentPage}" />
<!-- Commands -->
<Button Margin="5,115,0,0"
Width="75"
Content="< Back"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Command="{Binding BackCommand}" />
<Button Margin="85,115,0,0"
Width="75"
Content="Next >"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Command="{Binding NextCommand}" />
MainWindowViewModel
public MainWindowViewModel()
{
m_pages = new List<BaseViewModel>();
m_pages.Add(new PageViewModel(1, 7f));
m_pages.Add(new PageViewModel(2, 8.5f));
m_pages.Add(new OtherViewModel(3, 10f));
m_pages.Add(new OtherViewModel(4, 11.5f));
m_currentPage = m_pages.First();
m_nextCommand = new BaseCommand(param => this.OnNext(), param => this.EnableNext());
m_backCommand = new BaseCommand(param => this.OnBack(), param => this.EnableBack());
}
// Title
public string Title
{
get
{
return (CurrentPage != null) ? CurrentPage.Name : Name;
}
}
// Pages
BaseViewModel m_currentPage = null;
List<BaseViewModel> m_pages = null;
public BaseViewModel CurrentPage
{
get
{
return m_currentPage;
}
set
{
if (value == m_currentPage)
return;
m_currentPage = value;
OnPropertyChanged("Title");
OnPropertyChanged("CurrentPage");
}
}
// Back
ICommand m_backCommand = null;
public ICommand BackCommand
{
get
{
return m_backCommand;
}
}
public void OnBack()
{
CurrentPage = m_pages[m_pages.IndexOf(CurrentPage) - 1];
}
public bool EnableBack()
{
return CurrentPage != m_pages.First();
}
// Next
ICommand m_nextCommand = null;
public ICommand NextCommand
{
get
{
return m_nextCommand;
}
}
public void OnNext()
{
CurrentPage = m_pages[m_pages.IndexOf(CurrentPage) + 1];
}
public bool EnableNext()
{
return CurrentPage != m_pages.Last();
}
}
Обратите внимание на 2 экземпляра одной модели представления, за которыми следуют 2 экземпляра другой.
страницуПоказать
<Grid.Resources>
<x:Array x:Key="DepthList"
Type="sys:Single">
<sys:Single>7</sys:Single>
<sys:Single>8.5</sys:Single>
<sys:Single>10</sys:Single>
<sys:Single>11.5</sys:Single>
</x:Array>
<local:MyConverter x:Key="MyConverter" />
</Grid.Resources>
<TextBlock Text="Values:"
Margin="5,5,0,0">
</TextBlock>
<ComboBox Width="100"
Height="23"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="5,25,0,0"
DataContext="{Binding}"
SelectedItem="{Binding Depth}"
ItemsSource="{Binding Source={StaticResource DepthList}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource MyConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
PageViewModel / OtherViewModel / MyConverter
public class PageViewModel : BaseViewModel
{
public PageViewModel(int index, float depth)
{
Depth = depth;
Name = "Page #" + index.ToString();
}
public float Depth
{
get;
set;
}
}
public class OtherViewModel : BaseViewModel
{
public OtherViewModel(int index, float depth)
{
Depth = depth;
Name = "Other #" + index.ToString();
}
public float Depth
{
get;
set;
}
}
[ValueConversion(typeof(DateTime), typeof(String))]
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("IValueConverter.Convert : received a " + value.GetType().Name);
string text = "";
if (value is float)
{
text = value.ToString();
}
else
{
throw new ArgumentException("MyConverter : input value is NOT a float.");
}
return text;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return float.Parse(value as string);
}
}
Примечание: я могу удалить исключение в методе Convert, и все, кажется, работает нормально. Но я хотел бы знать, почему это происходит. Почему конвертер получает пустую строку вместо ожидаемого с плавающей точкой, и только когда мы переключаем модели представления?
Любая идея будет принята с благодарностью. Заранее спасибо...
Джо