2014-01-06 3 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를 업데이트하기위한 것입니다. 그의 문제는 UI에서 객체를 업데이트하는 것입니다. –

+0

ResourceDictionary는 단순화의 유물입니다. 실제 코드에는 다른 것들이 있습니다. 이 모델은 다른 어셈블리의 POCO입니다. 나는 그것을 다른 어셈블리에 추가하지 않기를 바랄 것이지만 클래스를 상속하고 인터페이스를 구현하는 것이 가능할 수 있습니다. –

2

컨버터는 그런 식으로 작동하지 않습니다 컬렉션. ConvertBack전체 컬렉션을 바꿀 때만 호출됩니다. 콜렉션의 항목이 수정 될 때 호출되지 않습니다. 귀하의 경우에는 컬렉션 (DataView)이 새 DataView 인스턴스로 대체되지 않고 오히려 수정되므로 ConvertBack이 호출되지 않습니다.

내게 묻는다면 어쨌든 왜 변환기를 사용해야하는지 알지 못합니다. Scenarios 속성에 직접 바인딩하고 뷰 모델에 의해 노출 된이 컬렉션에서 작업하거나 뷰 모델에서 변환 코드를 호출하여 DataView을 다른 속성에 표시합니다. 그런 다음 변환기를 지정하지 않고 해당 속성에 바인딩하면됩니다.

+0

ListBox는 여러 시나리오를 작업 할 수 있도록보기로 이미 시나리오에 바인딩되어 있습니다. 그것은 그리드에 도달하려고하는 시나리오 내에서의 옵션 콜렉션입니다. 그래서 당신이 말하는 것은 Scenario에서 DataView를 가져 오거나 설정하는 속성을 구현하는 것입니다. –

+0

아마도 이것이 가장 쉬운 방법 일 것입니다. 변환기를 제거했는지 확인하십시오. –

+0

나는 그걸로 놀았고 지금까지의 행동은 똑같은 것처럼 보입니다. DataView의 setter는 결코 호출되지 않습니다. –

관련 문제