4

I need to know what the procedure is for making a write-only dependency-property. I can see that the DependencyProperty class does not have a special "Register" method for write-only properties, but I have no idea whether the RegisterAttached method may apply to what I am trying to do.

This property needs to be a dependency-property, rather than a simple CLR property. Internally, my class requires a PropertyChangedCallback on this property in order to remain stable.

I know that write-only dependency-properties can be created, because it is stated quite clearly in:
Pro C# 2008 and the .NET 3.5 Platform, Page 1061.
However, this is the only place where I can even find "dependency property" and "write only" on the same page. And this author apparently did not think it was necessary to actually show the reader the procedure for anything other than a basic read-write dependency-property. Of course, this book could be a load of BS - but this book looks pretty standard, so I think it's a pretty safe bet that the author is correct. I assume the lack of information on the internet stems from the fact that nobody generally needs to make a property like this.

I know it sounds very questionable to want to make your own write-only dependency-property. I assure you it makes sense where I want it. My class has a property whose value is only useful to the object setting it. If another object were to request the value of this property later, it wouldn't be able to make any rational sense out of the value without knowing the original context of the setter.

This property is not intended to be used for informational purposes. Letting outside objects attempt to use the property value this way is problematic, dangerous, and a security risk. So I believe the best design is to prohibit read operations on this property. Anyone using my class will find that they are forced to use the class the way it was intended, which will work out much better and cleaner in the end.

Giffyguy
  • 20,378
  • 34
  • 97
  • 168
  • 2
    Very interested to hear what the actual scenario is on this. You say "risky," I think "crap this will launch nukes if the setter is called", so I want to hear more! – Jeff Wilcox Aug 25 '09 at 00:56
  • You asked "how can I create" vs "do they exist", it's a little troubling that you picked a "it's not possible" answer over a "here's how to accomplish your scenario" answer. – Jeff Wilcox Aug 31 '09 at 17:23
  • He didn't pick my answer, he just hasn't been visiting the site for 2 days at least, therefore it has been auto accepted after the bounty period ended (you can see this by hovering over the check mark: auto accept yields half the bounty only). I think both of our answers contributed to the topic with me questioning the supposed 'official' way and you providing a potential workaround still. The bounty system is just not flexible enough to accommodate this right now (amongst quite some other controversial issues, see http://meta.stackexchange.com/questions/tagged/bounty). – Steffen Opel Sep 01 '09 at 09:00

4 Answers4

4

You can't, this appears to be by design. While I can understand your approach to the mentioned book and am in no way questioning its quality, I'd still presume this to be some sort of copy&paste or similar issue. Here is my reasoning:

WPF property system code

WPF property system design

  • More important, 'The current WPF implementation of its XAML processor is inherently dependency property aware. The WPF XAML processor uses property system methods for dependency properties when loading binary XAML and processing attributes that are dependency properties. This effectively bypasses the property wrappers.', see XAML Loading and Dependency Properties.
  • Most important, 'Dependency properties should generally be considered to be public properties. The nature of the Windows Presentation Foundation (WPF) property system prevents the ability to make security guarantees about a dependency property value.', see Dependency Property Security.

Especially the latter two points are outlining the design constraint, that dependency property values are always accessible via GetValue()/SetValue(), no matter whether their CLR wrappers are access restricted or available at all, with the only exception being the specifically accounted for Read-Only Dependency Properties.

Consequently, as Jeffs answer implies already, just removing the getter for example does not really prevent anyone accessing the property via GetValue(), though this may at least 'reduce the immediately exposed namespace of a custom class'. The usefulness of any such semantic workaround of making the property value somewhat less visible/accessible and the retrieved value inherently useless for clients as suggested by Jeff depends on your particular scenario of course.

Community
  • 1
  • 1
Steffen Opel
  • 63,899
  • 11
  • 192
  • 211
2

Interesting, this is definitely a rare scenario, I'd be interested to hear more in what it enables.

Would you consider the idea of providing an invalid value (such as null) for reads through binding or GetValue, while just not having a CLR getter?

Either use a private DependencyProperty to store the "real" value you care about, or just a private member variable.

In the property changed callback, always revert the value back to the original value, while storing away the new value that was set.

I spend most of my time doing Silverlight control development now, so this property works in WPF and Silverlight-land, and doesn't use coercian or anything fun like that. Maybe it gets you going on the right track, though.

    /// <summary>
    /// Sets the write-only dependency property.
    /// </summary>
    public string MyWriteOnlyDependencyProperty
    {
        set { SetValue(MyWriteOnlyDependencyPropertyProperty, value); }
    }

    private string _theRealSetValue;

    private bool _ignorePropertyChange;

    /// <summary>
    /// Identifies the MyWriteOnlyDependencyProperty dependency property.
    /// </summary>
    public static readonly DependencyProperty MyWriteOnlyDependencyPropertyProperty =
        DependencyProperty.Register(
            "MyWriteOnlyDependencyProperty",
            typeof(string),
            typeof(TemplatedControl1),
            new PropertyMetadata(null, OnMyWriteOnlyDependencyPropertyPropertyChanged));

    /// <summary>
    /// MyWriteOnlyDependencyPropertyProperty property changed handler.
    /// </summary>
    /// <param name="d">TemplatedControl1 that changed its MyWriteOnlyDependencyProperty.</param>
    /// <param name="e">Event arguments.</param>
    private static void OnMyWriteOnlyDependencyPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TemplatedControl1 source = d as TemplatedControl1;
        if (source._ignorePropertyChange)
        {
            source._ignorePropertyChange = false;
            return;
        }
        string value = e.NewValue as string;

        source._theRealSetValue = value;

        // Revert, since this should never be accessible through a read
        source._ignorePropertyChange = true;
        source.SetValue(e.Property, e.OldValue);
    }
Jeff Wilcox
  • 6,375
  • 1
  • 24
  • 31
  • I'm not out to grab rep points by making stuff up, I'd love to hear what other folks out there think first. Must be some WPF experts around! I actually own the book and am likewise confused! – Jeff Wilcox Aug 22 '09 at 07:53
  • source.SetValue(e.Property, e.OldValue); Would this line invoke the 'OnMyWriteOnlyDependencyPropertyPropertyChanged' callback as well? It kinda feels like it might make an infinite loop of sorts. But I'm not sure if that's the case. – Giffyguy Aug 28 '09 at 18:59
  • You're right : I had forgotten to add the ignorePropertyChange=true call right before SetValue. Updated my response. – Jeff Wilcox Aug 28 '09 at 19:12
  • This is a hacky way to accomplish it. Did the coercion callback not exist back in 2009? – Ben Voigt Jun 12 '14 at 17:31
  • Silverlight != WPF years ago, and still has a different set of functionality, including no coercion. – Jeff Wilcox Jul 17 '14 at 17:01
0

It looks like you can use the CoerceValueCallback associated with the property via the FrameworkPropertyMetadata applied in the dependency property definition. Just install a callback that takes the second argument, the new value, passes it to the object via your own write-only mechanism, then returns null (or for value types, default(T)).

It's true that ".NET remembers the original value prior to coercion", but it won't be propagated via data-binding. Calls to GetValue will return the coerced value, which doesn't leak anything.

I'm using this to implement one-way convenience setters for the value of my primary property, which is a sequence of bytes. A user can bind a string, for example, to set the primary property to the encoded bytes (ASCII or UTF-8, depending what property is set). But not all byte sequences are valid UTF-8, so it isn't possible to reverse the conversion and read a string back through the convenience property.

public string AsciiData
{
    set { BinaryArray = Encoding.ASCII.GetBytes(value); }
}

public static readonly DependencyProperty AsciiDataProperty =
    DependencyProperty.Register("AsciiData",
        typeof(string),
        typeof(HexView),
        new FrameworkPropertyMetadata(null, CoerceAsciiData));

private static object CoerceAsciiData(DependencyObject target, object value)
{
    (target as HexView).AsciiData = value as string;
    return null;
}

The coercion handler can be removed via metadata replacement, so this isn't providing security, but it will prevent developers from accidentally creating coupling in wrong ways.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
-1

I'm confused as to why you can't just have the 'get' return nothing useful?

But furthermore, perhaps you just don't implement the 'OnMyWriteOnlyDependencyPropertyPropertyChanged', in Jeff's example.

No real reason to have the event, if no-one can read it, right?

Noon Silk
  • 54,084
  • 6
  • 88
  • 105
  • 1
    No change to score. However, there are situations with data binding (or in any code!) where you can call GetValue, instead of using the CLR getter. It's why it's super important to NOT place code in the CLR getter/setters for your DependencyProperties. – Jeff Wilcox Aug 25 '09 at 00:49
  • But who is calling GetValue? Himself? Or some control he doesn't have control over? – Noon Silk Aug 25 '09 at 00:51
  • 1
    Exactly the point - you never know who is calling GetValue :-/ that's why an invalid value is probably better than the last set value, for instance. – Jeff Wilcox Aug 25 '09 at 00:56
  • I know in theory we don't know, but I'm just saying in this *specific case*, is anyone calling that? Or are you suggesting that under the hood, sometimes the framework will call the method, and sometimes it will call the property? – Noon Silk Aug 25 '09 at 00:58
  • Other controls, built by other developers, will have direct access to this property. I can't allow them to attempt to use this property's value for anything. It will never be safe for them to do so. – Giffyguy Aug 25 '09 at 04:11
  • So you need to actively stop them from calling .GetValue()? Or can you just not write the getter? I'm truly interested in what it holds, by the way, just incase there is another way around it. – Noon Silk Aug 25 '09 at 04:21
  • I'd have to stop them from calling GetValue(), since it is a dependency property. But if this can't be done, as others have suggested, then I'd have to make GetValue() return an obviously useless dummy-value. – Giffyguy Aug 25 '09 at 06:21