-1

@update - See update below, please

I've got the UserControl that is used inside DataGridTemplateColumn. When I specify the binding like this {Binding MyProperty, UpdateSourceTrigger=PropertyChanged} it updates the source. However then I ommit UpdateSourceTrigger it doesn't update the source.

The DependencyProperty is created like this:

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

As you can see below, the Binding is created with Default flag when the UpdateSourceTrigger is omitted, and with PropertyChanged flag if not.

The "not working" binding (see line 26 on both examples):

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      

The "working" binding:

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      

How to solve this so I don't have to set "UpdateSourceTrigger=PropertyChanged" explicitly in WPF.

@update 2017-06-03

I have attached link (click) to the example project to illustrate the issue. Please open MainWindow.xaml and fiddle with lines #35-#39. The line #36 presents the not-working behaviour, the line #39 presents the working behaviour.

Line #36

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

Line #39

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

@Edit - Requested XAML code

<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>
user2475983
  • 1,916
  • 2
  • 13
  • 20
  • 1
    The value of the `DefaultUpdateSourceTrigger` property of the FrameworkPropertyMetadata class is `PropertyChanged` anyway, so you could use a FrameworkPropertyMetadata constructor with less parameters. That said, all dependency properties with `BindsTwoWayByDefault` that I've ever created do update their source on `PropertyChanged` without problems, so it's unclear why yours doesn't. – Clemens May 29 '17 at 18:34
  • @Clemens I've edited my question (added link to example project), please see for yourself – user2475983 Jun 03 '17 at 12:56
  • Can we see RatingControl's XAML? – 15ee8f99-57ff-4f92-890c-b56153 Jun 24 '17 at 16:44
  • @EdPlunkett There's link to example project at the bottom of the question which contains RatingControl's XAML and CS. - https://github.com/tariel36/so_44248130/blob/master/so_44248130/RatingControl.xaml – user2475983 Jun 24 '17 at 16:59
  • Please add it to the question. If it's on an external site, it'll be a dead link sooner or later, and in any case you can't ask people to dig through a github project to find something. – 15ee8f99-57ff-4f92-890c-b56153 Jun 24 '17 at 17:17

2 Answers2

0

I've figured it out... partially...

In the example project linked in question one should navigate to RatingControl.xaml.cs file, lines #206-#228 (click). This is the ButtonBase_OnClick method. Just under if (star.Value ==~ part I've added the force UpdateSource of binding. See code below.

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

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

which forces the update of BindingSource and resolves the issue (it's some kind of workaround probably.. but maybe it will put someone in the right direction to post the real valid answer).

Note:

While debugging I've inspected the received BindingExpression and the property named IsUpdateOnPropertyChanged is set to false for some reason. I belive it should be set to true however I lack the knowledge about WPF internals to post it as answer, hence the "note" region.

user2475983
  • 1,916
  • 2
  • 13
  • 20
-1

Is there any specific reason for using FrameworkPropertyMetadata?

if you are just backing up a property then try using PropertyMetadata.

Refer here for more details.

Anurag
  • 36
  • 5
  • No specific reason. I've changed `FrameworkPropertyMetadata` to `PropertyMetadata` however the only result is that I have to specify `Mode=TwoWay` in XAML's `Binding` explicitly, so.. didn't really help :(. – user2475983 Jun 05 '17 at 17:31
  • I got the issue its because it not able to find the source, try `` – Anurag Jun 16 '17 at 19:44
  • Yes it seems to help however it looks like another workaround, could you elaborate why it's not able to find the source even if `DataContext` is inherited by UC? – user2475983 Jun 17 '17 at 09:26
  • Even though the UC inherists the `DataContext`, `DataTempelete` has some issue with `DataContext`. I my experience I came across a scenario where `DataTemplete` was unable to find the Governing `FrameworkElement` i.e. its ancestor. You can google for (unable to find the Governing `FrameworkElement`) and there are tons of article lying down over internet. – Anurag Jun 17 '17 at 14:38