Search code examples
c#xamluwpwinrt-xamluwp-xaml

XAML: Property on DependecyObject not updating when it is an item in an attached collection


I have a DependencyObject that's inside an attached dependency property (that's a collection). Binding to that object does not work for some reason.

In my example, I am binding two things, a basic attached property (local:CollHolder.BasicProperty) and a regular dependent property (local:MyItem.MyData) - both are bound to the Text of a TextBox control. The XAML looks like this:

<ListView ItemsSource="{x:Bind Items}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="x:String">
            <StackPanel x:Name="stack" local:CollHolder.BasicProperty="{Binding ElementName=text, Path=Text}" VerticalAlignment="Center" HorizontalAlignment="Center" >
                <TextBox Text="" x:Name="text"/>
                <local:CollHolder.Coll>
                    <local:MyItem MyData="{Binding ElementName=text, Path=Text}"/>
                </local:CollHolder.Coll>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

When changes happen the Text property, they propagate to the attached property, but not to the dependency property.

CollHolder:

public class CollHolder : DependencyObject
{
    public static readonly DependencyProperty BasicPropertyProperty =
        DependencyProperty.RegisterAttached("BasicProperty", typeof(string), typeof(CollHolder), new PropertyMetadata("", DPC));
    public static readonly DependencyProperty CollProperty =
        DependencyProperty.RegisterAttached("Coll", typeof(Coll), typeof(CollHolder), new PropertyMetadata(null));
    public static Coll GetColl(DependencyObject obj)
    {
        var coll = (Coll)obj.GetValue(CollProperty);
        if (coll == null)
        {
            obj.SetValue(CollProperty, coll = new Coll());
        }

        return coll;
    }

    public static void SetColl(DependencyObject obj, Coll value)
    {
        obj.SetValue(CollProperty, value);
    }


    public static string GetBasicProperty(DependencyObject obj)
    {
        return (string)obj.GetValue(BasicPropertyProperty);
    }

    public static void SetBasicProperty(DependencyObject obj, string value)
    {
        obj.SetValue(BasicPropertyProperty, value);
    }


    private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine("Basic Property changed");
    }
}

MyItem:

public class MyItem : DependencyObject
{
    public string MyData
    {
        get { return (string)GetValue(MyDataProperty); }
        set { SetValue(MyDataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyData.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MyDataProperty =
        DependencyProperty.Register("MyData", typeof(string), typeof(MyItem), new PropertyMetadata("", DPC));



    private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine("CHANGED!!");
    }
}

And the collection is pretty simple:

public class Coll : List<MyItem>
{
}

Solution

  • When changes happen the Text property, they propagate to the attached property, but not to the dependency property.

    <TextBox Text="" x:Name="text"/>
    <local:CollHolder.Coll>
      <local:MyItem MyData="{Binding ElementName=text, Path=Text}"/>
    </local:CollHolder.Coll>
    

    The TextBox and local:MyItem in the same DataContext, you could use {x:Bind } to get the text value directly.

    <TextBox Text="{x:Bind }" x:Name="text" />
    <local:CollHolder.Coll>
        <local:MyItem  MyData="{x:Bind }" />
    </local:CollHolder.Coll>
    

    Update

    I tried to replace MyItem with Control to test whether DependencyProperty works in Binding mode. It works as expected. So, you could use Control as MyItem's base class.

    public class MyItem : Control
     {
         public string MyData
         {
             get { return (string)GetValue(MyDataProperty); }
             set { SetValue(MyDataProperty, value); }
         }
    
         // Using a DependencyProperty as the backing store for MyData.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty MyDataProperty =
             DependencyProperty.Register("MyData", typeof(string), typeof(MyItem), new PropertyMetadata("", DPC));
    
         private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
             Debug.WriteLine("CHANGED!!");
         }
     }