MultiValueConverter - NotifyPropertyChanged

时间:2014-03-24 13:19:20

标签: c# wpf datagrid combobox multibinding

对我的要求有一点恼人的问题,希望有可能解决。

让我们假设您有以下类:

public class Foo
{
    public string Name { get; set; }
    public List<FooB> FooBs { get; set; }
}

public class FooB
{
    public int Id1 { get; set; }
    public int Id2 { get; set; }
    public decimal Sum { get; set; }
}

现在,Foo类的FooB列表包含Id1Id2个值以及Sum值。

在我的WPF用户界面中,我有2 ComboBoxes1 DataGrid。 1 ComboBox包含有关Id1和其他Id2的信息。

现在在我的DataGrid中,我有一个显示为Foo的列表,其中2列显然是名称,但另一个让我头疼。

第二列应显示Sum类的"correct" FooB属性。

正确的FooB类由UI中的2个ComboBox和SelectedItem决定。

到目前为止我所做的是在我的CodeBehind中创建2个属性:(注意他们实际上有后台和PropertyChanged指定我将代码减少到我的主要问题)

public int SelectedId1 { get; set; }
public int SelectedId2 { get; set; }

这两个属性绑定到相应的ComboBox:

<c1:C1ComboBox ItemsSource="{Binding Id1s}"
                SelectedItem="{Binding SelectedId1}" />
<c1:C1ComboBox ItemsSource="{Binding Id2s}"
                SelectedItem="{Binding SelectedId2}" />

到目前为止,我的DataGrid看起来像这样:

<local:BindingProxy x:Key="BindingProxy"
                    Data="{Binding}" />

<DataGrid ItemsSource={Bindings Foos}>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTextColumn>
            <DataGridTextColumn.Binding>
                <MultiBinding Converter="{StaticResource GetCorrectFooB}">
                    <Binding Binding="."></Binding>
                    <Binding Binding="Data.SelectedId1"></Binding>
                    <Binding Binding="Data.SelectedId2"></Binding>
                </MultiBinding>
            </DataGridTextColumn.Binding>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

(注意BindingProxy来自这里:http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ - 启用从DataGridRow的DataContext中的Window获取数据)

转换器看起来像这样:

public class GetCorrectFooB : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var foo = (Foo)values[0];
        var id1 = (int) values[1];
        var id2 = (int) values[2];

        return foo.FooBs.First(x => x.Id1 == id1 && x.Id2 == id2).Sum;
    }

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

现在效果非常好,当我更改UI中的2个ComboBox中的值并相应地更新信息时,即使行为也正确但是......当底层Sum PropertyChanges并且得到PropertyChanges的通知时,UI不会更新。

如果我将列绑定到

,它的工作正常
<DataGridTextColumn Binding="{Binding FooBs[12].Sum}" />

你可以想象我不想在那里有索引,因为我需要通过UI中的ComboBox来更新它。

希望你能帮助我。

1 个答案:

答案 0 :(得分:2)

首先,我很难理解你当前的绑定表达式实际上是如何呈现总和,因为转换器似乎只返回 FooB < / strong>对象本身,而不是 sum 属性 - 所以除非你在 FooB 类上有一个特定的 ToString()实现,格式化它以显示总和,我完全迷失了。

其次,我认为您的问题源于您的 MultiBinding 仅基于两个属性 - Data.SelectedId1 Data.SelectedId2 ,因此,除非这两个属性中的任何一个发生变化,否则 MultiBinding 将不会收到 PropertyChanged 事件并进行更新。

结果的 sum 属性值的更改既不是两者之一,因此这很可能是视图未更新的原因。

我正在绞尽脑汁作为解决这个问题的最佳方法,也许你应该在ViewModel中处理这个问题,拥有一个名为 CurrentFooB 的属性或者你可以绑定的东西。然后,您可以将此值设置为组合框的 SelectionChanged 事件处理程序中的相应 FooB 实例。

您的绑定看起来像是:

<DataGrid ItemsSource={Binding Foos}>

    <DataGrid.Columns>

        <DataGridTextColumn Binding="{Binding Name}"/>
        <DataGridTextColumn Binding="{Binding CurrentFooB.Sum}"/>

    </DataGrid.Columns>

</DataGrid>

现在,只要 FooB 实现了INotifyPropertyChanged,当您当前找到的 FooB 总和发生变化时,您的视图就会更新。

编辑1(尝试动态设置绑定源):

<DataGrid ItemsSource={Binding Foos}>

    <DataGrid.Columns>

        <DataGridTextColumn Binding="{Binding Name}"/>
        <DataGridTextColumn>

            <DataGridTextColumn.Binding>

                <Binding Path="Sum">

                    <Binding.Source>

                        <MultiBinding Converter="{StaticResource GetCorrectFooB}">
                            <Binding Binding="."></Binding>
                            <Binding Binding="Data.SelectedId1"></Binding>
                            <Binding Binding="Data.SelectedId2"></Binding>
                        </MultiBinding>

                    </Binding.Source>

                </Binding>

            </DataGridTextColumn.Binding>

        </DataGridTextColumn>

    </DataGrid.Columns>

</DataGrid>