如何使用PropertyChanged传递DataTemplate?

时间:2011-05-04 15:41:47

标签: wpf datatemplate

问题很简单:如何在不修改dataObject的情况下触发dataObject的更改,并在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>

现在我有一个VM:

product.OnNotifyPropertyChanged("ProductPartData");

问题:  即使在执行OnNotifyPropertyChanged时调用ProductPart的getter,也不会通知visual,因为它与ProductPartData的实例相同。

如何在不更改实例的情况下触发Visual看到的更改?  谢谢,

5 个答案:

答案 0 :(得分:3)

丹尼尔,

一个解决方案是使用BindingExpression类的UpdateTarget()方法,这样无论如何都会刷新绑定的目标;当然,你的转换器也会被击中 - 如果有的话。由于我猜你没有访问产品中的visual,你可以使用附加属性,在它的回调中,你可以得到BindingExpression并在其上调用UpdateTarget()。

请注意,我使用简单的TextBlock作为数据对象的视觉效果。

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();
            }
        }
    }
}

此外,在您的数据对象中,您可以创建一个绑定到模板中附加属性的新bool属性(让我们将其命名为ShouldRefresh) - 这将触发AP的属性更改:

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

因此,这样,只要您想通过绑定更新目标,就可以设置:

ShouldRefresh = !ShouldRefresh

在您的数据类中。

HTH。

答案 1 :(得分:1)

如果你引发一个PropertyChanged事件,并且该属性的新值等于WPF已经拥有的值,它将忽略你。你有几个选择:

“快速”方式是将属性设置为null,然后再次返回正确的值,确保每次都引发PropertyChanged事件。它很脏但每次都有效。

“正确”的方法是强制进行绑定刷新,如this post by Jaime Rodriguez中所述。因为你的视觉是数据模板化的,虽然让“dependencyObject”传入该帖子中的调用是有点棘手的。您最终可能需要使用this post by Josh Smith中讨论的模板的FindName方法。

答案 2 :(得分:1)

我们遇到了这种来自数据库并转换为DTO(数据传输对象)的数据的问题。

我们的DTO基类覆盖对象的方法,例如Equals()GetHashCode(),如下所示:

public override Boolean Equals(Object obj)
{
    // Null reference
    if (null == obj)
        return false;

    // Same reference
    if (Object.ReferenceEquals(this, obj))
        return true;

    EntityDTOBase<TEntity> entiteObj = obj as EntityDTOBase<TEntity>;
    if (null == entiteObj)
        return false;
    else
        return Equals(entiteObj);
}

public Boolean Equals(EntityDTOBase<TEntity> other)
{
    // Null reference
    if (null == other)
        return false;

    // Same reference
    if (Object.ReferenceEquals(this, other))
        return true;

    // No Id: cannot be compared, return false
    if (this.id == TypeHelper.DefaultValue<long>())
        return false;

    // Id comparison
    if (this.id != other.id)
        return false;

    return true;
}

public override Int32 GetHashCode()
{
    return this.id.GetHashCode();
}

所以问题是当我们从数据库再次加载相同的实体时,因为ID是相同的,一些绑定没有正确更新。

通过添加额外的虚拟EqualsExtended()方法来避免此特定问题,默认实现只返回true

protected virtual Boolean EqualsExtended(EntityDTOBase<TEntity> other)
{
    return true;
}

public Boolean Equals(EntityDTOBase<TEntity> other)
{
  /// Same code as before (except last line):
  return EqualsExtended(other);
}

现在在我们的DTO类的任何实现中,我们可以添加一些逻辑以使Equals()在某些情况下返回false,例如通过在从数据库中检索数据时添加时间戳:

protected override Boolean EqualsExtended(EntityDTOBase<Act> other
{
    if (this.Timestamp != other.Timestamp)
    {
        return false;
    }
    return true;
}

简而言之,解决此问题的一种方法是,只要您希望相应地更新GUI,就可以使您的类实例看起来不同。

答案 3 :(得分:0)

问题可能是您使用GuiProductPartData键入myProductPartData返回ProductPartData键入的ProductPartData?但无论如何这不应该是这样的:)

将变量名称与类型相同并不是一个好习惯,因此您不应该拥有ProductPartData ProductPartData属性。

答案 4 :(得分:0)

除了命名约定(假设只是输入错误)问题可能存在于ProductPartData类中。它是否也实现了INotifyPropertyChanged?