ViewState restoration does not call any setters

I know, it's kind of obvious and similar to what happens during deserialization, but I've fallen into this trap more than once (today, for example), so maybe I can restate the "kind of obvious" so that it can save some time to others. Imagine you have some ViewState-managed property that has a non-trivial setter, like this:
 


public
bool Selected {
 
get {
   
object o = ViewState["Selected"];
   
if (o == null) {
     
return false;
    }
   
return (bool)o;
  }
 
set {
    ViewState["Selected"] =
value;
   
if (Owner == null) return;
   
if (value) {
      Owner.SetSelectedNode(
this);
    }
   
else if (this == Owner.SelectedNode) {
      Owner.SetSelectedNode(
null);
    }
  }
}

Well, you're in trouble if you're not overriding LoadViewState because during ViewState restoration, only the state bag will be restored, but the setter will not be called, and thus the owner of this object will not get notified that the property has been set and will be out of sync. The solution is to isolate any additional treatment that you're doing in ViewState-managed property setters in a private or protected method and to call that method from both the setter and from LoadViewState, like this:
 


public
bool
Selected {
 
get {
   
object o = ViewState["Selected"];
   
if (o == null) {
     
return false;
   
}
   
return (bool)o;
 
}
 
set {
   
ViewState["Selected"] =
value;
   
NotifyOwnerSelected(); 
  
}
}

// ...

private void NotifyOwnerSelected() {
 
object o = ViewState["Selected"];
 
bool value = (o == null ? false : ( bool )o);
 
if (Owner == null ) {
   
return ;
 
}
 
else if (value) { 
    O
wner.SetSelectedNode(
this );
 
}
 
else if (this == Owner.SelectedNode) { 
    O
wner.SetSelectedNode(
null );
 
}
}

// ...

// This method is not mandatory if this is a Control.

void IStateManager.LoadViewState(object state) {
 
LoadViewState(state);
}

protected virtual void LoadViewState(object state) {
 
// Replace the following line with base.LoadViewState
 
// if the class is a control and not simply
 
// a class that implements IStateManager.
 
((IStateManager)ViewState).LoadViewState(state);
  NotifyOwnerSelected();
}

This way, the owner gets notified in all cases that the state of the object changed. Of course, if the ViewState had been public, anyone could defeat this system by changing it directly. This is why ViewState is a protected property of Control.

No Comments