Search code examples
c#xamlwin-universal-apptemplate-control

Update UI in UWP Template Control when dependency property changes


I want template control that dynamically generates different shapes according to a dependency property.The control looks like this:

<Style TargetType="local:ColorShape">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:ColorShape">
                <ContentControl x:Name="shapeParent">
                </ContentControl>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

It has a depdency property for the shape: public ShapeType

ShapeType
{
    get { return (ShapeType)GetValue(ShapeTypeProperty); }
    set { SetValue(ShapeTypeProperty, value); }
}

public static readonly DependencyProperty ShapeTypeProperty =
    DependencyProperty.Register("ShapeType", typeof(ShapeType), typeof(ColorShape), new PropertyMetadata(ShapeType.Circle));

I generate the shape in the OnApplyTemplate method:

protected override void OnApplyTemplate()
{
    var shapeParent = (ContentControl)this.GetTemplateChild("shapeParent");
    var shape = GetShape(ShapeType);
    shapeParent.Content = shape;

    base.OnApplyTemplate();
}

I can DataBind the property, and it works the first time I create the control:

<Controls:ColorShape Grid.Row="1" Width="200" Height="200" Stroke="Black" ShapeType="{x:Bind ViewModel.Shape, Mode=OneWay}" StrokeThickness="5" Fill="{x:Bind ViewModel.Color, Mode=OneWay}" />

But if I modify the bound property in the ViewModel, that generates an INotifyPropertyChange notification but doesn't repaints the template control

I tried to add a callback in the dependency property of the template control, and I can see it refreshes the dependency property with the new values bound to the property (in this case ViewModel.Shape), but it doesn't refresh the UI (never calls again OnApplyTemplate). I tried to manually call ApplyTemplate method, and the event OnApplyTemplate doesn't get fired ever.


Solution

  • OnApplyTemplate is only called once, when the template is generated. It will only be called again, when you change the Template on the control.

    What you need is a PropertyChangedCallback on the DependencyProperty:

    public int MyProperty
    {
        get { return (int)GetValue(MyPropertyProperty); }
        set { SetValue(MyPropertyProperty, value); }
    }
    
    public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0, OnPropertyChanged);
    
    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // Do what you need here
    }
    

    (The part you were missing is the second parameter at new PropertyMetadata(...).