2012-03-30 1 views
2

これは少し時間がかかるかもしれませんが、ここに入ります。私は、MVVMパターン(基本的に私の「本当の」アプリのコードの愚かなバージョン)を使って、小さなウィザードスタイルのサンプルアプリケーションを作成しました。このアプリでは、メインウィンドウは、ビューモデルのList < ..>から移動し、各ビューモデルは関連付けられたビューを表示します。私は本質的に同じ2つのビューモデルクラスを持ち、同じビューを表示します。MVVM IValueConverter浮動小数点数を求めるときに空の文字列引数を取得するメソッドを変換します。

ビューでは、コンボボックスにfloatの配列が設定されています。 SelectedItemは、ビューモデルのfloatプロパティにバインドされています。コンボボックスのテンプレートを作成して各アイテムをTextBlockとして表示し、テキストは浮動小数点値をとり、値コンバータを通過します。

問題は、ビューモデル間で前後に切り替えると、すべてのビューモデルが切り替わる限り、すべて同じクラスになります。現在のページを別のビューモデルのインスタンスに変更すると、値コンバータのConvertは長さゼロの文字列である 'value'パラメータで呼び出されます。それまでのところ、Convertは浮動小数点数で呼び出されていましたが、私は期待していました。

私の質問:ビューモデルクラスを切り替える場合にのみコンバータが空の文字列で呼び出されるのはなぜですか?

メインウィンドウのXAMLとビューモデル、および各 "ページ"に表示されるビュー/ビューモデルを添付します。メインウィンドウビューモデルには、PageViewModelの2つのインスタンスと、OtherViewModelの2つのインスタンスを含むリストがあります。私は最初の2つの間で前後に切り替えることができ、値コンバータはfloat値で呼び出されるだけです。最初の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(); 
        } 
    } 
    

注意他の2つのインスタンスに続く1つのビューモデルの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); 
         } 
        } 
    

注:私は変換メソッドで例外を削除することができ、すべてが正常に動作するようです。しかし、私はなぜこれが起こっているのか知りたいです。予想される浮動小数点数の代わりにコンバーターが空の文字列を取得するのはなぜですか?また、ビューモデルを切り替えるときだけです。

洞察力があれば幸いです。前もって感謝します... ジョー

+0

DataTemplateで浮動するように設定しようとしましたか? – Silvermind

+0

Silvermind、DataType = "{x:Type sys:Single}"をPageViewのDataTemplate行に追加しようとしましたが、それでも同じ問題が発生します。私はまだ値として空の文字列でコンバータを呼び出します。 –

答えて

3

をテストしたわけではありません。

ビューを閉じると、コンボボックス選択が空になります。これは、SelectionChangedイベントを処理し、SelectionChangedEventArgs RemovedItemsコレクションを調べることで確認できます。 これで、String.EmptyがValueConverterに渡されます。

私の場合、有効な値としてstring.Emptyを許可し、string.Emptyを返すようにValueConverter.Convertを変更しました。 これは私が使用したコードです:

// 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のDataTemplateに

<Grid.Resources> 
    <DataTemplate DataType="{x:Type local:BaseViewModel}"> 
     <local:PageView /> 
    </DataTemplate> 
</Grid.Resources> 
が必要

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

をお試しください

私は、DataTemplatesが切り替わっているためにコンバータに渡されている奇妙な値を推測しています。

は私が(代わりにフロートの列挙と)同じ問題を持っていた

+0

フィル、私はこれを試して、それは動作するようです。私は、ビューモデル、DataTemplateだけを変更する必要はなく、問題は消えました。 –

+0

しかし、これはDataTemplateが異なる必要がある状況では依然として問題となります。このサンプルアプリケーションでは、2つのビューモデル間でビューを再利用しました。しかし、私のより複雑なアプリでは、それは選択肢ではありません。だから、まだ完全に吹き飛ばされた解決策についてはまだ分かりません。より多くの検索を試みます... –

関連する問題