0
votes

I have user control with dependency property. When I try bind it in windows it doesn't work. But when I change binding to normal parameter (in this situation, true) it's working. What i need to do to make my binding working? Maybe something with DataContext? Here is my code:

First user control's xaml:

<UserControl x:Class="DPTest.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="100" d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">

<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
    <TextBlock Text="{Binding Property}"/>
</Grid>
</UserControl>

Code behind:

public partial class CustomUserControl : UserControl, INotifyPropertyChanged
{
    public static readonly DependencyProperty DPTestProperty = DependencyProperty.Register("DPTest", typeof(bool), typeof(CustomUserControl), new PropertyMetadata(default(bool), propertyChangedCallback));
    public bool DPTest
    {
        get
        {
            return (bool)GetValue(DPTestProperty);
        }
        set
        {
            SetValue(DPTestProperty, value);
        }
    }
    private bool _property;
    public bool Property
    {
        get
        {
            return _property;
        }
        set
        {
            _property = value;
            RaisePropertyChanged("Property");
        }
    }
    private static void propertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as CustomUserControl;
        control.Property = (bool)e.NewValue;
    }

    public CustomUserControl()
    {
        InitializeComponent();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

In the windows where i put this user control data context is set to my view model:

<phone:PhoneApplicationPage
x:Class="DPTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:my="clr-namespace:DPTest"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
DataContext="{Binding Main, Source={StaticResource Locator}}">

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
        <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <my:CustomUserControl Name="cstom" DPTest="{Binding Property}"/>
    </Grid>

</Grid>

</phone:PhoneApplicationPage>

In the view model I have property which is binded to DPTest.

public class MainViewModel : ViewModelBase
{
    private bool _property;

    public bool Property
    {
        get { return _property; }
        set
        {
            _property = value;
            RaisePropertyChanged("Property");
        }
    }

    public MainViewModel()
    {
        Property = true;
    }
}

When I run this application Textblock have value False but this should be True and propertyChangedCallback isn't even called. What i need to do?

Thanks

2
What is this Main and Locator in {Binding Main, Source={StaticResource Locator}}?dkozl
It's instance of ViewModelLocator added with MvvmLight. It works on normal TextBlock (it has value True when i run it).user3261163
So Locator.Main is of a MainViewModel type and has Property property. And if, on the same page, you add <Checkbox IsChecked={Binding Property}/> it will be checked?dkozl
Yes, it's exactly as you wrote.user3261163
And also, as you mentioned, if you do DPTest="True" instead of binding then TextBlock shows true. Correct?dkozl

2 Answers

2
votes

Try this:

<UserControl x:Class="DPTest.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource
PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" d:DesignHeight="100"
d:DesignWidth="200"
x:Name="self">

<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
    <TextBlock Text="{Binding Property, ElementName=self}"/>
</Grid>

2
votes

You have bind UserControl DataContext to itself since you want to bind Property present in CustomUserControl to TextBlock inside it which is completely fine.

But when you use CustomUserControl in Window, it won't inherit Window's DataContext since its explicitly set on UserControl declaration. So, binding engine is trying to look for property in UserControl which it can't fine.

Note: Binding engine looks for property inside it's DataContext otherwise instructed to look somewhere else using RelativeSource, ElementName, x:Reference etc.

So you need to manually provide binding engine the property instance like this:

<my:CustomUserControl Name="cstom"
                      DPTest="{Binding Path=DataContext.Property, 
                                       ElementName=LayoutRoot}"/>

Explanation for working with DPTest="True":

Since you already manually provided the value to DP, binding engine doesn't have to worry about finding it in DataContext.