为什么这个WPF自定义DependencyProperty绑定值转换器没有执行?

时间:2017-10-13 09:08:55

标签: c# wpf data-binding dependency-properties ivalueconverter

我正在构建我所学到的基本上是一个手风琴控件,其选择模式确保当时只有一个部分是打开的。每个部分都使用Expander控件实现,因此如果打开Expander,其他所有部分都应该关闭。

我以下列方式做到了:

  • 在视图模型(实现INotifyPropertyChanged)上添加了表示当前打开部分的Id的属性 ActiveQuestionId
  • 通过向转换器添加DependencyProperty ControlValue ,创建了一个继承自DependencyObject的转换器,该转换器能够将 ActiveQuestionId 转换为指示特定部分是否应该打开的布尔值。表示它属于哪个部分
  • 为每个部分创建一个本地转换器,并将 ControlValue 绑定到其部分的 QuestionId

虽然转换器方法成功执行,但问题是DependencyProperty ControlValue 从未设置,即使它成功绑定到某个值并且不会引发任何错误。我通过各种调试证实了这一点。因此结果是所有部分都停留在默认值上,呈现我想要的手风琴选择行为,没用。

为什么要忽略DependencyProperty绑定?是因为它是在绑定中定义的,还是其他什么?

说明

一切都是数据驱动的,并且在实现通用数据驱动版本之前我在静态模型中工作得很好。完全数据驱动的解决方案是必须的,因此使用单向多绑定或硬编码的XAML参数(我能够找到相关问题的解决方案)不是一种选择。

重要的是要注意所有其他绑定都是完美的,因此DataContext没有问题。因为一切都应该工作(在我看来),这也是我尚未采用WPF Toolkit Accordion方式的原因,所以请不要在最初提出这个建议(除非它真的是唯一的方法)。首先,作为WPF的新手,我想了解为什么这不起作用。

XAML(摘录 - 某些名称更改为混淆业务含义 - 中心部分是 IsExpanded 绑定):

<ItemsControl ItemsSource="{Binding QuestionSection.QuestionAssignments}"
          VerticalAlignment="Stretch" 
          HorizontalAlignment="Stretch"           
          Style="{x:Null}">
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Border BorderBrush="{StaticResource Grey400Brush}" 
                BorderThickness="0 1 0 0">
            <Expander Background="{StaticResource Grey200Brush}" 
                      Foreground="Black" 
                      Padding="0" 
                      Margin="0">
                <Expander.IsExpanded>
                    <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=views:TypeOfParentControl}"
                             Path="DataContext.ActiveQuestionId"
                             Mode="TwoWay">
                        <Binding.Converter>
                            <converters:TestConverter ControlValue="{Binding QuestionId}"/>
                        </Binding.Converter>
                    </Binding>
                </Expander.IsExpanded>
                <Expander.HeaderTemplate>
                    <!--Custom Styling Here, All Bindings Work-->
                </Expander.HeaderTemplate>
                <!--Content Here, All Bindings Work-->      
            </Expander>
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>

转换器(简化)

public class TestConverter : DependencyObject, IValueConverter
{
    public static readonly DependencyProperty ControlValueProperty = DependencyProperty.Register("ControlValue", typeof(short), typeof(TestConverter), new PropertyMetadata(default(short)));

    public short ControlValue
    {
        get { return (short) GetValue(ControlValueProperty); }
        set { SetValue(ControlValueProperty, value); }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (short)value==ControlValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? ControlValue : Binding.DoNothing;
    }
}

ViewModel中的ActiveQuestionId实现 - INotifyPropertyChanged已经过测试并且有效,ViewModel是父UserControl上的DataContext

    private short activeQuestionId;
    public short ActiveQuestionId
    {
        get
        {
            return activeQuestionId;
        }
        set
        {
            if (value != activeQuestionId)
            {
                activeQuestionId = value;
                OnPropertyChanged();
            }
        }
    }

1 个答案:

答案 0 :(得分:2)

当前的DataContext值不会继承到TestConverter实例。

您可以完全避免这种复杂的绑定,并使用ListBox实现您的控制:

<ListBox ItemsSource="{Binding QuestionSection.QuestionAssignments}"
         SelectedValuePath="QuestionId"
         SelectedValue="{Binding ActiveQuestionId}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Expander IsExpanded="{Binding IsSelected,
                                      RelativeSource={RelativeSource TemplatedParent}}">
                            ...
                        </Expander>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>