这可能会有点长,但这里有。我使用MVVM模式创建了一个小型的向导式示例应用程序(基本上是我的“真实”应用程序中的代码的简化版本)。在这个应用程序中,主窗口从List< ..>移动视图模型,每个视图模型显示其关联的视图。我有两个基本相同的视图模型类,它们显示相同的视图。
在视图上是一个组合框,填充了一个float数组。 SelectedItem绑定到视图模型上的float属性。我已经为组合框创建了一个模板,将每个项目显示为TextBlock,文本采用浮点值并通过值转换器。
问题是,当我在视图模型之间来回切换时,只要我切换到的每个视图模型属于同一类,所有工作都可以正常工作。只要我将当前页面更改为不同视图模型的实例,就会使用'value'参数调用值转换器的Convert,该参数是零长度字符串。直到那时,转换只是用浮点调用,正如我所料。
我的问题:为什么在切换视图模型类的情况下,仅使用空字符串调用转换器?
我正在附加主窗口XAML和视图模型,以及为每个“页面”显示的视图/视图模型。您会注意到主窗口视图模型有一个列表,其中包含2个PageViewModel实例和2个OtherViewModel实例。我可以在前2个精细之间来回切换,而值转换器只能用浮点值调用。切换到第一个OtherViewModel实例后,转换器将获得一个“额外”调用,并将空字符串作为值。
代码段:
主窗口
<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方法中删除异常,一切似乎都能正常工作。但是,我想知道为什么会这样。为什么转换器变为空字符串而不是预期的浮点数,并且只有当我们切换视图模型时才会这样?
任何见解都将不胜感激。提前致谢... 乔
答案 0 :(得分:3)
我遇到了同样的问题(使用枚举而不是浮点数)。
关闭视图时,清空ComboBox选择。您可以通过处理SelectionChanged事件并检查SelectionChangedEventArgs RemovedItems集合来检查这一点。 这将以String.Empty传递给您的ValueConverter。
在我的例子中,我修改了ValueConverter.Convert以允许string.Empty作为有效值,并返回string.Empty。 这是我使用的代码:
// When view is unloaded, ComboBox Selection is emptied and Convert is passed string.Empty
// Hence we need to handle this conversion
if (value is string && string.IsNullOrEmpty((string)value))
{
return string.Empty;
}
答案 1 :(得分:1)
尝试
public BaseViewModel
{
public virtual float Depth{get;set;}
...
}
然后
public class PageViewModel : BaseViewModel
{
...
public override float Depth { get; set; }
}
和
public class OtherViewModel : BaseViewModel
{
...
public override float Depth { get; set; }
}
然后你只需要一个DataTemplate
<Grid.Resources>
<DataTemplate DataType="{x:Type local:BaseViewModel}">
<local:PageView />
</DataTemplate>
</Grid.Resources>
我猜测传递给转换器的奇怪值是由于DataTemplates被切换了。
未经过测试