2
votes

I've been following this answer to expose some properties of my user-control.

The problem being that the binding doesn't find the source and I don't understand how to do it properly.

XAML:

<UserControl x:Class="Project.UI.Views.ucFilterDataGrid"
         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" 
         xmlns:local="clr-namespace:Project.UI.Views"
         xmlns:watermark="clr-namespace:Project.UI.Watermark"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Margin" Value="0,0,00,30"/>
        </Style>
    </StackPanel.Resources>
    <AdornerDecorator>
        <TextBox Name="SearchTextBox">
            <watermark:WatermarkService.Watermark>
                <TextBlock Name="waterMarkText" 
                           Text="{Binding Path=WatermarkContent, 
                                  RelativeSource={RelativeSource FindAncestor,
                                  AncestorType=local:ucFilterDataGrid}}"
                           HorizontalAlignment="Center" >

                </TextBlock>
            </watermark:WatermarkService.Watermark>
        </TextBox>
    </AdornerDecorator>

    <DataGrid Name="Results">

    </DataGrid>
</StackPanel>

CS:

namespace Project.UI.Views
{
/// <summary>
/// Interaction logic for ucFilterDataGrid.xaml
/// </summary>
public partial class ucFilterDataGrid : UserControl
{
    public ucFilterDataGrid()
    {
        InitializeComponent();
    }


    public string WatermarkContent
    {
        get { return GetValue(WatermarkContentProperty).ToString(); }
        set { SetValue(WatermarkContentProperty, value); }
    }

    public static readonly DependencyProperty WatermarkContentProperty = DependencyProperty.Register("WatermarkContent", typeof(string), typeof(ucFilterDataGrid), new FrameworkPropertyMetadata(string.Empty));
}
}

Window:

<Grid>
    <local:ucFilterDataGrid Margin="301,34,31,287" WatermarkContent="MyTest"/>
</Grid>

The result will be a blank TextBlock. If I just remove it from my watermark UserControl and put it on the same level as the DataGrid, it will work is intended.

2
Some information/xaml about your Watermark UserControl might help. I don't see anything obviously wrong here.Rowbear
change the binding to Self referenced & check.Nikita Shrivastava

2 Answers

6
votes

The problem here is your TextBlock is set as a value of an attached property, here it is:

<watermark:WatermarkService.Watermark>
            <TextBlock ...>
            </TextBlock>
</watermark:WatermarkService.Watermark>

watermark:WatermarkService.Watermark is an attached property. Its value is just an object in memory and detached from the visual tree. So you cannot use Binding with RelativeSource or ElementName. You need some proxy to bridge the disconnection. The Source will be used for Binding, the code you should try is as follow:

<TextBox Name="SearchTextBox">
        <TextBox.Resources>
            <DiscreteObjectKeyFrame x:Key="proxy" 
                                    Value="{Binding Path=WatermarkContent, 
                                            RelativeSource={RelativeSource FindAncestor,
                                            AncestorType=local:ucFilterDataGrid}}"/>
        </TextBox.Resources>
        <watermark:WatermarkService.Watermark>
            <TextBlock Name="waterMarkText" 
                       Text="{Binding Value, Source={StaticResource proxy}}"
                       HorizontalAlignment="Center" >

            </TextBlock>
        </watermark:WatermarkService.Watermark>
</TextBox>
1
votes

I made something similar the other day, and if I remember correctly. You will have to derive from the INotifyPropertyChanged interface and tell the component that the property has changed whenever you update the WatermarkContent. Otherwise the xaml (view) will not know when you change the Text, and the binding wont update.

Here is what you can try out

using System.ComponentModel;

public partial class ucFilterDataGrid : UserControl, INotifyPropertyChanged
{
    public static readonly DependencyProperty WatermarkContentProperty = DependencyProperty.Register("WatermarkContent", typeof(string), typeof(ucFilterDataGrid), new FrameworkPropertyMetadata(string.Empty));
    public event PropertyChangedEventHandler PropertyChanged;

    public ucFilterDataGrid()
    {
        InitializeComponent();
    }

    public string WatermarkContent
    {
        get { GetValue(WatermarkContentProperty).ToString(); }
        set { 
             SetValue(WatermarkContentProperty, value); 
             RaisePropertyChanged();
        }
    }

    protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

I've added the INotifyPropertyChanged and raises the event each time the WatermarkContent is changed.

Hope it helped!