#384 – The Benefits of Flow-Based Layout

WPF provides several benefits by making flow-based layout a default.

Resolution Independence

This is not really a benefit of flow-based layout, but a benefit based on the fact that WPF specifies positions and sizes in device independent units, rather than in pixels.  If the current DPI setting matches the native DPI of the device, then the actual size of the GUI elements will be as requested.

Controls Automatically Resized or Repositioned

Flow-based layout containers automatically reposition/resize child controls as the container is resized.

Controls Size to Fit Their Content

In WPF, you typically allow a control to size to fit it’s content.  This removes the need for tweaking size and position of elements if the content changes.

    <StackPanel>
        <Button Content="Bob" HorizontalAlignment="Center"/>
        <Button Content="John Jacob Jingleheimer Smith" HorizontalAlignment="Center"/>
    </StackPanel>

Easier Localization

When controls size to fit their content, you don’t need to tweak layout for each localized language.

Advertisements

#383 – The Problems with Coordinate-Based Layout

WPF uses flow-based layout by default (e.g. StackPanel and Grid containers), with coordinate-based layout as an option (e.g. Canvas).

At first glance, coordinate-based layout seems easier.  You can position and size every control exactly how you want it to look.  Flow-based layout can be a little frustrating, as you try to get a container to organize its child controls the way that you want.

But coordinate-based layout has some limitations:

  • Controls look different, depending on the resolution of the display device (e.g. get smaller on higher DPI devices)
  • If you allow resizing a window or any area within a GUI (e.g. with a splitter) you have to do a lot of work to resize or reposition the controls as the container changes size
  • Controls don’t automatically resize or re-position to fit their content
  • Controls don’t automatically resize or re-position to accommodate non-English text

#382 – Persisting RichTextBox Contents as XAML

You can use the Save method of a TextRange object to save the contents of a RichTextBox control.

In the example below, I enter some formatted text into a RichTextBox and then click the Save button.

Here’s the code that does the save behavior.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            TextRange allText = new TextRange(rtfMain.Document.ContentStart, rtfMain.Document.ContentEnd);

            FileStream stream = new FileStream(@"D:\AboutMyDog.xaml", FileMode.Create);

            allText.Save(stream, DataFormats.Xaml);

            if (stream != null)
                stream.Close();
        }

The resulting XAML file consists of an outer Section element that in turn contains multiple Paragraph elements.

    <Paragraph>
        <Run FontStyle="Italic">Kirby</Run>
        <Run> is a </Run>
        <Run FontWeight="Bold">Border Collie</Run>
        <Run>.</Run>
    </Paragraph>
    <Paragraph>
        <Run FontStyle="Italic">Jack</Run>
        <Run> is a </Run>
        <Run FontWeight="Bold">Jack Russell Terrier</Run>
        <Run>.</Run>
    </Paragraph>

#381 – Loading .rtf Files Into a RichTextBox

The RichTextBox control supports loading not only Text (.txt) files, but also Rich text (.rtf)  files.

You can load a file using the Load method of the RichTextBox’s Selection property.

        <RichTextBox x:Name="rtfMain" />
            rtfMain.Selection.Load(new FileStream(@"D:\Spade.rtf", FileMode.Open), DataFormats.Rtf);

#380 – The Frame Control Can Host Web Content

You can use the Frame control to host web content by setting its Source property to a valid URL.  Note that the control can also display standard web navigation controls.

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Frame Grid.Column="0" Source="http://en.wikipedia.org/wiki/Snoopy"
               NavigationUIVisibility="Visible"/>

        <Frame Grid.Column="1" Source="http://acepilots.com/wwi/ger_richthofen.html"
               NavigationUIVisibility="Visible"/>
    </Grid>

#379 – Using a Tooltip As a Magnifier

You can use a tooltip as a magnifier, so that when you hover over a control, it shows an enlarged view of the control.

We define a tooltip to contain a Rectangle that is exactly twice the size of the associated control (using data binding and a value converter).  We then fill the Rectangle with a VisualBrush and bind the Visual property of the VisualBrush to the element that is hosting the tooltip.

    <Window.Resources>
        <loc:DoubleIntConverter x:Key="doubleIntConverter"/>

        <ToolTip x:Key="reflectingTooltip" DataContext="{Binding Path=PlacementTarget, RelativeSource={x:Static RelativeSource.Self}}"
                 Placement="Center">
            <Rectangle Width="{Binding ActualWidth, Converter={StaticResource doubleIntConverter}}"
                       Height="{Binding ActualHeight, Converter={StaticResource doubleIntConverter}}">
                <Rectangle.Fill>
                    <VisualBrush Visual="{Binding}"/>
                </Rectangle.Fill>
            </Rectangle>
        </ToolTip>
    </Window.Resources>

Using the tooltip:

        <Label Content="Can you read me now?" ToolTip="{StaticResource reflectingTooltip}"
               HorizontalAlignment="Center"/>

The implementation of the value converter is left as an exercise.

#378 – Positioning Tooltips

There are several properties that dictate where a tooltip will be displayed.

By default, the tooltip is positioned just below the mouse pointer, with its left edge lined up with the left side of the mouse pointer.

The PlacementTarget property can be used to position the tooltip relative to a specific control.  By default, this property refers to the control to which the tooltip is attached.

You can set the Placement property to change the logic of how the tooltip’s position is calculated.  It can be set to a number of different values, including:

  • Absolute – absolute screen coordinates, specified by HorizontalOffset and VerticalOffset
  • Relative – relative to upper left of control, specified by HorizontalOffset and VerticalOffset
  • Bottom, Top, Left, Right – aligned with edge of control

  • Center – centered on control

  • Mouse – aligned with the current mouse pointer (the default)

The tooltip can be more precisely positioned using the HorizontalOffset and VerticalOffset properties.