#495 – Binding to a Visibility Property Without Using a Value Converter

You can use a value converter to convert a string value to a value of type System.Windows.Visibility, allowing data binding to a Visibility property.  You can also just set a Visibility property directly to a string, avoiding the need for a value converter.

In the example below, we bind to the Content property of a ComboBoxItem, which we access through the SelectedValue property of a ComboBox.  Since Content contains a string, the binding to Visibility works without a conversion.

    <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.Content}"/>
            <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>

#494 – Using a Value Converter to Bind to a Visibility Property

You can bind the Visibility property of a control to a data item whose type is System.Windows.Visibility.  You can also bind to an item whose type is string, provided that the string values returned represent one of the enumerated values from the Visibility type.

The example below shows how to use a value converter to convert from the SelectedValue of a ComboBox (a ComboBoxItem) to the Visibility type.

        <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>

Code for IValueConverter.Convert:

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Visibility visibility = Visibility.Visible;

            try
            {
                if (value != null)
                {
                    ComboBoxItem item = (ComboBoxItem)value;
                    visibility = (Visibility)Enum.Parse(typeof(Visibility), (string)item.Content, false);
                }
            }
            catch { }

            return visibility;
        }

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



#492 – Layout Can Change at Runtime

One benefit of flow-based layout, as opposed to coordinate-based layout, is that elements in the user interface will size to fit their content.  The GUI designer does not have to explicitly set the size and location of each child element.

The layout containers in WPF don’t just position their child elements at design-time, but will re-measure and arrange their child elements when certain properties of the child elements change.  For example, if the Content property of a Label control changes, the parent container will update its layout based on the new text.

In the example below, when the Label containing the movie title changes, the buttons to its right shift.

    <StackPanel>
        <Label Content="Have you seen this movie?" Margin="3"/>
        <StackPanel Orientation="Horizontal">
            <Label Content="{Binding NextMovieTitle}" Margin="3"/>
            <Button Content="Seen It" Margin="3"/>
            <Button Content="NOT Seen It" Margin="3"/>
        </StackPanel>
        <Button Content="Next Movie" HorizontalAlignment="Center" Margin="3"
                Click="btnNext_Click"/>
    </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>