2015-08-25 37 views
1

我在我的視圖模型,其具有例如具有多個屬性的類的屬性的屬性如何使用IValueConverter綁定到WPF中某個對象的不同屬性?

public class Content 
{ 
    public int Selector { get; set; } 
    public int Value1 { get; set; } 
    public int Value2 { get; set; } 
    public int Value3 { get; set; } 
    public int Value4 { get; set; } 
} 

public class ViewModel 
{ 
    public Content ContentInstance { get; set; } 
} 

我想用一個轉換器將它綁定到我的xaml中,這樣選擇器的值決定了哪個值綁定到元素,例如

<TextBox Text="{Binding ContentInstance, Converter="ContentValueConverter", TargetNullValue='', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> 

到目前爲止,我有:

public class ContentValueConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
    { 
     var contentPart = value as Content; 
     if(contentPart == null) return; 

     switch(contentPart.Selector) 
     { 
      case 1: 
       return contentPart.Value1; 
      //etc 
     } 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

這個工程要顯示的數值,但不保存值回模型。 我寧願將它保存在IValueConverter中,因爲這必須添加到代碼庫中的許多地方。任何幫助將價值節約回到模型將不勝感激。

+2

您需要實現* ConvertBack *方法,並使用附加屬性作爲ConverterParameter傳入您的內容。爲什麼不只是在Content類中做所有事情而不是使用轉換器?甚至在你的視圖模型中?奇怪你想如何在UI轉換器中做潛在的'業務邏輯'。 – Ruskin

+2

@Ruskin +1。視圖模型的重點在於將數據轉換爲視圖容易使用的格式。如果您發現自己不得不在轉換器中實現此類特定邏輯,那麼這通常表明您的視圖模型沒有正確執行其工作。 –

+0

@Ruskin我明白這是商業邏輯,但是我正在一個非常古老的代碼庫中工作,這是在沒有考慮MVVM的情況下編寫的。我的老闆要求我做這個影響很小,所以我僅限於在UI庫中工作,但他也希望它整齊地打包,這樣它就可以應用到代碼庫的其餘部分。但你是對的,這不是一個理想的做法。 –

回答

0

您的方法還有一個缺陷 - 即使Content實現了INotifyPropertyChanged,任何對Content的屬性進行的更改都不會被WPF拾取。

如果你不那麼在意,理論上,根據您的情況,您可以存儲參考獲取傳遞到Convert方法Content對象和ConvertBack重用。它不是很乾淨,也不是WPFish,每個綁定需要一個單獨的轉換器實例(所以轉換器必須內聯定義,而不是資源)。

那麼,爲什麼不你在你的視圖模型實現代理財產呢?

public class ViewModel 
{ 
    public Content ContentInstance { get; set; } 
    public int Value 
    { 
     get 
     { 
      switch (Content.Selector) 
      { 
       case 1: 
        return contentPart.Value1; 
       //etc 
      } 
     } 
     set 
     { 
      switch (Content.Selector) 
      { 
       case 1: 
        contentPart.Value1 = value; 
        break; 
       //etc 
      } 
     } 
    } 
} 

然後你就可以直接綁定到它:

<TextBox Text="{Binding Value, Mode=TwoWay}"/> 

清潔和有效的。如果Content執行INotifyPropertyChanged那麼ViewModel可以攔截它並引發Value屬性的更改事件。

0

綁定一個文本框每個屬性,並把它們放在同一格列/一行。使用轉換器將選擇器屬性的可見性綁定到選擇器屬性,該轉換器將使用選擇器的哪個值使其可見。現在,您的可見文本框將綁定到正確的屬性,並由您的選擇器選擇控制。您可以通過將它放在單獨的UserControl或ControlTemplate中使其可重用。

0

你也需要貫徹ContentValueConverter的ConvertBack方法。 ConvertBack用於將結果轉換回視圖模型

+0

如果你可以顯示代碼如何做它! – AADProgramming

0

你不能這樣做,因爲你需要一個ConvertBack方法來返回一個新的Content實例。

我會在球場上建立5個文本框,每個綁定雙向和隱藏基礎上,選擇價值(後來編輯:我看到李O.給出了相同的解決方案,但這裏是一些代碼)。

<TextBox Text="{Binding ContentInstance.Value1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
     Visibility="{Binding ContentInstance.Selector, Converter=SelectorToVisibilityConverter, ConverterParameter=1}"/> 
<TextBox Text="{Binding ContentInstance.Value2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
     Visibility="{Binding ContentInstance.Selector, Converter=SelectorToVisibilityConverter, ConverterParameter=2}"/> 
... 

和轉換器:

public class SelectorToVisibilityConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
    { 
     var selector = (int)value; 
     var desired = (int)parameter; //may need a string to int conversion 

     return selector == desired ? Visibility.Visible : Visibility.Collapsed; 
    } 
} 

注意,這是寫在記事本中,它不是在所有測試!

我猜你也可以使用一些附加的行爲。

0

這是基於一個附加的依賴特性的解決方案:

public static class GetValueBasedOnContentSelectorBehavior 
{ 
    private static readonly Dictionary<Content, TextBox> Map = new Dictionary<Content, TextBox>(); 

    public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached(
     "Content", typeof(Content), typeof(GetValueBasedOnContentSelectorBehavior), new PropertyMetadata(default(Content), OnContentChanged)); 

    private static void OnContentChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 
    { 
     var textBox = dependencyObject as TextBox; 
     if (textBox == null) return; 

     var oldContent = dependencyPropertyChangedEventArgs.OldValue as Content; 
     if (oldContent != null && Map.Remove(oldContent)) 
      oldContent.PropertyChanged -= ContentOnPropertyChanged; 

     var newContent = dependencyPropertyChangedEventArgs.NewValue as Content; 
     if (newContent != null) 
     { 
      newContent.PropertyChanged += ContentOnPropertyChanged; 
      Map.Add(newContent, textBox); 

      RedoBinding(textBox, newContent); 
     } 
    } 

    private static void ContentOnPropertyChanged(object sender, PropertyChangedEventArgs args) 
    { 
     var content = sender as Content; 
     if (content == null) return; 

     TextBox textBox; 
     if (args.PropertyName == "Selector" && Map.TryGetValue(content, out textBox)) 
      RedoBinding(textBox, content); 
    } 

    private static void RedoBinding(TextBox textBox, Content content) 
    { 
     textBox.SetBinding(TextBox.TextProperty, 
      new Binding { Source = content, Path = new PropertyPath("Value" + content.Selector) }); 
    } 

    public static Content GetContent(TextBox txtBox) 
    { 
     return (Content)txtBox.GetValue(ContentProperty); 
    } 

    public static void SetContent(TextBox txtBox, Content value) 
    { 
     txtBox.SetValue(ContentProperty, value); 
    } 
} 

它如何使用:

<TextBox testWpf:GetValueBasedOnContentSelectorBehavior.Content="{Binding ContentInstance}"/> 
<ComboBox SelectedItem="{Binding ContentInstance.Selector}" ItemsSource="{Binding AvailableValues}"/> 

和其他一些附加的東西:

public class Content : INotifyPropertyChanged 
{ 
    private int _selector; 

    public int Selector 
    { 
     get { return _selector; } 
     set 
     { 
      _selector = value; 
      OnPropertyChanged(); 
     } 
    } 

    public int Value1 { get; set; } 
    public int Value2 { get; set; } 
    public int Value3 { get; set; } 
    public int Value4 { get; set; } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

public class ViewModel 
{ 
    public Content ContentInstance { get; set; } 
    public IEnumerable<int> AvailableValues { get { return Enumerable.Range(1, 4); } } 
} 
相關問題