我正在使用一个自定义控件,它在ItemsControl中显示了一个ComboBox列表。 ItemsControl绑定到int列表,因此每个ComboBox的DataContext只是一个int。这与SelectedIndex绑定,项目列表来自其他地方。 ItemsControl定义为
SELECT order,
MAX( CASE WHEN attribute LIKE 'CL_ACC%G_ADSL_DETAILS1COS_BLEACHING_FLAG'
THEN value END ) AS tech,
MAX( CASE WHEN attribute LIKE 'CL_ACC%G_ADSL_DETAILS1G_6LAYER_COS_CIPR_AF1'
THEN value END ) AS bleach,
MAX( CASE WHEN attribute LIKE 'CL_ACC%G_ADSL_DETAILS1G_6LAYER_COS_CIPR_AF2'
THEN value END ) AS af1,
MAX( CASE WHEN attribute LIKE 'CL_ACC%G_ADSL_DETAILS1G_6LAYER_COS_CIPR_AF3'
THEN value END ) AS af2,
MAX( CASE WHEN attribute LIKE 'CL_ACC%G_ADSL_DETAILS1G_6LAYER_COS_CIPR_AF4'
THEN value END ) AS af3,
MAX( CASE WHEN attribute LIKE 'CL_ACC%G_ADSL_DETAILS1G_6LAYER_COS_CIPR_EF'
THEN value END ) AS af4,
MAX( CASE WHEN attribute LIKE 'CL_ACC%G_ADSL_DETAILS_ACCESS_TECHNOLOGY'
THEN TO_DATE( value, 'DD/MM/YYYY' ) END ) AS ef
FROM attributes
WHERE order IN ( '802605-S844' /*, ...*/ )
GROUP BY order;
这最初看起来很好,但我发现当你点击ComboBox并更改选择时,更改不会传播到基础List。
我确实很难让这个绑定工作,因为没有Path,并且发现了here.然而,想到第一个看起来就是这个奇怪的绑定直接上下文,我修改了它而是绑定到IntContainer列表,是一个只包含单个int属性的类。这很好用,但很麻烦。
尽管没有错误,即使完全跟踪绑定,我确实看到了诊断输出的差异。它的大部分都很笨拙,但是在使用直线int更改值时我看到了
<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>
并在使用IntContainer时更改值我看到
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'
所以它看起来像我设置绑定的方式,当使用直接int时,最初得到正确的值,然后将更改写回ComboBox本身,而不是写入支持数组。这很奇怪,当然也没有意义。
有没有人知道如何更改绑定以便更新List中的值?
答案 0 :(得分: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));
}
}