The blog of dlaa.me

"I would prefer even to fail with honor than to win by cheating" [Tip: For a truly read-only custom DependencyProperty in Silverlight, use a read-only CLR property instead]

Tip

For a truly read-only custom DependencyProperty in Silverlight, use a read-only CLR property instead

Explanation

My last tip outlined the best way I knew to approximate a WPF-like read-only DependencyProperty on Silverlight. (Aside: It's also a good way to implement coercion.) However, it suffers from the unavoidable problem that the actual DependencyProperty is still writable and can "twitch" if written to. The comments for that post proved to be very interesting - with two suggestions for alternate techniques. I looked into them both (and stumbled across a third myself) to understand their merits. Unfortunately, the technique I stumbled across and the one Morten Nielsen (SharpGIS) suggested both suffer from this same issue: the DependencyProperty is still writable and there are ways to sneak around the protections and write to it anyway. (Aside: Morten's solution is fiendishly clever and well worth having a look at.) However, the technique suggested by Dr. WPF doesn't have that limitation - it produces a guaranteed read-only property. The catch is that it's not actually a DependencyProperty! However, Dr. WPF points out that a read-only DependencyProperty doesn't actually need to be a DependencyProperty as long as the class implements INotifyPropertyChanged. What's great is that there's no loss of functionality with the implementation shown below, and no need to try to subvert the platform! The thing to remember is that you can't use TemplateBinding to get at it - you'll need to use Binding + RelativeSource instead. Overall, this is a very nice solution - even if it does bend the rules just a bit... :)

Good Example

public class MyControl : Control, INotifyPropertyChanged
{
    public int MyReadOnly
    {
        get { return _myReadOnly; }
        protected set
        {
            if (value != _myReadOnly)
            {
                int oldValue = _myReadOnly;
                _myReadOnly = value;
                OnMyReadOnlyChanged(oldValue, _myReadOnly);
                PropertyChangedEventHandler handler = PropertyChanged;
                if (null != handler)
                {
                    handler.Invoke(this, new PropertyChangedEventArgs("MyReadOnly"));
                }
            }
        }
    }
    private int _myReadOnly;
    protected virtual void OnMyReadOnlyChanged(int oldValue, int newValue)
    {
        // TODO: Handle property change
    }
    public event PropertyChangedEventHandler PropertyChanged;
}
<ControlTemplate TargetType="local:MyControl">
    <ContentControl Content="{Binding MyReadOnly, RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>

More information