XAML element properties and cut-paste refactoring

As you know, to set properties for WPF objects in XAML, one uses either attributes or elements, like the Text and LayoutTransform properties of the TextBox element below, respectively:

<Border>
  <TextBlock Text="Hello!">
    <TextBlock.LayoutTransform>
      <ScaleTransform ScaleY="2"/>
    </TextBlock.LayoutTransform>
  </TextBlock>
</Border>

Using elements is required when the content to set as property value is complex (and cannot be expressed with a simple attribute) and it is also local (i.e. there are no reasons to transform it into a resource), such as the case of the LayoutTransform setting above.

However, many times XAML is refactored during the lifetime of a project, and requirements like this appear often: the Hello text block should be replaced by a TextBox when the app enters edit mode, so that the end user can change the text at will.

To address this, you would probably update the XAML to something like this:

<Border>
  <Grid>
    <TextBlock Text="Hello!" Visibility="{Binding IsReadOnly, Converter={StaticResource BooleanToVisibilityConverter}}">
      <TextBlock.LayoutTransform>
        <ScaleTransform ScaleY="2"/>
      </TextBlock.LayoutTransform>
    </TextBlock>
    <TextBox Text="{Binding Text, ElementName=TextBlock}" Visibility="{Binding IsEditing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
  </Grid>
</Border>

Of course, this works assuming that you have prepared IsReadOnly and IsEditing properties under data context (e.g. view model), and surely there are easier ways (such as triggers) to do this, but for now let’s focus on the original LayoutTransform setting. In the updated code, it applies only to the TextBlock, but not to the TextBox, so the look of the text will change when entering edit mode. Not good!

To resolve the problem, you need to move the ScaleTransform definition to the LayoutTransform property of the element that contains both the TextBlock and the TextBox, which is the new Grid element that you introduced to ensure that the two text elements are on top of each other, i.e. same grid row and column, zero and zero, since a Border cannot have multiple children (and you needed a Panel, e.g. Grid):

<Border>
  <Grid>
    <Grid.LayoutTransform>
      <ScaleTransform ScaleY="2"/>
    </Grid.LayoutTransform>
    <TextBlock Text="Hello!" Visibility="{Binding IsReadOnly, Converter={StaticResource BooleanToVisibilityConverter}}"/>
    <TextBox Text="{Binding Text, ElementName=TextBlock}" Visibility="{Binding IsEditing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
  </Grid>
</Border>

But as you can see in the updated example, the property element needs to be updated, i.e. the prefix class needs to be changed from TextBlock to Grid, so the refactoring is not an easy cut and paste operation! Imagine this repeats for multiple properties and multiple locations within the XAML.

To avoid the required prefix changes, however, you could have used this simple tip from the beginning:

When setting WPF properties using XAML elements, use a base class prefix.

In our example, that means that we could use <FrameworkElement.LayoutTransform> instead of <TextBlock.LayoutTransform> originally, since TextBlock inherits, at some point in the hierarchy, from FrameworkElement, and then we could easily move the definition from the TextBlock to the root Grid with a simple cut-paste operation because the Grid is also a FrameworkElement itself – both examples below work correctly:

<TextBlock ...>
  <FrameworkElement.LayoutTransform>
    <ScaleTransform ScaleY="2"/>
  </FrameworkElement.LayoutTransform>
</TextBlock>
<Grid ...>
  <FrameworkElement.LayoutTransform>
    <ScaleTransform ScaleY="2"/>   
  </FrameworkElement.LayoutTransform>
  ...
</Grid>

And now you just need to know good base classes for the objects you set up in XAML. Although the WPF class hierarchy seems (and is) complex, remember that most of the times FrameworkElement is the one you can use for most common properties where you would want to support cut-paste refactoring (interchangeability).

About Sorin Dolha

My passion is software development, but I also like physics.
This entry was posted in WPF and tagged , , , , , , . Bookmark the permalink.

Add a reply