#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;
        }

#493 – Setting the Visibility of a User Interface Element

You can set the visibility of any element that inherits from UIElement by settings its Visibility property to one of the three values listed below.  Because every child hosted in a panel derives from UIElement, this is how you hide/show child elements in a panel.

Values for Visibility (of type System.Windows.Visibility):

  • Visible – element is displayed
  • Hidden – element is not displayed, but the space where it is located is preserved
  • Collapsed – element is not displayed and space for it is not preserved

Below is an example that lets a user toggle between the three possible values for the Visibility of the middle Label.

    <StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10">
            <Label Content="Snoopy" Margin="3" Background="BurlyWood"/>
            <Label Content="Waldo" Margin="3" Background="Thistle"
                   Visibility="{Binding ElementName=cboVisibility, Path=SelectedValue, Converter={StaticResource cboVisibilityConverter}}"/>
            <Label Content="Dagwood" Margin="3" Background="LightGreen"/>
        </StackPanel>
        <ComboBox Name="cboVisibility" HorizontalAlignment="Center" SelectedIndex="0">
            <ComboBox.Items>
                <ComboBoxItem Content="Visible"/>
                <ComboBoxItem Content="Collapsed"/>
                <ComboBoxItem Content="Hidden"/>
            </ComboBox.Items>
        </ComboBox>
        <Label Content="Select visibility of middle Label" HorizontalAlignment="Center"/>
    </StackPanel>



#491 – Displaying a Hyperlink

In WPF, you can display a hyperlink that the user can click on by combining a TextBlock control with a Hyperlink element.  (System.Windows.Controls.TextBlock and System.Windows.Documents.Hyperlink).

In the example below, the TextBlock contains some text on either side of the Hyperlink.  The text that should be underlined, and can be clicked on to navigate to a particular URI, is located within the Hyperlink element.

    <TextBlock HorizontalAlignment="Center">Click to see some great
        <Hyperlink NavigateUri="http://www.worldbeardchampionships.com/" RequestNavigate="Hyperlink_RequestNavigate">
            beards and moustaches
        </Hyperlink>!  (Wow).
    </TextBlock>


To actually browse to a web page when the user clicks on the link, you’ll need to handle the RequestNavigate event.

        private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
            e.Handled = true;
        }

#490 – Using the InkCanvas to Recognize Gestures, part II

With the EditingMode of an InkCanvas set to GestureOnly or to InkAndGesture, the InkCanvas will attempt to recognize any gestures drawn using a mouse or a stylus.

You can handle the Gesture event of an InkCanvas and then call the GetGestureRecognitionResults to get a list of candidate gestures that the InkCanvas thinks that you drew.  In the example below, we get this list of gestures and dump them out to a debug window.

        private void ink_Gesture(object sender, InkCanvasGestureEventArgs e)
        {
            ReadOnlyCollection<GestureRecognitionResult> gestures = e.GetGestureRecognitionResults();

            foreach (GestureRecognitionResult gest in gestures)
            {
                Trace.WriteLine(string.Format("Gesture: {0}, Confidence: {1}", gest.ApplicationGesture, gest.RecognitionConfidence));
            }
        }

Below are some example gestures, followed by the list of GestureRecognitionResult objects. Each has a Confidence property that indicates how confident .NET is of the suggested result.

#489 – Using the InkCanvas to Recognize Gestures, part I

You can set the EditingMode of an InkCanvas to recognize gestures, rather than to let the users draw strokes onto the surface of the InkCanvas.  If you set the EditingMode to GestureOnly, the user cannot draw on the control, but can use the mouse or stylus to draw a gesture, which the InkCanvas will try to recognize.

In this mode, anything that you draw will show up as a stroke while you’re drawing it, but will then disappear as soon as you lift the mouse or stylus.

Drawing:

Done drawing:

As soon as you finish drawing, the InkCanvas will try to recognize what you drew.  It will fire the Gesture event, where you can ask it what gestures it thinks it recognized.

Gestures that the InkCanvas can recognize are listed in the System.Windows.Ink.ApplicationGesture enumeration.  These include things like: circles, squares, checks, and flicks in different directions.

#488 – You Can Change Drawing Attributes of Existing Strokes in an InkCanvas

You can change the drawing attributes for new strokes added to an InkCanvas by setting the DefaultDrawingAttributes property.  You can also change the drawing attributes of strokes already present in the InkCanvas.

Existing strokes are stored in the Strokes property of the InkCanvas, which is of type StrokeCollection (in System.Windows.Ink).  This collection contains instances of Stroke objects, each of which has a DrawingAttributes property, which references an instance of the DrawingAttributes class.

In the example below, we can click on the Fatten button to thicken existing strokes.

Before clicking on Fatten:

After clicking on Fatten:

And again:

Here’s the code for the Button.Click event, which increases the Width of each Stroke already on the InkCanvas.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            foreach (Stroke s in ink.Strokes)
                s.DrawingAttributes.Width = s.DrawingAttributes.Width + 3;
        }

#487 – Specify DrawingAttributes When Drawing to an InkCanvas

You can specify a number of different drawing attributes that affect how new strokes appear when drawing on an InkCanvas control.  You set the DefaultDrawingAttributes property of the InkCanvas to an instance of the DrawingAttributes class, which contains properties that you can set to change how new strokes appear.

Properties of DrawingAttributes include:

  • Color - the color of the new stroke
  • Height – height of the brush used to draw a stroke
  • Width – width of the brush used to draw a stroke
  • FitToCurve – if true, smooths out the stroke
  • IsHighlighter – if true, stroke is somewhat translucent, simulating a highlighter

            <InkCanvas Name="ink" MinHeight="0" MinWidth="0">
                <InkCanvas.DefaultDrawingAttributes>
                    <DrawingAttributes Color="DarkGreen" Width="5" Height="20" FitToCurve="True" IsHighlighter="False" />
                </InkCanvas.DefaultDrawingAttributes>
                <Label Content="Drawing using a dark green stroke, 5x20"/>
            </InkCanvas>

Here are some examples of different values for Color, Width and Height.


Here is an example of setting the IsHighlighter property to true.

#486 – InkCanvas Supports Different Editing Modes

The InkCanvas control has an EditingMode property that allows you to change how you interact with the InkCanvas.  You can draw on it, select strokes that you’ve already drawn, or erase strokes.

EditingMode can take on one of the following values:

  • None - You can’t drawn on the InkCanvas at all
  • Ink - You can draw strokes, using a mouse or stylus

  • GestureOnly - Responds to gestures, does not allow drawing
  • InkAndGesture - Responds to gestures, or allows drawing
  • Select - You can select elements that you previously drew

  • EraseByPoint - You can use the mouse or stylus as an eraser

  • EraseByStroke - You can erase entire strokes by clicking on them

    <Window.Resources>
        <ObjectDataProvider x:Key="editingModes" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="controls:InkCanvasEditingMode"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Border Grid.Column="0" BorderThickness="2" BorderBrush="DodgerBlue"
                Margin="5" >
            <InkCanvas MinHeight="0" MinWidth="0"
                       EditingMode="{Binding ElementName=cboEditingMode, Path=SelectedValue}"/>
        </Border>
        <ComboBox Grid.Column="1" Name="cboEditingMode" Width="100" Height="25" Margin="5"
                  ItemsSource="{Binding Source={StaticResource editingModes}}"/>
    </Grid>

#484 – InkCanvas Contains a Collection of Strokes

When you draw on an InkCanvas control, you create a series of Stroke objects.  One Stroke is created each time you hold the left mouse button down, drag the mouse to draw and then release the mouse button.  All strokes are stored in the Strokes property of the InkCanvas.

Each Stroke stores information about its drawing attributes (e.g. color, width) as well as the collection of points that make up the Stroke, stored in its StylusPoints collection.

In the example below, we use data binding to show all Strokes for an InkCanvas in an adjacent ListBox.

</pre>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="150"/>
        </Grid.ColumnDefinitions>

        <Border Grid.Row="1" BorderBrush="DodgerBlue" BorderThickness="2" Margin="5" SnapsToDevicePixels="True">
            <InkCanvas Name="ink" MinHeight="0" MinWidth="0"/>
        </Border>
        <Label Grid.Column="1" Content="Strokes:" Margin="10,5"/>
        <ListBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding ElementName=ink, Path=Strokes}">
             <ListBox.ItemTemplate>
                 <DataTemplate>
                     <Label Content="{Binding Path=StylusPoints, Converter={StaticResource strokeInfoConverter}}"/>
                 </DataTemplate>
             </ListBox.ItemTemplate>
        </ListBox>
    </Grid>



#483 – InkCanvas MinHeight and MinWidth

If you don’t give an InkCanvas an explicit Height or Width, but leave both to default to Auto, the InkCanvas automatically sets its MinHeight property to 350 and its MinWidth to 250.  This is done using a trigger, created in the constructor of the InkCanvas.

This can be confusing, since the same is not true for the Canvas control, whose MinHeight and MinWidth properties are initially set to 0.0.

Below, we include an InkCanvas control in a window and attach a button at each corner, so that we can better see its boundaries.  The InkCanvas sizes to fit its container (the Window), but you can see that a minimum height and width has been set.

    <InkCanvas>
        <Button Content="Bottom Left" InkCanvas.Bottom="5" InkCanvas.Left="5"/>
        <Button Content="Bottom Right" InkCanvas.Bottom="5" InkCanvas.Right="5"/>
        <Button Content="Top Right" InkCanvas.Top="5" InkCanvas.Right="5"/>
        <Button Content="Top Left" InkCanvas.Top="5" InkCanvas.Left="5"/>
    </InkCanvas>


To allow the InkCanvas to size freely, set MinHeight and MinWidth to 0.0.

Follow

Get every new post delivered to your Inbox.

Join 110 other followers