MVVM IValueConverter转换方法在期望浮点时获取空字符串参数

时间:2012-03-30 15:34:49

标签: wpf binding mvvm ivalueconverter

这可能会有点长,但这里有。我使用MVVM模式创建了一个小型的向导式示例应用程序(基本上是我的“真实”应用程序中的代码的简化版本)。在这个应用程序中,主窗口从List< ..>移动视图模型,每个视图模型显示其关联的视图。我有两个基本相同的视图模型类,它们显示相同的视图。

在视图上是一个组合框,填充了一个float数组。 SelectedItem绑定到视图模型上的float属性。我已经为组合框创建了一个模板,将每个项目显示为TextBlock,文本采用浮点值并通过值转换器。

问题是,当我在视图模型之间来回切换时,只要我切换到的每个视图模型属于同一类,所有工作都可以正常工作。只要我将当前页面更改为不同视图模型的实例,就会使用'value'参数调用值转换器的Convert,该参数是零长度字符串。直到那时,转换只是用浮点调用,正如我所料。

我的问题:为什么在切换视图模型类的情况下,仅使用空字符串调用转换器?

我正在附加主窗口XAML和视图模型,以及为每个“页面”显示的视图/视图模型。您会注意到主窗口视图模型有一个列表,其中包含2个PageViewModel实例和2个OtherViewModel实例。我可以在前2个精细之间来回切换,而值转换器只能用浮点值调用。切换到第一个OtherViewModel实例后,转换器将获得一个“额外”调用,并将空字符串作为值。

代码段:

  1. 主窗口

    <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="&lt; Back"
            VerticalAlignment="Top"
            HorizontalAlignment="Left"
            Command="{Binding BackCommand}" />
    
    <Button Margin="85,115,0,0"
            Width="75"
            Content="Next &gt;"
            VerticalAlignment="Top"
            HorizontalAlignment="Left"
            Command="{Binding NextCommand}" />
    
  2. 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();
        }
     }
    
  3. 注意一个视图模型的2个实例,后跟另一个视图模型的2个实例。

    1. 网页浏览

      <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>
      
    2. 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);
            }
         }
      
    3. 注意:我可以在Convert方法中删除异常,一切似乎都能正常工作。但是,我想知道为什么会这样。为什么转换器变为空字符串而不是预期的浮点数,并且只有当我们切换视图模型时才会这样?

      任何见解都将不胜感激。提前致谢... 乔

2 个答案:

答案 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被切换了。

未经过测试