2016-03-17 79 views
1

我有一個自定義的控件,它在ItemsControl裏面提供了一個ComboBoxes列表。 ItemsControl綁定到一個int列表,因此每個ComboBox的DataContext只是一個int。這與SelectedIndex綁定,並且項目列表從別處引入。 ItemsControl的被定義爲直接綁定到DataContext的雙向綁定

<ItemsControl x:Name="itemsCtl" ItemsSource="{Binding SelectedSourceIndices}" 
       Grid.Row="1"> 
    <ItemsControl.Resources> 
     <util:BindingProxy x:Key="parent" Data="{Binding}" /> 
    </ItemsControl.Resources> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <ComboBox ItemsSource="{Binding Source={StaticResource parent},Path=Data.SourceFieldNames}" 
         SelectedIndex="{Binding Path=DataContext, 
          RelativeSource={RelativeSource Self}, 
          UpdateSourceTrigger=PropertyChanged, 
          Mode=TwoWay, 
          diag:PresentationTraceSources.TraceLevel=High}"/> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

這最初看起來不錯,但我發現,當你點擊一個組合框和改變選擇,變化不會傳播到下面的列表中。

我確實有困難,最初得到這個綁定工作,因爲沒有路,並發現瞭如何here.然而,盤算,首先要看是直接這個奇怪的結合上下文,我修改了它,而不是綁定到一個IntContainer列表,它是一個只包含一個int屬性的類。這工作正常,但它很混亂。

雖然沒有錯誤,即使對綁定進行完全跟蹤,我也看到了診斷輸出的差異。它的體積是笨重,但在採用了直板INT更改值我看到

System.Windows.Data Warning: 90 : BindingExpression (hash=20081636): Update - got raw value '3' 
System.Windows.Data Warning: 93 : BindingExpression (hash=20081636): Update - implicit converter produced '3' 
System.Windows.Data Warning: 94 : BindingExpression (hash=20081636): Update - using final value '3' 
System.Windows.Data Warning: 102 : BindingExpression (hash=20081636): SetValue at level 0 to ComboBox (hash=64451636) using DependencyProperty(DataContext): '3' 
System.Windows.Data Warning: 101 : BindingExpression (hash=20081636): GetValue at level 0 from ComboBox (hash=64451636) using DependencyProperty(DataContext): '3' 
System.Windows.Data Warning: 80 : BindingExpression (hash=20081636): TransferValue - got raw value '3' 
System.Windows.Data Warning: 84 : BindingExpression (hash=20081636): TransferValue - implicit converter produced '3' 
System.Windows.Data Warning: 89 : BindingExpression (hash=20081636): TransferValue - using final value '3' 

,並在更改值使用IntContainer當我看到

System.Windows.Data Warning: 90 : BindingExpression (hash=35938393): Update - got raw value '1' 
System.Windows.Data Warning: 94 : BindingExpression (hash=35938393): Update - using final value '1' 
System.Windows.Data Warning: 102 : BindingExpression (hash=35938393): SetValue at level 0 to IntContainer (hash=56037929) using ReflectPropertyDescriptor(Value): '1' 
System.Windows.Data Warning: 95 : BindingExpression (hash=35938393): Got ValueChanged event from IntContainer (hash=56037929) 
System.Windows.Data Warning: 101 : BindingExpression (hash=35938393): GetValue at level 0 from IntContainer (hash=56037929) using ReflectPropertyDescriptor(Value): '1' 
System.Windows.Data Warning: 80 : BindingExpression (hash=35938393): TransferValue - got raw value '1' 
System.Windows.Data Warning: 89 : BindingExpression (hash=35938393): TransferValue - using final value '1' 

所以它看起來像我以前做的方式當使用直線int時,獲得了綁定設置,最初獲取正確的值,但是將更改寫回到ComboBox本身,而不是寫入後備數組。這很奇怪,當然也沒有意義。

有誰知道如何更改綁定,以便更新列表中的值?

回答

1

SelectedIndex綁定到對象而不是對象的屬性。當它改變時,新值是一個完全不同於舊對象的對象 - 你不能將對象改變爲另一個對象(變量/屬性是,對象號)。而這個新對象不在你的數組中。

您需要將您的int列表轉換爲具有int值的對象列表。您已經在使用BindingProxy,因此您可以將其轉換爲這些列表並更新SelectedIndex綁定。

如果你真的想保留你的整數列表,你需要使用別的東西作爲代理。在下面的代碼中,我使用了一個轉換器將int列表轉換爲一個新類「IntProxy」的列表。

這裏是整個測試情況下,我對您的問題做出:

XAML:

<Window x:Class="WpfApplication32.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:WpfApplication32" 
     xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="450" Width="525"> 
    <StackPanel> 
     <StackPanel.Resources> 
      <local:IntProxyConverter x:Key="IntProxyConverter" /> 
     </StackPanel.Resources> 
     <StackPanel.DataContext> 
      <local:VM /> 
     </StackPanel.DataContext> 
     <ItemsControl x:Name="itemsCtl" ItemsSource="{Binding SelectedSourceIndices, Converter={StaticResource IntProxyConverter}}"> 
      <ItemsControl.Resources> 
       <local:BindingProxy x:Key="parent" Data="{Binding}" /> 
      </ItemsControl.Resources> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <ComboBox ItemsSource="{Binding Source={StaticResource parent},Path=Data.SourceFieldNames}" 
          SelectedIndex="{Binding Path=Value, 
           UpdateSourceTrigger=PropertyChanged, 
           Mode=TwoWay, 
           diag:PresentationTraceSources.TraceLevel=High}" /> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
     <TextBlock Text="{Binding AsString}" /> 
    </StackPanel> 
</Window> 

CS:

using System; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Globalization; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Data; 

namespace WpfApplication32 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 
    } 

    public class VM : INotifyPropertyChanged 
    { 
     public ObservableCollection<int> SelectedSourceIndices { get; set; } = new ObservableCollection<int>(new int[] { 5, 3, 6, 1, 0, 2, 4 }); 
     public string[] SourceFieldNames { get; set; } = new string[] { "S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7" }; 


     // The rest of this class is just to visualize the above properties in realtime 
     public event PropertyChangedEventHandler PropertyChanged; 
     public string AsString 
     { 
      get { 
       StringBuilder sb = new StringBuilder(); 
       foreach (var s in SelectedSourceIndices) 
        sb.AppendFormat("int = {0}", s).AppendLine(); 
       foreach (var s in SourceFieldNames) 
        sb.AppendFormat("name = {0}", s).AppendLine(); 
       return sb.ToString(); 
      } 
     } 

     public VM() 
     { 
      SelectedSourceIndices.CollectionChanged += SelectedSourceIndices_CollectionChanged; 
     } 

     private void SelectedSourceIndices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs("AsString")); 
     } 
    } 

    public class IntProxyConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      var src = value as Collection<int>; 
      var col = new ObservableCollection<IntProxy>(); 
      if (src != null) 
      { 
       for (int i = 0; i < src.Count(); i++) 
       { 
        col.Add(new IntProxy() { Index = i, Source = src }); 
       } 
      } 
      return col; 
     } 

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

    public class IntProxy : INotifyPropertyChanged 
    { 
     public int Value { get { return Source.ElementAt(Index); } set { if (Source[Index] != value) { Source[Index] = value; OPC("Value"); } } } 
     public int Index { get; set; } // This shouldn't be changing 
     public Collection<int> Source { get; set; } // This shouldn't be changing either 

     public event PropertyChangedEventHandler PropertyChanged; 
     private void OPC(string name) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(name)); 
     } 
    } 

    public class BindingProxy : Freezable 
    { 
     #region Overrides of Freezable 

     protected override Freezable CreateInstanceCore() 
     { 
      return new BindingProxy(); 
     } 

     #endregion 

     public object Data 
     { 
      get { return (object)GetValue(DataProperty); } 
      set { SetValue(DataProperty, value); } 
     } 

     // Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc... 
     public static readonly DependencyProperty DataProperty = 
      DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); 
    } 
} 
+0

Thanks-更爲詳細和完整的比我的預期。 :) –