If the VisualState is ReadOnly and the Trigger Validation.HasError is called the Background changes as expected. But if the Trigger is not any longer true, the Background will not set back to the ReadOnly style, again.

To fix this issue the VisualState of ReadOnly is removed.

<VisualState x:Name="ReadOnly" />

And a Trigger for the IsReadOnly property is defined. This Trigger must be placed above the Validation.HasError Trigger. Otherwise the TextBox would have always the ReadOnly color and never the HasError color.