#504 – GroupBox Basics

GroupBox is a control that draws a labeled border around a single child element.  Being a content control, it can contain at most one child element, but the child can be a panel control which in turn contains a number of other controls.

The GroupBox is typically used to show visually that a set of child controls are related.

The example below shows a GroupBox that contains a Grid as its child element.  The Grid then contains some Labels.

    <GroupBox Header="Dog Info" Margin="15">
        <Grid Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <Label Grid.Row="0" Grid.Column="0" Content="Name:" FontWeight="Bold" HorizontalAlignment="Right"/>
            <Label Grid.Row="0" Grid.Column="1" Content="Kirby"/>
            <Label Grid.Row="1" Grid.Column="0" Content="Age:" FontWeight="Bold" HorizontalAlignment="Right"/>
            <Label Grid.Row="1" Grid.Column="1" Content="15" />
            <Label Grid.Row="2" Grid.Column="0" Content="Hobby:" FontWeight="Bold" HorizontalAlignment="Right"/>
            <Label Grid.Row="2" Grid.Column="1" Content="Chasing balls"/>
        </Grid>
    </GroupBox>

#503 – Be Careful When Casting RoutedEventArgs.Source

In an event handler, you’ll often cast the RoutedEventsArgs.Source property to a particular type.  When you have an event handler attached to a panel that can contain controls of various types, this can lead to problems.

In the example below, we’re assuming that the originator of the Click event is a Button.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button b = e.Source as Button;
            MessageBox.Show(string.Format("You clicked on {0} button, sender is of type {1}",
                b.Content, sender.GetType().ToString()));
        }

If we have a panel that contains some Buttons, but also a ToggleButton control–which has a Click event but is not a Button–we get a crash when clicking on the ToggleButton.

A better solution is to check the type of the originator.

            if (e.Source is Button)
            {
                Button b = e.Source as Button;
                MessageBox.Show(string.Format("You clicked on {0} button, sender is of type {1}",
                    b.Content, sender.GetType().ToString()));
            }

#502 – Sender vs. RoutedEventArgs.Source

When handling a routed event, you can check the RoutedEventArgs.Source property to get at the control that is the originator of the event.  But the event handler also includes a sender parameter that in many cases also points to the originator of the event.

In the case of routed events, RoutedEventArgs.Source will refer to the originator of the event and sender will refer to the object that owns the event handler.

In the example below, RoutedEventArgs.Source will refer to the Button that a user clicked on, while sender will refer to the StackPanel to which the  Click event is attached.

    <StackPanel ButtonBase.Click="Button_Click">
        <Button Content="Keaton" />
        <Button Content="Chaplin" />
        <Button Content="Arbuckle" />
    </StackPanel>

 

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button b = e.Source as Button;
            MessageBox.Show(string.Format("You clicked on {0} button, sender is of type {1}",
                b.Content, sender.GetType().ToString()));
        }

#501 – Sharing an Event Handler Across Multiple Controls, Method II

If you want to use an event handler to handle an event originating from several controls, you can specify the handler for each control and then use the RoutedEventArgs.Source property to determine the control that originated the event.

You can also just specify a handler for the Button.Click event on the parent panel that holds all of the buttons.  It will handle all Click events that originate lower down in the visual tree.

    <StackPanel ButtonBase.Click="Button_Click">
        <Button Content="Keaton" HorizontalAlignment="Center" Padding="10,5" Margin="5"/>
        <Button Content="Chaplin" HorizontalAlignment="Center" Padding="10,5" Margin="5"/>
        <Button Content="Arbuckle" HorizontalAlignment="Center" Padding="10,5" Margin="5"/>
    </StackPanel>

The event handler is the same as before–you can check the RoutedEventArgs.Source property to determine the Button that originated the event.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Get at originator of event using RoutedEventArgs.Source property

            Button b = e.Source as Button;
            MessageBox.Show(string.Format("You clicked on {0} button", b.Content));
        }

#500 – Sharing an Event Handler Across Multiple Controls, Method 1

You can use the same event handler for more than one control by specifying the handler for each control and pointing to the same event handler code.

In the example below, we have three buttons, each of which wires up a handler for the Click event, but using the same handler (Button_Click).

    <StackPanel>
        <Button Content="Keaton" HorizontalAlignment="Center" Padding="10,5" Margin="5"
                Click="Button_Click"/>
        <Button Content="Chaplin" HorizontalAlignment="Center" Padding="10,5" Margin="5"
                Click="Button_Click"/>
        <Button Content="Arbuckle" HorizontalAlignment="Center" Padding="10,5" Margin="5"
                Click="Button_Click"/>
    </StackPanel>

In the Button_Click event handler, we can check the Source property of the RoutedEventArgs parameter to determine which button sent us the event.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Get at originator of event using RoutedEventArgs.Source property

            Button b = e.Source as Button;
            MessageBox.Show(string.Format("You clicked on {0} button", b.Content));
        }

#499 – Interacting with the Control That Initiated an Event

In WPF, you do not have to provide a name for every control in your user interface.  Since so much is accomplished through data binding, you often don’t need to interact with a control at all from your code.

But one scenario where you might want to interact with a control is from an event handler for an event that the control initiated.  Every event handler will have a sender argument that represents the control that fired the event.  You can cast this argument to the appropriate type to get at the original control.

<Button Content="Click Me" HorizontalAlignment="Center" Padding="10,5" Margin="10"
        Click="Button_Click"/>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button b = sender as Button;
            b.Width = b.ActualWidth + 1;
        }

No Post Today

2,000 Things / WPF is on hiatus today–back tomorrow.

#498 – A List of WPF Blogs and Forums

Though many WPF bloggers have long since moved on to greener pastures, there are still a few blogs out there that either still have good WPF content or that still blog about WPF/XAML.  Here is a list of the most useful blogs and forums.

#497 – Use a UniformGrid to Make a Group of Buttons the Same Size

You can use a StackPanel to make its child elements the same size in one of its dimensions.  This is harder to do in the other dimension.

The example below uses a StackPanel to contain some buttons.  They end up the same height, but are still different widths.

You can make the buttons the same height and width using a UniformGrid instead of a StackPanel.

        <UniformGrid DockPanel.Dock="Bottom" Margin="10" Rows="1" HorizontalAlignment="Right"
                    VerticalAlignment="Bottom">
            <Button Grid.Column="0" Content="No" FontSize="18" Margin="5" Padding="6,3"/>
            <Button Grid.Column="1" Content="Yes, Absolutely" Margin="5" Padding="6,3"/>
            <Button Grid.Column="2" Content="Maybe" Margin="5" Padding="6,3"/>
        </UniformGrid>

The UniformGrid will make sure that each cell is the same height and the same width.  This is desirable because you then avoid having to set the button sizes manually.

#496 – Using a StackPanel to Make a Group of Buttons the Same Size

You’ll often want to stack a group of buttons in a GUI, vertically or horizontally.  You’d typically use a StackPanel to do this.

Let’s say that you want a series of buttons stacked vertically on the right side of a window.

We can use a DockPanel as the main container and add a StackPanel docked on the right and oriented vertically.  But when we do this, the StackPanel expands to fill the available space, as does each Button.

The HorizontalAlignment of the StackPanel defaults to Stretch, as do each of the buttons.  We could set the HorizontalAlignment for each Button to Right, but the buttons now all size to fit their content, which is not quite what we want.

What we really want is for the HorizontalAlignment of each Button to be Stretch, but for the HorizontalAlignment of the StackPanel itself to be Right.  This gives us what we want.