1

Not sure how much it matters, but I am using devexpress grid and defining a celltemplate for one of the columns. The issue is that when I bind with a "slidercontrol" it works, but when I bind to a UserControl of my own making, the binding doesn't work. The UserControl binds properly... I use it elsewhere in the code. It just doesn't work in this context, and I'm wondering why? Thanks.

This binding works:

<dxg:GridColumn FieldName="SetupRating" Header="Setup Rating" AllowFocus="True" AllowEditing="True">
                <dxg:GridColumn.CellTemplate>
                    <DataTemplate>
                        <Slider Value="{Binding RowData.Row.SetupRating}" Minimum="0" Maximum="3" />           
                    </DataTemplate>
                </dxg:GridColumn.CellTemplate>
</dxg:GridColumn>

This binding does not work:

<dxg:GridColumn FieldName="SetupRating" Header="Setup Rating" AllowFocus="True" AllowEditing="True">
                <dxg:GridColumn.CellTemplate>
                    <DataTemplate>
                        <wpf:RatingControl RatingValue="{Binding RowData.Row.SetupRating}" MaxRating="3" />
                    </DataTemplate>
                </dxg:GridColumn.CellTemplate>
 </dxg:GridColumn>

EDIT: PLEASE DO NOT SUGGEST THAT wpf:RatingControl IS BROKEN. IT IS A PROVEN USER CONTROL THAT IS WORKING IN MANY OTHER DATA BINDING SCENARIOS. IF THERE IS ANYTHING WRONG WITH IT, IT WOULD HAVE TO BE SOMETHING OBSCURE THAT ONLY IMPACTS THE WAY I'M BINDING WITHIN THIS DATATEMPLATE. THANK YOU.

Here's how I define the dependency property in my rating control:

public static readonly DependencyProperty RatingValueProperty =
        DependencyProperty.Register("RatingValue", typeof(int), typeof(RatingControl),
                                    new FrameworkPropertyMetadata(0,
                                                                  FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                                                  RatingValueChanged));

    public int RatingValue
    {
        get 
        { 
            return AdjustRatingValue((int)GetValue(RatingValueProperty), MaxRating); 
        }
        set
        {
            SetValue(RatingValueProperty, AdjustRatingValue(value, MaxRating));
        }
    }

    private static int AdjustRatingValue(int value, int maxValue)
    {
        if (value < 0)
            return 0;
        else if (value > maxValue)
            return maxValue;
        else
            return value;
    }
    private static void RatingValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        RatingControl parent = sender as RatingControl;
        int numberOfButtonsToHighlight = AdjustRatingValue((int)e.NewValue, parent.MaxRating);

        SelectStars(numberOfButtonsToHighlight, parent.StarValues); 
    }
Mooser
  • 97
  • 2
  • 10
  • What is the binding error you are getting... – myermian Mar 16 '12 at 16:52
  • There is no binding error. It appears to bind successfully, but then it just doesn't update the source value when I change the rating. If I were to change the binding path to say "foo.bar" then I will get a binding error #40. So, yeah, it actually does bind without error... But unlike the SliderControl, it fails to update the source. – Mooser Mar 16 '12 at 17:11
  • Perhaps you need to set the `UpdateSourceTrigger` to `PropertyChanged`? – myermian Mar 16 '12 at 17:31
  • Incredibly, it has no effect. – Mooser Mar 16 '12 at 18:21
  • try changing your dependency property to a double instead of an int? – myermian Mar 16 '12 at 18:30
  • I'm only missing the `RatingValueChanged` callback. What does that do? – Silvermind Mar 16 '12 at 18:33
  • 1
    Not sure that's it's in anyway related to your problem, but your getter and setter shouldn't do anything other than get and set the value. The thing to remember here is that the getter and setter are just syntactic sugar for DependencyProperty because the binding infrastructure doesn't use them. So you binding will certainly never call AdjustRatingValue. If you are trying to coerce a value there is a mechanism for that already within the DependencyProperty. You just need to give it a CoerceValueCallback in the constructor. – Matt Burland Mar 16 '12 at 18:55
  • 1
    @MattBurland Agree, that's why I also asked for the `RatingValueChanged` callback defined in his `FrameworkPropertyMetadata` which could just be `(UI)PropertyMetadata` btw. :). – Silvermind Mar 16 '12 at 19:06
  • @MattBurland, Thanks that's a good point. And Silvermind, I popped the event handler in there for you. – Mooser Mar 16 '12 at 19:18
  • I'm now using CoerceValue and UIPropertyMetadata and as was anticipated, it doesn't actually fix the issue I'm having. – Mooser Mar 16 '12 at 19:37
  • I'm having a similar issue at the moment: 4 UserControls, 3 in which the binding work and one in which it does not. They are exact copies of one other (with the exception of the class name of course) as I tried to locate the problem... Nothing seems to work unfortunately. – Knots Jun 25 '13 at 15:09

2 Answers2

2

You have to use RelativeSource to find the parent element, something like this

<dxg:GridColumn FieldName="SetupRating" Header="Setup Rating" AllowFocus="True" AllowEditing="True">
                <dxg:GridColumn.CellTemplate>
                    <DataTemplate>
                        <wpf:RatingControl RatingValue="{Binding DataContext.RowData.Row.SetupRating, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dxg:GridColumn}}}" MaxRating="3" />
                    </DataTemplate>
                </dxg:GridColumn.CellTemplate>
 </dxg:GridColumn>

More about how to use RelativeSource

Community
  • 1
  • 1
Andrei Schneider
  • 3,618
  • 1
  • 33
  • 41
  • Thanks, I have tried this exactly and it does not work. – Mooser Mar 16 '12 at 17:01
  • On a secondary note, do you have any reading material on why relative source is necessary on a UserControl but not on a Control in this context? (Because as you can see, the SliderControl has no issue binding without the RelativeSource attribute) – Mooser Mar 16 '12 at 17:02
  • I couldn't discern any difference to your code besides adding dxg: which is what I've already done. Anyways, here's the error output: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='DevExpress.Xpf.Grid.GridColumn', AncestorLevel='1''. BindingExpression:Path=DataContext.RowData.Row.SetupRating; DataItem=null; target element is 'RatingControl' (Name=''); target property is 'RatingValue' (type 'Int32') – Mooser Mar 16 '12 at 17:54
  • I see `RatingValue="{Binding DataContext.RowData...` I don't see the DataContext prefix in the askers code, should'nt it just start like this: `RatingValue="{Binding RowData...` – Silvermind Mar 16 '12 at 18:55
  • I tried them both, no dice. Also, looking at the error you can see that it is not a missing property error. (Error #44), it is Error #4 which means it cannot find the source for the binding. – Mooser Mar 16 '12 at 19:23
1

Based on your last update, I'm guessing it's because the Slider defaults to two way binding and your user control doesn't. Try adding Mode=TwoWay to your binding:

<wpf:RatingControl RatingValue="{Binding RowData.Row.SetupRating, Mode=TwoWay}" MaxRating="3" />
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • Nice try, thanks. My RatingControl binds TwoWay by default (I set this in the dependency property metadata). – Mooser Mar 16 '12 at 17:49
  • @user1156915: You absolutely positive about that? Because that would lead to exactly the symptoms you describe. But since you are so insistent that nothing can possibly be wrong with your control... – Matt Burland Mar 16 '12 at 18:00
  • I'll post the source of my RatingControl. Naturally, I have also already tried "mode=twoway" in XAML because I'm absolutely desperate. – Mooser Mar 16 '12 at 18:23