Sugz
Sugz

Reputation: 107

DependencyProperty CoerceValueCallback with generic

I'm building a wpf control that use DependencyProperty with generic, for which I need (at least I think) a CoerceValueCallback to check if the value is correct. The idea is to build a base class from which I'll derive with number type.

public class MyClass<T> : Control where T : struct
{

    public T Value
    {
        get { return (T)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    // DependencyProperty as the backing store for Value
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value",
        typeof(T),
        typeof(MyClass<T>),
        new PropertyMetadata(null, null, CoerceValue)
    );


    private static object CoerceValue(DependencyObject d, object baseValue)
    {
        // Check if value is valid
        return verifiedValue;
    }
}


public class MyDerivedClass : MyClass<int>
{

}

Problem is that the CoerceValue is returning an object and I can't find how to return a generic instead.

Any idea ?

EDIT: Here is what I did thanks to the answers below

public abstract class MyClass<T> : Control where T : struct, IComparable
{

    public T MinValue { get; set; }
    public T MaxValue { get; set; }

    public T Value
    {
        get { return (T)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    // DependencyProperty as the backing store for Value
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value",
        typeof(T),
        typeof(MyClass<T>),
        new PropertyMetadata(default(T), null, CoerceValue)
    );


    private static object CoerceValue(DependencyObject d, object baseValue)
    {
        T value = (T)baseValue;
        ((MyClass<T>)d).CoerceValueToBounds(ref value);

        return value;
    }

    private void CoerceValueToBounds(ref T value)
    {
        if (value.CompareTo(MinValue) < 0)
            value = MinValue;
        else if (value.CompareTo(MaxValue) > 0)
            value = MaxValue;
    }
}

This way, I can limit the Value within MinValue and MaxValue and keeping everything with generic, thus avoiding overriding an abstract method in every derived class.

Upvotes: 0

Views: 687

Answers (2)

Clemens
Clemens

Reputation: 128097

Add an abstract CoerceValue method like this:

public abstract class MyClass<T> : Control where T : struct
{
    ...

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof(T), typeof(MyClass<T>),
        new PropertyMetadata(default(T), null, CoerceValue));

    protected abstract T CoerceValue(T value);

    private static object CoerceValue(DependencyObject d, object value)
    {
        return ((MyClass<T>)d).CoerceValue((T)value);
    }
}

public class MyDerivedClass : MyClass<int>
{
    protected override int CoerceValue(int value)
    {
        return Math.Max(100, Math.Min(200, value));
    }
}

Upvotes: 1

techvice
techvice

Reputation: 1301

While your control can utilize the generic, when using the CoerceValueCallback, you will have to maintain the delegate callback. The C# documentation specifies that delegates are sealed.

I also noticed you have an issue with your PropertyMetadata. You are passing null as the default value. Which is what caught me off guard when I previously checked for null. Here is the code that worked for me when I tested it:

public class MyClass<T> : Control where T : struct
{
    public T Value
    {
        get { return (T)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    // DependencyProperty as the backing store for Value
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value",
        typeof(T),
        typeof(MyClass<T>),
        new PropertyMetadata(default(T), null, CoerceValue) //Must pass default(T) otherwise an exception will occur.
    );

    private static object CoerceValue(DependencyObject d, object baseValue)
    {
        var tValue = (T)baseValue;
        // Check if tValue is valid 
        return tValue;
    }
}

Once you have the value returned from the CoerceValue method, you will have to cast it to T.

Upvotes: 0

Related Questions