为什么此 WPF 自定义 DependencyProperty 绑定到值转换器未执行?
Why is this WPF custom DependencyProperty binding on value converter not executing?
我正在构建我所学的基本上是一个手风琴控件,具有一种选择模式,可确保一次只打开一个部分。每个部分都是使用 Expander 控件实现的,因此如果打开 Expander,则应关闭所有其他部分。
我是按以下方式完成的:
- 在视图模型(实现 INotifyPropertyChanged)上添加了一个 属性 表示当前打开部分的 Id,ActiveQuestionId
- 创建了一个继承自 DependencyObject 的转换器,它能够通过添加 DependencyProperty ControlValue 将 ActiveQuestionId 转换为指示是否应打开特定部分的布尔值 到指示它属于哪个部分的转换器
- 为每个部分创建一个本地转换器,其中 ControlValue 绑定到其部分
的 QuestionId
虽然转换器方法成功执行,但问题是 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>
我正在构建我所学的基本上是一个手风琴控件,具有一种选择模式,可确保一次只打开一个部分。每个部分都是使用 Expander 控件实现的,因此如果打开 Expander,则应关闭所有其他部分。
我是按以下方式完成的:
- 在视图模型(实现 INotifyPropertyChanged)上添加了一个 属性 表示当前打开部分的 Id,ActiveQuestionId
- 创建了一个继承自 DependencyObject 的转换器,它能够通过添加 DependencyProperty ControlValue 将 ActiveQuestionId 转换为指示是否应打开特定部分的布尔值 到指示它属于哪个部分的转换器
- 为每个部分创建一个本地转换器,其中 ControlValue 绑定到其部分 的 QuestionId
虽然转换器方法成功执行,但问题是 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>