ataulm
ataulm

Reputation: 15334

How can I bind one property to another property, offset by a specific amount?

I'm trying to bind two points of a line to two Ellipses, which are inside ScatterViewItems. At the moment, I'm able to bind to ActualCenter.X and .Y for the ScatterViewItem, which works, but because the ellipse is not the only element in that ScatterViewItem (there is also a label), it's off-center, such that the line's endpoints are not the center of the ellipse.

Ellipse doesn't have an ActualCenter property.

I wanted to know whether I'm able to offset a property binding by a fixed amount, or whether there is some other kind of binding I can use such that I can work out the correct ellipse center in the container class (for the ellipse, label and scatterviewitem), and return that as the source of the binding.

Below is the code where I am setting the binding. this.avatar refers to a Line shape. node_one and node_two are the container objects, which contain a ScatterViewItem. The Node class also contains the Ellipse, the centre for which I would essentially like as the source, but I'm happy with a simple offset of the current source.

        BindingOperations.SetBinding(this.avatar, Line.X1Property, new Binding
        {
            Source = node_one.getScatterViewItem(),
            Path = new PropertyPath("ActualCenter.X")
        });
        BindingOperations.SetBinding(this.avatar, Line.Y1Property, new Binding
        {
            Source = node_one.GetNodeCenterY(),
            Path = new PropertyPath("GetNodeCenterY()")
        });

        // Bind line.(X2,Y2) to destination.ActualCenter  
        BindingOperations.SetBinding(this.avatar, Line.X2Property, new Binding
        {
            Source = node_two.getScatterViewItem(),
            Path = new PropertyPath("ActualCenter.X")
        });
        BindingOperations.SetBinding(this.avatar, Line.Y2Property, new Binding
        {
            Source = node_two.GetNodeCenterY(),
            Path = new PropertyPath("GetNodeCenterY()")
        });

I attempted to set to the Source as node_one and Path as new PropertyPath("getOffsetCenter()") which returns a double, but it didn't work. It didn't complain, but it didn't work :) Any tips are appreciated :)


Edit, trying to use IValueConverter:

[ValueConversion(typeof(Double), typeof(Double))]
public class OffsetValues : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        DateTime date = (DateTime)value;
        Double y = (Double)value;
        Double offset = (Double)parameter;
        return y + offset;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Double dblValue = (Double)value;
        Double offset = (Double)parameter;
        Double resultDblValue = dblValue - offset;
        return resultDblValue;
    }
}

The above was added as a new class, but how would I attach it to the source of my binding - the example at MSDN has a XAML based implementation but mine is all programmatic.

Upvotes: 1

Views: 1984

Answers (3)

MovGP0
MovGP0

Reputation: 7765

First create a value converter:

    [ValueConversion(typeof(double), typeof(double))]
    public sealed class OffsetValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var dblValue = (double)value;
            var offset = parameter is string pstr ? double.Parse(pstr) : (double) parameter;
            return dblValue + offset;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var dblValue = (double)value;
            var offset = (double)parameter;
            return dblValue - offset;
        }
    }

Next, create an instance in your UserControl and reference it as a Converter in your Binding with a ConverterParameter:

<ParentControl x:Name="ParentName">
    <ParentControl.Resources>
        <local:OffsetValueConverter x:Key="Offset" />
    </ParentControl.Resources>
    <ParentControl.Content>
        <ChildControl Height="{Binding 
            ElementName=ParentName,
            Path=Height,
            Converter={StaticResource Offset},
            ConverterParameter=-16}" />
    </ParentControl.Content>
</ParentControl>

Upvotes: 1

Rachel
Rachel

Reputation: 132558

It looks like you're trying to set a binding to a method call, not a property. Bindings can only reference Properties, since the dependency property the binding is set on is meant to point to the location of a property, not to a method call.

I have seen people use a Converter to convert a Method to a Property value, however have not used it myself.

As a side note, I got tired of always writing converters to offset my bindings by a set amount, so created my own Math Converter to do this. To use it, just set the ConverterParameter to a mathematical equation with @VALUE substituting for the bound value, such as (@VALUE / 2) + 20. If you want it to use more than one bound value, just change it to a MultiConverter

Upvotes: 3

Fischermaen
Fischermaen

Reputation: 12458

You need to write some ValueConverter in the code behind for that.

For further information look here.

Upvotes: 3

Related Questions