当绑定未明确设置 UpdateSourceTrigger 时,DependencyProperty 无法在绑定内创建正确的 UpdateSourceTrigger

DependencyProperty fails to create proper UpdateSourceTrigger inside binding when the binding has not set UpdateSourceTrigger explicitly

@update - 请参阅下面的更新

我得到了 DataGridTemplateColumn 中使用的 UserControl。当我像这样 {Binding MyProperty, UpdateSourceTrigger=PropertyChanged} 指定绑定时,它会更新源。但是我忽略了 UpdateSourceTrigger 它不会更新源代码。

DependencyProperty是这样创建的:

public static readonly DependencyProperty RatingValueProperty = DependencyProperty.Register(
    "RatingValue", typeof(int?), typeof(RatingControl), new FrameworkPropertyMetadata(default(int),
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
        DependencyPropertyChangedCallback,
        CoerceRatingValueCallback,
        false,
        UpdateSourceTrigger.PropertyChanged
));

正如您在下面看到的,当省略 UpdateSourceTrigger 时,Binding 是使用 Default 标志创建的,如果没有,则使用 PropertyChanged 标志创建。

"not working" 绑定(参见两个示例的第 26 行):

0   _binding    {System.Windows.Data.Binding}   System.Windows.Data.BindingBase {System.Windows.Data.Binding}
1   AsyncState  null    object
2   BindingGroupName    ""  string
3   BindsDirectlyToSource   false   bool
4   Converter   null    System.Windows.Data.IValueConverter
5   ConverterCulture    null    System.Globalization.CultureInfo
6   ConverterCultureInternal    null    System.Globalization.CultureInfo
7   ConverterParameter  null    object
8   Delay   0   int
9   ElementName null    string
10  FallbackValue   {{DependencyProperty.UnsetValue}}   object {MS.Internal.NamedObject}
11  Flags   Default System.Windows.Data.BindingBase.BindingFlags
12  IsAsync false   bool
13  Mode    Default System.Windows.Data.BindingMode
14  NotifyOnSourceUpdated   false   bool
15  NotifyOnTargetUpdated   false   bool
16  NotifyOnValidationError false   bool
17  Path    {System.Windows.PropertyPath}   System.Windows.PropertyPath
18  RelativeSource  null    System.Windows.Data.RelativeSource
19  Source  null    object
20  SourceReference null    MS.Internal.Data.ObjectRef
21  StringFormat    null    string
22  TargetNullValue {{DependencyProperty.UnsetValue}}   object {MS.Internal.NamedObject}
23  TransfersDefaultValue   true    bool
24  TreeContextIsRequired   false   bool
25  UpdateSourceExceptionFilter null    System.Windows.Data.UpdateSourceExceptionFilterCallback
26  UpdateSourceTrigger Default System.Windows.Data.UpdateSourceTrigger
27  ValidatesOnDataErrors   false   bool
28  ValidatesOnExceptions   false   bool
29  ValidatesOnNotifyDataErrors true    bool
30  ValidatesOnNotifyDataErrorsInternal true    bool
31  ValidationRules Count = 0   System.Collections.ObjectModel.Collection<System.Windows.Controls.ValidationRule> {MS.Internal.Controls.ValidationRuleCollection}
32  ValidationRulesInternal Count = 0   System.Collections.ObjectModel.Collection<System.Windows.Controls.ValidationRule> {MS.Internal.Controls.ValidationRuleCollection}
33  XPath   null    string
34  _attachedPropertiesInPath   0   int
35  _bindsDirectlyToSource  false   bool
36  _doesNotTransferDefaultValue    false   bool
37  _flags  Default System.Windows.Data.BindingBase.BindingFlags
38  _isAsync    false   bool
39  _isSealed   true    bool
40  _ppath  {System.Windows.PropertyPath}   System.Windows.PropertyPath
41  _source {MS.Internal.Data.ExplicitObjectRef}    MS.Internal.Data.ObjectRef {MS.Internal.Data.ExplicitObjectRef}
42  _sourceInUse    None    System.Windows.Data.Binding.SourceProperties
43  _values {MS.Internal.UncommonValueTable}    MS.Internal.UncommonValueTable
44  Static members      

"working"绑定:

0   _binding    {System.Windows.Data.Binding}   System.Windows.Data.BindingBase {System.Windows.Data.Binding}
1   AsyncState  null    object
2   BindingGroupName    ""  string
3   BindsDirectlyToSource   false   bool
4   Converter   null    System.Windows.Data.IValueConverter
5   ConverterCulture    null    System.Globalization.CultureInfo
6   ConverterCultureInternal    null    System.Globalization.CultureInfo
7   ConverterParameter  null    object
8   Delay   0   int
9   ElementName null    string
10  FallbackValue   {{DependencyProperty.UnsetValue}}   object {MS.Internal.NamedObject}
11  Flags   PropDefault | ValidatesOnNotifyDataErrors   System.Windows.Data.BindingBase.BindingFlags
12  IsAsync false   bool
13  Mode    Default System.Windows.Data.BindingMode
14  NotifyOnSourceUpdated   false   bool
15  NotifyOnTargetUpdated   false   bool
16  NotifyOnValidationError false   bool
17  Path    {System.Windows.PropertyPath}   System.Windows.PropertyPath
18  RelativeSource  null    System.Windows.Data.RelativeSource
19  Source  null    object
20  SourceReference null    MS.Internal.Data.ObjectRef
21  StringFormat    null    string
22  TargetNullValue {{DependencyProperty.UnsetValue}}   object {MS.Internal.NamedObject}
23  TransfersDefaultValue   true    bool
24  TreeContextIsRequired   false   bool
25  UpdateSourceExceptionFilter null    System.Windows.Data.UpdateSourceExceptionFilterCallback
26  UpdateSourceTrigger PropertyChanged System.Windows.Data.UpdateSourceTrigger
27  ValidatesOnDataErrors   false   bool
28  ValidatesOnExceptions   false   bool
29  ValidatesOnNotifyDataErrors true    bool
30  ValidatesOnNotifyDataErrorsInternal true    bool
31  ValidationRules Count = 0   System.Collections.ObjectModel.Collection<System.Windows.Controls.ValidationRule> {MS.Internal.Controls.ValidationRuleCollection}
32  ValidationRulesInternal Count = 0   System.Collections.ObjectModel.Collection<System.Windows.Controls.ValidationRule> {MS.Internal.Controls.ValidationRuleCollection}
33  XPath   null    string
34  _attachedPropertiesInPath   0   int
35  _bindsDirectlyToSource  false   bool
36  _doesNotTransferDefaultValue    false   bool
37  _flags  PropDefault | ValidatesOnNotifyDataErrors   System.Windows.Data.BindingBase.BindingFlags
38  _isAsync    false   bool
39  _isSealed   true    bool
40  _ppath  {System.Windows.PropertyPath}   System.Windows.PropertyPath
41  _source {MS.Internal.Data.ExplicitObjectRef}    MS.Internal.Data.ObjectRef {MS.Internal.Data.ExplicitObjectRef}
42  _sourceInUse    None    System.Windows.Data.Binding.SourceProperties
43  _values {MS.Internal.UncommonValueTable}    MS.Internal.UncommonValueTable
44  Static members      

如何解决这个问题,这样我就不必在 WPF 中显式设置 "UpdateSourceTrigger=PropertyChanged"。

@更新 2017-06-03

我已将 link (click) 附加到示例项目以说明该问题。请用第 #35-#39 行打开 MainWindow.xaml 和 fiddle。 #36 行表示不工作行为,#39 行表示工作行为。

第 36 行

<local:RatingControl StarsCount="5" RatingValue="{Binding Rating, PresentationTraceSources.TraceLevel=High}" IsReadonly="False" HorizontalContentAlignment="Center" />

第 39 行

<local:RatingControl StarsCount="5" RatingValue="{Binding Rating, UpdateSourceTrigger=PropertyChanged, PresentationTraceSources.TraceLevel=High}" IsReadonly="False" HorizontalContentAlignment="Center" />

@Edit - 请求 XAML 代码

<UserControl x:Class="so_44248130.RatingControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="24" d:DesignWidth="240">
    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        <BitmapImage x:Key="StarEmpty" UriSource="pack://application:,,,/so_44248130;component/star_black_empty.png" CacheOption="OnLoad" CreateOptions="IgnoreImageCache" />
        <BitmapImage x:Key="StarFull" UriSource="pack://application:,,,/so_44248130;component/star_black_full.png" />
    </UserControl.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding StarsList, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Button Grid.Row="0" Grid.Column="0" Width="24" Height="24" MaxHeight="24" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Tag="{Binding}" Padding="0" HorizontalContentAlignment="Center" Click="ButtonBase_OnClick">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="24" />
                                </Grid.ColumnDefinitions>

                                <Rectangle Grid.Row="0" Grid.Column="0" Width="{Binding EmptyStarWidth}" Height="24" HorizontalAlignment="Left">
                                    <Rectangle.Fill>
                                        <ImageBrush ImageSource="{StaticResource StarEmpty}"
                                        TileMode="Tile"
                                        Stretch="Uniform"
                                        AlignmentY="Top"
                                        Viewport="0,0,24,3000"
                                        ViewportUnits="Absolute" />
                                    </Rectangle.Fill>
                                </Rectangle>
                                <Rectangle Grid.Row="0" Grid.Column="0" Width="{Binding FullStarWidth}" Height="24" HorizontalAlignment="Left">
                                    <Rectangle.Fill>
                                        <ImageBrush ImageSource="{StaticResource StarFull}"
                                        TileMode="Tile"
                                        Stretch="Uniform"
                                        AlignmentY="Top"
                                        Viewport="0,0,24,3000"
                                        ViewportUnits="Absolute" />
                                    </Rectangle.Fill>
                                </Rectangle>
                            </Grid>
                        </Button>
                        <Rectangle Grid.Row="0" Grid.Column="0" Height="24" Width="24" Visibility="{Binding IsReadonly, Converter={StaticResource BooleanToVisibilityConverter}}" Fill="#02FFFFFF" />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</UserControl>

使用FrameworkPropertyMetadata有什么具体原因吗?

如果您只是备份 属性 然后尝试使用 PropertyMetadata.

参考here了解更多详情。

我已经弄明白了...部分...

在问题链接的示例项目中,应该导航到 RatingControl.xaml.cs 文件,行 #206-#228 (click)。这是 ButtonBase_OnClick 方法。在 if (star.Value ==~ 部分下,我添加了绑定力 UpdateSource。请参阅下面的代码。

if (star.Value == RatingValue)
{
    RatingValue = 0;
}
else
{
    RatingValue = star.Value;
}

BindingExpression binding = GetBindingExpression(RatingValueProperty);
if (binding != null)
{
    binding.UpdateSource();
}

这会强制更新 BindingSource 并解决问题(这可能是某种解决方法.. 但也许它会让某人朝着正确的方向前进 post 真正有效的答案)。

注:

在调试时我检查了接收到的 BindingExpression 和名为 IsUpdateOnPropertyChanged 的 属性 由于某种原因被设置为 false。我相信它应该设置为 true 但是我缺乏关于 WPF 内部的知识 post 它作为答案,因此 "note" 区域。