0

I have a custom UserControl which is basically just a fancy container that has a title and some contents:

My Custom <code>UserControl</code> as can be seen when code is running And this is how I use it in my XAML:

<local:MDCard Header="Some Title">
<Grid>
...
...
</Grid>
</local:MDCard>

The problem is that in design time (before running the code) this is what I see in the graphical XAML editor:

My Custom <code>UserControl</code> as can be seen before running the code

Notice that the title is not showing.

And this is the source code of my UserControl:

<UserControl x:Class="HealthAndWellBeing.MDCard"
         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:HealthAndWellBeing"
         mc:Ignorable="d" 
         d:DesignHeight="300"
         d:DesignWidth="300"
         x:Name="self">
<UserControl.Template>
    <ControlTemplate TargetType="{x:Type UserControl}">
        <Grid>
            <Grid>
                <Grid.Effect>
                    <DropShadowEffect Direction="270" ShadowDepth="1" Opacity="0.2" BlurRadius="2"/>
                </Grid.Effect>
                <Grid>
                    <Grid.Effect>
                        <DropShadowEffect Direction="270" ShadowDepth="0" Opacity="0.12" BlurRadius="8"/>
                    </Grid.Effect>
                    <Grid>
                        <Grid.Effect>
                            <DropShadowEffect Direction="270" ShadowDepth="1" Opacity="0.14"/>
                        </Grid.Effect>
                        <Border Background="#FFFAFAFA" CornerRadius="2"/>
                    </Grid>
                </Grid>
            </Grid>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="#19000000">
                    <Label FontWeight="Bold" Foreground="#FF616161" Margin="10,10,10,10">
                        <ContentPresenter Content="{Binding Header}"/>
                    </Label>
                </Border>
                <ContentPresenter Grid.Row="1"/>
            </Grid>
        </Grid>
    </ControlTemplate>
</UserControl.Template>

And this is my code-behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace HealthAndWellBeing
{
    /// <summary>
    /// Interaction logic for MDCard.xaml
    /// </summary>
    public partial class MDCard : UserControl, INotifyPropertyChanged
    {
        public MDCard()
        {
            InitializeComponent();
            DataContext = this;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyChange(string PropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }
        public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(MDCard), new PropertyMetadata("Title"));
    }
}

So Why is it that when I change the text of a Button it can be seen in the graphical XAML editor instantly, but changing the Header of my custom UserControl can only be seen at run time?

Amir
  • 1,885
  • 3
  • 19
  • 36
  • Is the [Label](https://stackoverflow.com/a/5386878/3766034) displaying your Header-property properly updated after a rebuild? – Jirajha Dec 18 '17 at 14:57
  • no, it's just working during the run time – Amir Dec 18 '17 at 14:57
  • I think the main issue is that changing the property in design time, does not fire the PropertyChanged event – Amir Dec 18 '17 at 14:59
  • Does changing the Binding-Signature to "{Binding Path=Header, RelativeSource={RelativeSource TemplatedParent}}" have any effect? – Jirajha Dec 18 '17 at 14:59
  • @Jirajha Yes, it worked perfectly. Thank you. Can you explain your answer so I understand what happened? – Amir Dec 18 '17 at 15:01
  • 2
    `DataContext = this;` - don't do this in UserControl! you break DataContext DP inheritance. doing so fixes short-term problem (Header of UserControl can be seen at run time), but creates many more in the long run. setup bindings properly using RelativeSource – ASh Dec 18 '17 at 15:11
  • @ASh so what should I do instead? – Amir Dec 18 '17 at 15:13
  • @Amir, remove `DataContext = this;` and use bindings with RelativeSource. Also: `INotifyPropertyChanged` is usually implemented in viewmodels to notify about changes; UserControls don't need to implement INPC because they use DependencyProperties which have built-in notification mechanism – ASh Dec 18 '17 at 15:16

1 Answers1

0

In essence, changing the Binding-Signature of your Label from

<Label FontWeight="Bold" Foreground="#FF616161" Margin="10,10,10,10">
   <ContentPresenter Content="{Binding Header}"/>
</Label>

to

<Label FontWeight="Bold" Foreground="#FF616161" Margin="10,10,10,10">
   <ContentPresenter Content="{Binding Header, RelativeSource={RelativeSource TemplatedParent}}"/>
</Label>

should / could solve the issue.

After all you are not trying to resolve to a property Header of the ControlTemplate you are giving to a UserControl, but to a Descendant of a UserControl class that has a DependencyProperty called Header. This is why you should bind to the TemplatedParent, i.e. the actual UserControl descendant.

In the case of a CustomControl you use the compile-time binding TemplateBinding, since you are templating this specific class.

With UserControls however you use Bindings, which are resolved at runtime via System.Reflection. That's why you are supposed to tell it to look for a specific parent if it is not a property of that specific class. In this case the TemplatedParent.

A better explanation between Binding vs TemplatedBinding however can be found here.

I can only assume that the "real" WPF runtime libraries will simply help you resolve the actual DependencyProperty - the blend (VS designer) ones won't.

Jirajha
  • 483
  • 3
  • 10