DEV Community

Cover image for A simple guide on WPF RelativeSources 2022 | iFour Technolab
Harshal Suthar
Harshal Suthar

Posted on • Originally published at ifourtechnolab.com

A simple guide on WPF RelativeSources 2022 | iFour Technolab

WPF RelativeSource is a Markup Extension that assists us in binding data of an element with another source element using its relationship. It states about the source position that where it falls relative to a given element.

It can be used with the Bindings property of any object to another object’s property or its relative parent or when we bind them to a dependency property and lastly, the distinctive series of the bounded data. It Gets or Sets the binding property of the source by specifying the location relative to the position of the target element type. Its default value is NULL.

{ and } (curly braces) are used to write all the markup extension codes in XAML. It is the syntax for the markup extension attributes. And through this only, the XAML processor diagnoses that a markup extension must have to process the particular attribute

RelativeSource Modes

Self Mode

This mode is used when an object binds its property to its own other property or its parent. It means that we can use this mode when the source element is similar to the target property.


<window height="300" mc:ignorable="d" title="Self Property" width="300" windowstartuplocation="CenterScreen" x:class="RelativeSrc.SelfProp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:RelativeSrc" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <border borderbrush="Black" borderthickness="5" cornerradius="5">
        <grid background="DimGray">
            <textblock fontsize="30" foreground="Black" horizontalalignment="Center" margin="0 
 20" text="Self Property">
            <ellipse height="100" stroke="Cornsilk" strokethickness="4" width="{Binding 
      RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}">
                <ellipse.fill>
                    <lineargradientbrush endpoint="1,1" startpoint="0,0">
                        <gradientstop color="Cornsilk" offset="0">
                        <gradientstop color="Sienna" offset="0.5">
                        <gradientstop color="Goldenrod" offset="0.7">
                        <gradientstop color="PaleGoldenrod" offset="1">
                    </gradientstop></gradientstop></gradientstop></gradientstop></lineargradientbrush>
                </ellipse.fill>
            </ellipse>
        </textblock></grid>
    </border>
</window>  

Enter fullscreen mode Exit fullscreen mode

Image description

This property is used to link the property of the given element to one of its direct parents in all elements as it has the parent property defined inside it. Here, the height of the window is the parent for the width of the Ellipse.

FindAncestor Mode

This mode is similar to the self-mode. The only difference is, here we can choose the ancestor or parent of the element to which the actual property is defined. It has two properties to give the value i.e. ancestor type and ancestor rank.


<window height="250" mc:ignorable="d" title="FindAncestor Property" width="400" windowstartuplocation="CenterScreen" x:class="RelativeSrc.AncestorProp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:RelativeSrc" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <canvas name="Parent_0">
        <border borderbrush="Aquamarine" borderthickness="15" height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}" name="Parent_1" width="{Binding RelativeSource={RelativeSource Self}, 
Path=Parent.ActualWidth}">
            <canvas name="Parent_2">
                <border borderbrush="MediumVioletRed" borderthickness="10" height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}" name="Parent_3" width="{Binding RelativeSource={RelativeSource Self}, 
      Path=Parent.ActualWidth}">
                    <canvas name="Parent_4">



       <border borderbrush="Pink" borderthickness="10" height="{Binding RelativeSource={RelativeSource Self}, 
                 Path=Parent.ActualHeight}" name="Parent_5" width="{Binding RelativeSource={RelativeSource Self}, 
              Path=Parent.ActualWidth}">
                            <canvas name="Parent_6">
                                <textblock background="LightSkyBlue" fontsize="17" margin="4 5" padding="15 10" text="Displaying the Name of the Ancestor">
                                <textblock fontsize="22" margin="110 70" text="{Binding 
                      RelativeSource={RelativeSource FindAncestor,
                                   AncestorType={x:Type Canvas}, AncestorLevel=3},Path=Name}" width="90">
                            </textblock></textblock></canvas>
                        </border>
                    </canvas>
                </border>
            </canvas>
        </border>
    </canvas>
</window>

Enter fullscreen mode Exit fullscreen mode

Image description

Read More: What Is New In Xaml Developer Tools In Visual Studio 2019 For Wpf & Uwp?

TemplatedParent Mode

To link any control template property with the other control to which the control template is applied. At the time, when we have to apply the property to the ControlTemplate of any control, then we can use this method.


<window height="200" mc:ignorable="d" title="TemplatedParentProp" width="400" windowstartuplocation="CenterScreen" x:class="RelativeSrc.TemplatedParentProp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:RelativeSrc" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <stackpanel background="Lavender"><button background="Transparent" content="This is Button of Parent Control" height="20" margin="20" name="btn" width="200">
            <button.template>
                <controltemplate targettype="Button">
                    </controltemplate></button.template></button><button background="{Binding RelativeSource={RelativeSource TemplatedParent} , 
          Path=Background}" click="btn_Click" content="{TemplateBinding Content}"></button>
        <textblock background="LightGray" margin="100 50" padding="10" text="{Binding 
ElementName=btn,Path=Content}">
    </textblock></stackpanel>
</window>

Enter fullscreen mode Exit fullscreen mode

Image description

There is a similar property available called TemplateBinding evaluated at the compile time. It is the short-hand of the TemplatedParent which runs first at the time of execution.

PreviousData Mode

This mode is very puzzling and the less used mode out of all modes of RelativeSource modes. It is used for discrete cases. The agenda of this method is to link the value of any control to the new one. For example, if we have to use the value of textbox in any other textbox or text block, we can use PreviousData mode. And from this, we get that we have to use this mode with the item controls.

Example:

Firstly, create the ItemsControl using the custom collection.


class Items : ObservableCollection<item>
{
        public Items()
        {
            Add(new Item { Value = 110.30 });
            Add(new Item { Value = 200 });
            Add(new Item { Value = 70.89 });
            Add(new Item { Value = 123.45 });
            Add(new Item { Value = 222.00 });
            Add(new Item { Value = 50.50 });
        }
}
</item>

Enter fullscreen mode Exit fullscreen mode

Planning to Hire WPF App Development Company? Your Search ends here.

In this, we use ObservableCollection of Item type. It has only one value of type double.

Now, we create the class for Item.

namespace RelativeSrc
{
    class Item : INotifyPropertyChanged
    {
        private double _value;

        public double Value
        {
            get { return _value; }
            set { _value = value; OnPropertyChanged("Value"); }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string PropertyName)
        {
            if (null != PropertyChanged)
            {
                PropertyChanged(this,
                     new PropertyChangedEventArgs(PropertyName));
            }
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Now, for binding the ItemsControl to the collection of data, we set constructor level collection of DataContext property to the whole Document


public partial class PreviousDataProp: Window
{
   public PreviousDataProp()
   {
      InitializeComponent();
      this.DataContext = new Items();
    }
}        

Enter fullscreen mode Exit fullscreen mode

Now by adding the ItemsControl with the Binding property and we can observe bit improvement with the actual view of the ItemsControl.


<window height="400" mc:ignorable="d" title="PreviousDataProp" width="700" x:class="RelativeSrc.PreviousDataProp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:RelativeSrc" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <grid>
        <itemscontrol itemssource="{Binding}" margin="10 30">
            <itemscontrol.rendertransform>
                <transformgroup>
                    <scaletransform scaley="-1">
                    <translatetransform y="300">
                </translatetransform></scaletransform></transformgroup>
            </itemscontrol.rendertransform>
            <itemscontrol.itemspanel>
                <itemspaneltemplate>
                    <stackpanel orientation="Horizontal">
                </stackpanel></itemspaneltemplate>
            </itemscontrol.itemspanel>
            <itemscontrol.itemtemplate>
                <datatemplate>
                    <stackpanel>
                        <border background="LightSeaGreen" borderbrush="CornflowerBlue" borderthickness="3" cornerradius="3" height="{Binding 
                            Value}" margin="0,0,35,0" width="80">
                            <textblock fontweight="bold" foreground="Yellow" horizontalalignment="Center" text="{Binding Value}" verticalalignment="Center">
                                <textblock.rendertransform>
                                    <transformgroup>
                                        <scaletransform scaley="-1">
                                    </scaletransform></transformgroup>
                                </textblock.rendertransform>
                            </textblock>
                        </border>
                    </stackpanel>
                </datatemplate>
            </itemscontrol.itemtemplate>
        </itemscontrol>
    </grid>
</window>

Enter fullscreen mode Exit fullscreen mode

This code only shows the actual present data specified in the Items collection. To get the previous data of the collection we have to add one textblock having the PreviousData property. And in this textblock, we add the value of previous border value to the items list control and through this, we get the actual output as visualized.


<textblock fontsize="14" fontweight="bold" margin="20" text="{Binding 
  RelativeSource={RelativeSource PreviousData}, Path=Value}">
             <textblock.rendertransform>
                      <scaletransform scaley="-1">
</scaletransform></textblock.rendertransform>
</textblock>

Enter fullscreen mode Exit fullscreen mode

Image description

Here, we can see the value of the previous item is displayed in the new text block which we added recently.

Conclusion

As we know, RelativeSource is a Markup extension. So markup extensions are generally implemented at the time when we have to escape the values of the attribute to be the value of any other literal or handler named. However, the requirement should also be global not just to put the type converters determined properties or types.

Top comments (0)