asusralis
Topic Author
Posts: 152
Joined: 30 Jul 2018, 05:03

ContentProperty and DataContext.

02 Sep 2022, 02:23

I have a custom control like this:
[ContentProperty("Tiers")]
    public class TieredProgressBar : Control
    {
        public static readonly DependencyProperty TiersProperty = DependencyProperty.Register("Tiers", typeof(ObservableCollection<Tier>), typeof(TieredProgressBar));

        public ObservableCollection<Tier> Tiers
        {
            get => (ObservableCollection<Tier>)GetValue(TiersProperty);
            set => SetValue(TiersProperty, value);
        }
    }
And I have a framework object like this:
 public class Tier : FrameworkElement
    {
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(float), typeof(Tier));

        public float Value
        {
            get => (float)GetValue(ValueProperty);
            set => SetValue(ValueProperty, value);
        }
    }
And I set tiers like this:
<TieredProgressBar Maximum="10">
    <salt:Tier Value="{Binding TotalValue}" />
    <salt:Tier Value="2" />
<TieredProgressBar>
How do I see the Tiers to the TieredProgressBar's DataContext? I tried doing this:
tier.DataContext = DataContext;
but the Value bindings still fail. Is there a way to have them set automatically to the TieredProgressBar? Thanks!
 
User avatar
maherne
Site Admin
Posts: 53
Joined: 01 Jul 2022, 10:10

Re: ContentProperty and DataContext.

02 Sep 2022, 11:24

In order to using bindings in dependency properties, certain conditions must be met. The object must be Freezable (and if it is a collection of objects then the collection must be a FreezableCollection), this allows it to be thread safe. The object must also be part of the logical tree, to allow for dependency values (like DataContext) to be inherited.

To meet the first condition, Freezable, you should have the Tier class inherit from Animatable instead of FrameworkElement. You'll also need the Tiers dependency property to be a FreezableCollection.
public class Tier : Animatable
{
	public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(float), typeof(Tier));

	public float Value
	{
		get => (float)GetValue(ValueProperty);
		set => SetValue(ValueProperty, value);
	}
}


public class TierCollection : FreezableCollection<Tier> { }

[ContentProperty("Tiers")]
public class TieredProgressBar : Control
{
	public static readonly DependencyProperty TiersProperty = DependencyProperty.Register("Tiers", typeof(TierCollection), typeof(TieredProgressBar));

	public TierCollection Tiers
	{
		get => (TierCollection)GetValue(TiersProperty);
		set => SetValue(TiersProperty, value);
	}
}
For the second requirement, adding the object to the logical tree, this requires the FrameworkElement.AddLogicalChild method, which is not currently exposed in our C# SDK. Adding this functionality is in our roadmap.

What this means is that for now you need to bind the Tier's Value property to a a named element, since they do not inherit the DataContext from the logical tree.
<TieredProgressBar x:Name="TieredProgress" Maximum="10">
    <salt:Tier Value="{Binding DataContext.TotalValue, ElementName=TieredProgress}" />
    <salt:Tier Value="2" />
<TieredProgressBar>
Let me know if you have any questions.
 
asusralis
Topic Author
Posts: 152
Joined: 30 Jul 2018, 05:03

Re: ContentProperty and DataContext.

02 Sep 2022, 12:57

Thanks for the info. Which type of FreezeableCollection should I use? It has a protected constructor.

Edit: Oh, I see each type has its own type. I created one for my Tiers and it works, thanks!
 
asusralis
Topic Author
Posts: 152
Joined: 30 Jul 2018, 05:03

Re: ContentProperty and DataContext.

02 Sep 2022, 13:49

Hmm, I am running into an issue where the Tier's Value property isn't being notified of any change. The initial value is being set, but nothing after that.
public float Value
        {
            get => (float)GetValue(ValueProperty);
            set
            {
                SetValue(ValueProperty, value); //Breakpoint

                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
            }
        }
 
<salt:Tier Value="{Binding ElementName=tiered, Path=DataContext.BaseValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <salt:Tier.Brush>
        <SolidColorBrush Color="Green" />
    </salt:Tier.Brush>
</salt:Tier>
           
public int BaseValue
    {
        get => _baseValue;
        set => Set(ref _baseValue, value);
     }
If I bind to BaseValue outside of the Tier object, the notification works properly.
 
User avatar
maherne
Site Admin
Posts: 53
Joined: 01 Jul 2022, 10:10

Re: ContentProperty and DataContext.

02 Sep 2022, 19:52

I have created an example project to test the bindings, I am not experiencing the same issue. You can download this project here, to check your code against it.

I noticed that you have a PropertyChanged invocation inside of your Value property setting. Dependency properties have property changed functionality built in, this invocation is not needed.

Who is online

Users browsing this forum: Google [Bot] and 7 guests