CanWriteProperty

CanWriteProperty

Old forum URL: forums.lhotka.net/forums/t/3782.aspx


culprit posted on Thursday, October 25, 2007

I am using the Overrides of the CanWriteProperty function to cause the interface to disallow editing of properties I wish to be readonly. My editable business objects also often have several readonly child collections for wish I want every property in that child collection to be readonly. Some of these child collections have many properties; is there a way I can make all the properties of a readonly child collection unwritable so that the interface marks the text boxes as readonly as above? I have tried returning false in the CanWriteProperty function on the collection name but it appears only the property name gets passed to this function.

Thank you in advance,

Rob

ajj3085 replied on Friday, October 26, 2007

Rob,

If the properties are always readonly, you might be better off just removing the property setter, or making it private.  Then set your textboxes to readonly. 

If they CAN be writable by those with permissions, then you should go the CanWriteProperty route.  Override just as you would expect.  The part you're missing may be using the ReadWriteAuthorization component, which you can use to automatically set textboxes to readonly, and other controls disabled.  You can control that behavior on a control by control basis as well.

HTH
Andy

culprit replied on Friday, October 26, 2007

Andy,

Thanks for the response. I am using the ReadWriteAuthorization and it works great, in fact, I am wanting to find a way to make it mark all the text boxes containing fields from readonly child collections as readonly. It works great for a few fields in the Editable Business Object itself to use the CanWriteProperty such as:

Public Overrides Function CanWriteProperty(ByVal propertyName As String) As Boolean

If propertyName = "ID" Then

Return False

End If

End Function

But when trying to make all the properties of a child collection (50 to 100 properties) readonly thru this method seems impractical. However, I definitely want to prevent the UI designer from having to manually make all the TextBoxes containing these child columns ReadOnly. I want to control as much as possible from the Business Objects.

Maybe there is a way through reflection or some other means to determine if the property being checked by the parent object thru the CanWriteProperty routine is actually a property of a child collection.

If propertyName is ChildOf(ReadOnlyCollectionName) Then

Return False

End if

Possibly something like this. Any thoughts.

Rob

ajj3085 replied on Friday, October 26, 2007

I'm a little confused now; does the collection itself have a bunch of properties?  Or the child objects in the collection?

culprit replied on Friday, October 26, 2007

Yes, here is the declaration and property for the collection in the parent (BusinessBase) object:

Private _drugInfo As ScriptAssist.DrugInfo

Public ReadOnly Property DrugInfo() As ScriptAssist.DrugInfo

<System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _

   Get

      CanReadProperty("DrugInfo", True)

      Return _drugInfo

   End Get

End Property

As you might expect the DrugInfo object has many properties. It is of type ReadOnlyBase and has about 50 properties each of which need to be unwritable in the user interface. I wish there was a way in the CanWriteProperty routine of the BusinessBase class to get the class for the which the property is a member of, in this case the property is a member of the DrugInfo class.

Rob

 

ajj3085 replied on Friday, October 26, 2007

Ok, I'm a bit more confused now..

DrugInfo is a ReadOnlyBase... so there is no CanWriteProperty... so where does CanWriteProperty come in?

culprit replied on Friday, October 26, 2007

DrugInfo is a child collection of the parent BusinessBase, PatientRecord. The CanWriteProperty routine of the parent object, PatientRecord, is called when I call:

Me.ReadWriteAuthorization1.ResetControlAuthorization()

For each property exposed on the form from the child collection. Because there are so many properties in this child collection as well as others I do not wish to create hundreds of lines of code in the CanWriteProperty routine of the parent BusinessBase.  These children are all ReadOnly therefore it seems like there would be a way to always return a false in the CanWriteProperty of the parent when accessing properties on these ReadOnly children.

Does this make more sense?

Thank You,

Rob

RockfordLhotka replied on Friday, October 26, 2007

This is all perfectly valid. The whole reason ReadOnlyBase has CanReadProperty() is to allow authorization-based rules to refuse to let the UI read some values based on the user's roles.

However, some UI data binding technologies (Windows and WPF in particular) try to read the value no matter what you do (even if you otherwise prevent the value from being displayed), and of course that causes an exception and that can make the app go boom.

As I discuss in Using CSLA .NET 3.0, a solution to this is to use a different overload of the method:

Public ReadOnly Property Boo() As String
  Get
    If CanReadProperty(False) Then
      Return _boo
    Else
      Return "n/a"
    End If
  End Get
End Property

Rather than throwing an exception, return a dummy value. Then your UI code (using Csla.Windows.ReadWriteAuthorizer or Csla.Wpf.Authorizer) can prevent the dummy value from appearing to the user, even though data binding insisted on getting it from the object...

RockfordLhotka replied on Friday, October 26, 2007

culprit:

<System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _

   Get

      CanReadProperty("DrugInfo", True)

By the way, if you are going to pass the string literal name of the property to CanReadProperty(), then you shouldn't use the NoInlining attribute. All it does in that case is cost you a little performance.

culprit replied on Saturday, October 27, 2007

Rocky,

Thank you for your replies. However, I am trying to prevent the writing of the property via the ReadWriteAuthorization control (Would result in TextBoxes being readonly). Do you have a tip on how to do this for all properties in a ReadOnlyBase child class of a BusinessBase object?

Many Thanks,
Rob

RockfordLhotka replied on Saturday, October 27, 2007

ReadOnlyBase is designed to support objects with read-only properties. If you want read-write properties, then use BusinessBase.

 

Rocky

 

From: culprit [mailto:cslanet@lhotka.net]
Sent: Saturday, October 27, 2007 1:42 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CanWriteProperty

 

Rocky,

Thank you for your replies. However, I am trying to prevent the writing of the property via the ReadWriteAuthorization control (Would result in TextBoxes being readonly). Do you have a tip on how to do this for all properties in a ReadOnlyBase child collection of a BusinessBase object?

Many Thanks,
Rob



culprit replied on Sunday, October 28, 2007

Rocky,

Yes, but here is my issue. I have a BusinessBase, Patient, which contains a child class ReadOnlyBase class, RecordInfo. What I was looking for was a way to make all the properties of the child ReadOnlyBase class, RecordInfo, ReadOnly with regards to the ReadWriteAuthorization control. For the BusinessBase properties I wish to make ReadOnly for the ReadWriteAuthorixation control I use the CanWriteProperty function and return False, but because there are so many properties in my ReadOnlyBase child classes I was hoping there was a way to return False in the CanWriteProperty function for all properties of the child ReadOnlyBase classes I might have. It just seems that thru reflection or some other means I could return False for all properties of these ReadOnlyBase child classes. Could you help in this situation?

Thank You,
Rob

culprit replied on Monday, October 29, 2007

Rocky,

I have also noticed that if I have a property in the ReadOnlyBase child class that is the same name as a property in a parent BusinessBase class the propertyName comes thru as the same name in the CanWriteProperty routine in the parent class. With this in mind do you have a suggestion on how to tell that the property being analyzed in the CanWriteProperty originates from the child ReadOnlyBase class? Possibly some reflection method or other way could be used.

Thanks,

Rob

RockfordLhotka replied on Monday, October 29, 2007

I'm afraid you are on your own here - this is outside the intended scope of ReadWriteAuthorizer, because having read-write properties on a ReadOnlyBase object is outside the scope of the ROB concept.

It has been a long time since I've looked at the RWA control's code, so I don't remember what information is available from the binding objects. I believe, though, that you can get access to the underlying business object's type and property name. From there, you can probably get a System.ComponentModel.PropertyInfo object for the business object's property, and that would tell you if the property is a read-only property (having nothing to do with authorization - just the fact that the property is read-only). That would probably be a better solution than trying to graft a CanWriteProperty() into read-only properties.

That'd be in the ApplyAuthorizationRules() method, where you'd need to change the code to accomodate this other concept.

culprit replied on Tuesday, October 30, 2007

Rocky,

Thank you very much. I will do this and post the resulting solution in case others need this functionality.

Rob

culprit replied on Wednesday, October 31, 2007

I know modifying the ReadWriteAuthorization component is probably the correct path but I am having difficulty with the case of ROCs (ReadOnlyChildren [ReadOnlyBase objects]).

I am modifying the ApplyAuthorizationRules to try and determine if a property of an object is set to ReadOnly. This works except when the property is a property of a child class. I cannot seem to get to the child class even though it appears to be coming across in the BindingMemberInfo. Here is a little of what I have tried:

' get the object property name

Dim propertyName As String = binding.BindingMemberInfo.BindingField

Dim bindingPath As String = binding.BindingMemberInfo.BindingPath

Dim bindInfo As New BindingMemberInfo(binding.BindingMemberInfo.BindingMember)

Dim t As Type = bs.Current.GetType

Dim pInfo As PropertyInfo

If bindingPath.Length > 0 Then

'This is not working to get to the child class datasource

Dim newsource As BindingSource = New BindingSource(bs.Current, bindingPath)

pInfo = bs.Current.GetType().GetProperty(binding.BindingMemberInfo.BindingMember)

pInfo = newsource.Current.GetType().GetProperty(binding.BindingMemberInfo.BindingMember)

Else

pInfo = bs.Current.GetType().GetProperty(binding.BindingMemberInfo.BindingMember)

End If

If pInfo.CanWrite = True Then

ApplyWriteRules(control, binding, _

ds.CanWriteProperty(propertyName))

End If

ApplyReadRules(control, binding, _

ds.CanReadProperty(propertyName))

The BindingMemberInfo is returning the Property name alone in the BindingField property (ie - "Cost"), the child class property of the parent in the BindingPath property ("DrugInfo") and the complete qualified name in the BindingMember property ("DrugInfo.Cost").

However, I am not able to get to the child class thru the BindingSource.

Could someone please give me a suggestion of how to go from here?

Thanks,

Rob

Copyright (c) Marimer LLC