2014-01-06 38 views
0

我在下面有一些代碼,這是我試圖做的一個簡單的例子。我正在使用轉換器嘗試使用我擁有的模型中的數據填充DataGrid。 DataGrid正在正確填充,但網格中的任何更改都不會被持久保存回對象。我已經將模式指定爲TwoWay。當我在轉換器ConvertBack方法上放置一個斷點時,它永遠不會被調用。WPF IValueConverter.ConvertBack不叫

我對WPF和MVVM相當陌生,所以我沒有看到我做錯了什麼。我不能改變模型,所以我想看看這個方法是否可行,除非有明確的方法。

XAML:

<Window x:Class="SampleBindingProblem.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:SampleBindingProblem" 
     Title="MainWindow" Height="400" Width="500"> 
    <Window.Resources> 
     <ResourceDictionary> 
      <local:ScenarioDataTableConverter x:Key="ScenarioDataTableConverter" /> 
     </ResourceDictionary> 
    </Window.Resources> 
    <Grid> 
     <ListBox ItemsSource="{Binding Scenarios}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <DataGrid Margin="5" ItemsSource="{Binding Path=Options, Mode=TwoWay, Converter={StaticResource ScenarioDataTableConverter}}" /> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ListBox> 
    </Grid> 
</Window> 

App.xaml.cs:

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Data; 
using System.Globalization; 
using System.Windows; 
using System.Windows.Data; 

namespace SampleBindingProblem 
{ 
    public class ColumnInfo 
    { 
     public static readonly String[] ColumnLabels = new String[] { "Variable1", "Variable2", "Variable3", "Variable4", "Variable5" }; 
    } 

    public class ScenarioOption 
    { 
     public String Label { get; set; } 
     public String[] Variables { get; set; } 
    } 

    public class Scenario 
    { 
     public ScenarioOption[] Options { get; set; } 
    } 

    internal class ScenarioDataTableConverter : IValueConverter 
    { 
     public Object Convert (Object value, Type targetType, Object parameter, CultureInfo culture) 
     { 
      if (value == null) 
       return (null); 

      ScenarioOption[] options = (ScenarioOption[]) value; 

      DataTable table = new DataTable(); 

      table.Columns.Add("Label", typeof(String)); 
      for (Int32 c = 0; c < ColumnInfo.ColumnLabels.Length; ++c) 
       table.Columns.Add(ColumnInfo.ColumnLabels[c], typeof(String)); 
      foreach (ScenarioOption option in options) 
      { 
       DataRow row = table.NewRow(); 
       List<String> lst = new List<String>(); 
       lst.Add(option.Label); 
       lst.AddRange(option.Variables); 
       row.ItemArray = lst.ToArray(); 
       table.Rows.Add(row); 
      } 

      return (table.DefaultView); 
     } 

     public Object ConvertBack (Object value, Type targetType, Object parameter, CultureInfo culture) 
     { 
      return (null); 
     } 
    } 

    internal class ViewModel : INotifyPropertyChanged 
    { 
     public void RaisePropertyChanged (String property) 
     { 
      if (this.PropertyChanged != null) 
       this.PropertyChanged(this, new PropertyChangedEventArgs(property)); 
     } 

     public event PropertyChangedEventHandler PropertyChanged = null; 

     public ObservableCollection<Scenario> Scenarios { get; set; } 

     public ViewModel() 
     { 
      Scenario s1 = new Scenario(); 
      s1.Options = new ScenarioOption[] { 
       new ScenarioOption() { Label = "Opt1", Variables=new String[] { "1", "2", "3", "4", "5" } }, 
       new ScenarioOption() { Label = "Opt2", Variables=new String[] { "2", "3", "4", "5", "6" } }, 
       new ScenarioOption() { Label = "Opt3", Variables=new String[] { "3", "4", "5", "6", "7" } }, 
      }; 
      Scenario s2 = new Scenario(); 
      s2.Options = new ScenarioOption[] { 
       new ScenarioOption() { Label = "Opt1", Variables=new String[] { "1", "2", "3", "4", "5" } }, 
       new ScenarioOption() { Label = "Opt2", Variables=new String[] { "2", "3", "4", "5", "6" } }, 
       new ScenarioOption() { Label = "Opt3", Variables=new String[] { "3", "4", "5", "6", "7" } }, 
      }; 

      this.Scenarios = new ObservableCollection<Scenario>(); 
      this.Scenarios.Add(s1); 
      this.Scenarios.Add(s2); 
     } 
    } 

    /// <summary> 
    /// Interaction logic for App.xaml 
    /// </summary> 
    public partial class App : Application 
    { 
     private void Application_Startup (Object sender, StartupEventArgs e) 
     { 
      MainWindow window = new MainWindow(); 
      window.DataContext = new ViewModel(); 
      window.ShowDialog(); 
     } 
    } 
} 
+0

ConvertBack()方法當前返回null,因爲我尚未實現它,因爲它尚未被調用。 –

回答

0

這聽起來像經典的初學者的錯誤......我認爲你需要實現你的模型類的INotifyPropertyChanged Interface。這個想法是,當任何屬性值改變時,你通知INotifyPropertyChanged接口。從鏈接頁面上MSDN:

public string CustomerName 
{ 
    get 
    { 
     return this.customerNameValue; 
    } 
    set 
    { 
     if (value != this.customerNameValue) 
     { 
      this.customerNameValue = value; 
      NotifyPropertyChanged(); 
     } 
    } 
} 

的UI然後可以從模型類更新和模型類將能夠從UI的變化進行更新。有關完整示例,請參閱MSDN上的鏈接頁面。


而且,你不需要在Window.Resources部分申報ResourceDictionary ......這一個ResourceDictionary

<Window.Resources> 
    <local:ScenarioDataTableConverter x:Key="ScenarioDataTableConverter" /> 
</Window.Resources> 
+0

'INotifyPropertyChanged'用於更新用戶界面。他的問題是相反的 - 從UI更新對象。 –

+0

ResourceDictionary是簡化的遺蹟;在真實的代碼中還有其他的東西。該模型也是來自不同裝配的POCO。我希望不必將其添加到其他程序集中,但可以繼承該類並實現接口。 –

2

,當涉及到轉換器不工作方式集合。只有在整個集合被替換時纔會調用ConvertBack。當集合中的項目被修改時它不會被調用。在你的情況下,集合(DataView)沒有被新的DataView實例取代,而是被修改,這就是爲什麼ConvertBack沒有被調用。

如果你問我,我不明白你爲什麼需要使用轉換器。直接綁定到Scenarios屬性,並在視圖模型公開的該集合上工作,或者在您的視圖模型中調用轉換代碼,並在不同的屬性中顯示結果DataView。那麼你只需要綁定到該屬性而不指定轉換器。

+0

該列表框已被綁定到方案,因爲該視圖允許多個方案進行處理。這是我嘗試訪問網格的場景中的選項集合。所以你說的是在Scenario中實現一個屬性來獲取/設置DataView? –

+0

這可能是最簡單的方法。只要確保你移除了轉換器。 –

+0

我一直在玩,迄今爲止行爲似乎完全相同。 DataView上的setter永遠不會被調用。 –