为什么此 WPF 自定义 DependencyProperty 绑定到值转换器未执行?

Why is this WPF custom DependencyProperty binding on value converter not executing?

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

我是按以下方式完成的:

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

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

备注

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

重要的是要注意所有其他绑定工作完美,因此在 DataContext 方面没有问题。由于一切都应该有效(在我看来),这也是我还没有采用 WPF 工具包手风琴方式的原因,所以请不要一开始就建议这样做(除非它确实是唯一的方式)。首先,作为 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();
            }
        }
    }

当前的 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>