C# 为什么值转换器上的WPF自定义DependencyProperty绑定未执行?

C# 为什么值转换器上的WPF自定义DependencyProperty绑定未执行?,c#,wpf,data-binding,dependency-properties,ivalueconverter,C#,Wpf,Data Binding,Dependency Properties,Ivalueconverter,我正在构建我所学到的基本上是一个手风琴控件,带有一个选择模式,确保在同一时间只有一个部分打开。每个部分都是使用扩展器控件实现的,所以如果打开了扩展器上的,所有其他部分都应该关闭 我是通过以下方式做到这一点的: 在视图模型(实现INotifyPropertyChanged)上添加了表示当前打开节的Id的属性ActiveQuestionId 创建了从DependencyObject继承的转换器,该转换器能够将ActiveQuestionId转换为布尔值,指示是否应打开特定节,方法是向转换器添加De

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

我是通过以下方式做到这一点的:

  • 在视图模型(实现INotifyPropertyChanged)上添加了表示当前打开节的Id的属性ActiveQuestionId
  • 创建了从DependencyObject继承的转换器,该转换器能够将ActiveQuestionId转换为布尔值,指示是否应打开特定节,方法是向转换器添加DependencyProperty ControlValue,指示该节所属的节
  • 为每个节创建本地转换器,并将ControlValue绑定到其节的QuestionId
当转换器方法成功执行时,问题是DependencyProperty ControlValue从未设置,即使它成功绑定到一个值并且没有引发任何错误。我已经通过各种调试确认了这一点。因此,结果是所有部分都被默认值卡住,从而使我想要的手风琴选择行为毫无用处

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

备注

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

需要注意的是,所有其他绑定都可以完美地工作,因此在DataContext方面没有问题。在我看来,一切都应该是可行的,这也是为什么我还没有采用WPF工具包手风琴的方式,所以请不要一开始就建议这样做(除非这真的是唯一的方式)。首先,作为WPF的新手,我想了解为什么这不起作用

XAML(摘录-一些名称更改为模糊业务含义-中心部分是扩展绑定):

<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>
ViewModel中的ActiveQuestionId实现-INotifyPropertyChanged已测试并运行,ViewModel是父用户控件上的DataContext

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

当前DataContext值不会向下继承到TestConverter实例

您可以完全避免这种复杂的绑定,并通过使用列表框实现您的控件:

<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>

...

可能当前DataContext值没有继承到TestConverter实例。您也许可以通过使用IMultiValueConverter的多重绑定来更好地解决这个问题。@Clemens可能不是,这是因为它在绑定中吗?如果绑定到任何其他元素,DataContext工作正常,即绑定到QuestionAssignment类上的QuestionId属性。我确实尝试过多重绑定,但意识到它并没有以支持我的用例的方式真正支持ConvertBack。也就是说,如果IsExpanded设置为true,我需要在视图模型上设置(ConvertBack)ActiveQuestionId,如果设置为false,则什么也不做。否则,不会通知其他部分有关更改。如果不考虑MVVM,我当然可以实现这一点,但我希望保持事物的解耦。如果希望转换器参与继承上下文,请尝试在
扩展器中将其声明为资源。您还需要使其扩展
Freezable
而不是
DependencyObject
@MikeStrobel感谢您的输入,我尝试使用带有x:Shared=“False”的模板方法中的资源,根据我目前的理解,应该为每个项目创建一个单独的转换器实例。我没有使用Freezable,我将研究该类,但现在选择使用一个列表框,因为它符合我的开箱即用的需要,在我的情况下是一个更优雅的解决方案。@SuppaiKamo听起来不错。仅供参考,
Freezable
是参与继承上下文的最高级类,因此,如果您想将对象用作绑定目标,它必须派生自该类。该类工作非常出色,显然是适合我的用例的方法,谢谢。我需要更好地了解我的控件的功能:)
<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>