【问题标题】:MVVM IValueConverter Convert method getting empty string argument when expecting a floatMVVM IValueConverter Convert 方法在期望浮点数时获取空字符串参数
【发布时间】:2012-03-30 15:34:48
【问题描述】:

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

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

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

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

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

代码 sn-ps:

  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. 主窗口视图模型

        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 个实例。

  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);
          }
       }
    

注意:我可以删除 Convert 方法中的异常,一切似乎都正常。但是,我想知道为什么会这样。为什么转换器得到一个空字符串而不是预期的浮点数,并且只有当我们切换视图模型时?

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

【问题讨论】:

  • 您是否尝试将 DataTemplate 上的 DataType 设置为浮动?
  • Silvermind,我刚刚尝试将 DataType="{x:Type sys:Single}" 添加到 PageView 的 DataTemplate 行,但我仍然遇到同样的问题。我仍然收到一个以空字符串作为值的转换器的调用。

标签: wpf binding mvvm ivalueconverter


【解决方案1】:

我遇到了同样的问题(使用枚举而不是浮点数)。

当视图关闭时,组合框选择被清空。您可以通过处理 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;
}

【讨论】:

    【解决方案2】:

    试试

    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。

    未测试

    【讨论】:

    • 菲尔,我试过了,它似乎有效。我什至不必更改视图模型,只需更改 DataTemplate,问题就消失了。
    • 也就是说,在 DataTemplate 必须不同的情况下,这仍然是一个问题。在这个示例应用程序中,我在 2 个视图模型之间重新使用了视图。但是,在我更复杂的应用程序中,这不是一个选择。所以,仍然不确定一个完整的解决方案。将尝试更多搜索...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-21
    • 2020-08-25
    • 2018-02-07
    • 2016-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多