Search code examples
wpfdatatemplate

How to use PropertyChanged to pass trough DataTemplate?


Question is simple: how can I trigger a change on the dataObject without acutaly changing the dataObject, and see this change on the visual?

DataObject:

ProductData : INotifyPropertyChanged
{
    private ProductPartData myProductPartData;

    public ProductPartData ProductPartData
    {
        get
        {
            return myProductPartData;
        }
        set
        {
            if (value != myProductPartData)
            {
                myProductPartData = value;
                OnNotifyPropertyChanged("ProductPartData");
            }
        }
    }
}

DataTemplate:

 <DataTemplate
    DataType="{x:Type ProductData}"
    >
    <VisualProduct
        ProductPartData="{Binding Path=ProductPartData, Mode=OneWay}"
        />
</DataTemplate>

And now in a VM I have:

product.OnNotifyPropertyChanged("ProductPartData");

Problem: Even if the getter for ProductPart is called when I execute OnNotifyPropertyChanged, the visual is not notified, because is the same instance of the ProductPartData.

How do I trigger a change seen by the Visual without changing the instance? Thank you,


Solution

  • Daniel,

    A solution is to use UpdateTarget() method of the BindingExpression class, this way the target of the binding gets refreshed no matter what; of course, your converter will also be hit - if any. Since I'm guessing you don't have access to your visual in the Product, you could use an attached property and in its callback, you can get the BindingExpression and call UpdateTarget() on it.

    Note that I'm using a simple TextBlock as the visual of the data object.

    public class BindingHelper
    {
        public static bool GetRefreshBinding(DependencyObject obj)
        {
            return (bool) obj.GetValue(RefreshBindingProperty);
        }
    
        public static void SetRefreshBinding(DependencyObject obj, bool value)
        {
            obj.SetValue(RefreshBindingProperty, value);
        }
    
        // Using a DependencyProperty as the backing store for RefreshBinding.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RefreshBindingProperty =
            DependencyProperty.RegisterAttached("RefreshBinding", typeof(bool), typeof(BindingHelper), new UIPropertyMetadata(false, OnRefreshBindingPropertyChanged));
    
        static void OnRefreshBindingPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs ea)
        {
            TextBlock elem = o as TextBlock;
            if (elem != null)
            {
                BindingExpression bEx = elem.GetBindingExpression(TextBlock.TextProperty);
                if (bEx != null)
                {
                    bEx.UpdateTarget();
                }
            }
        }
    }
    

    Also, in your data object that you can create a new bool property(let's name it ShouldRefresh) that is bound to the attached property within the template - this will trigger the AP's property changing:

    <DataTemplate DataType="{x:Type local:ProductData}">
            <TextBlock Text="{Binding Path=Name, Converter={StaticResource BlankConverter}}"
                       local:BindingHelper.RefreshBinding="{Binding Path=ShouldRefresh}"/>
        </DataTemplate>
    

    So, this way, whenever you want to update the target through binding, you can set:

    ShouldRefresh = !ShouldRefresh
    

    in your data class.

    HTH.