#946 – Checking for Valid Characters in a String

You can use a regular expression to check a string to see if the string contains only characters within a specified set of characters.

For example, to check whether a string is limited to a single line containing only alphabetic characters (e.g. a..z, A..Z), you can use the regular expression shown below.

        private bool IsAlphabetic(string s)
        {
            Regex r = new Regex(@"^[a-zA-Z]+$");

            return r.IsMatch(s);
        }

The regular expression defines a pattern and the IsMatch method checks the string to see if it matches the pattern.  You can read the regular expression as follows:

  • ^ – start of the string
  • [a..zA..Z] – single character that is a lowercase or uppercase letter
  • + – repeat previous item one or more times (alphabetic character)
  • $ – end of the string

A string will therefore match if it is a single line of text containing at least one alphabetic character and is limited to alphabetic characters.

946-001

 

946-002

Advertisement

#714 – Setting the Cursor to Render Some Text While Dragging

You can use the GiveFeedback to change the cursor during a drag-and-drop operation.  You can set the cursor to be some text by rendering a visual element to a bitmap and then converting that bitmap to a cursor.

Here’s a helper class containing a method that converts some text to a cursor.

    public class CursorHelper
    {
        private static class NativeMethods
        {
            public struct IconInfo
            {
                public bool fIcon;
                public int xHotspot;
                public int yHotspot;
                public IntPtr hbmMask;
                public IntPtr hbmColor;
            }

            [DllImport("user32.dll")]
            public static extern SafeIconHandle CreateIconIndirect(ref IconInfo icon);

            [DllImport("user32.dll")]
            public static extern bool DestroyIcon(IntPtr hIcon);

            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
        }

        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
        private class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            public SafeIconHandle()
                : base(true)
            {
            }

            override protected bool ReleaseHandle()
            {
                return NativeMethods.DestroyIcon(handle);
            }
        }

        private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp)
        {
            var iconInfo = new NativeMethods.IconInfo();
            NativeMethods.GetIconInfo(bmp.GetHicon(), ref iconInfo);

            iconInfo.xHotspot = 0;
            iconInfo.yHotspot = 0;
            iconInfo.fIcon = false;

            SafeIconHandle cursorHandle = NativeMethods.CreateIconIndirect(ref iconInfo);
            return CursorInteropHelper.Create(cursorHandle);
        }

        public static Cursor CreateCursor(string cursorText)
        {
            // Text to render
            FormattedText fmtText = new FormattedText(cursorText,
                    new CultureInfo("en-us"),
                    FlowDirection.LeftToRight,
                    new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Normal, new FontStretch()),
                    12.0,  // FontSize
                    Brushes.Black);

            // The Visual to use as the source of the RenderTargetBitmap.
            DrawingVisual drawingVisual = new DrawingVisual();
            DrawingContext drawingContext = drawingVisual.RenderOpen();
            drawingContext.DrawText(fmtText, new Point());
            drawingContext.Close();

            // The BitmapSource that is rendered with a Visual.
            RenderTargetBitmap rtb = new RenderTargetBitmap(
                (int)drawingVisual.ContentBounds.Width,
                (int)drawingVisual.ContentBounds.Height,
                96,   // dpiX
                96,   // dpiY
                PixelFormats.Pbgra32);
            rtb.Render(drawingVisual);

            // Encoding the RenderBitmapTarget into a bitmap (as PNG)
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(rtb));

            using (var ms = new MemoryStream())
            {
                encoder.Save(ms);
                using (var bmp = new System.Drawing.Bitmap(ms))
                {
                    return InternalCreateCursor(bmp);
                }
            }
        }
    }

We can test this code by setting the cursor to some text when we drag a Label.

    <StackPanel>
        <Label Content="Mayflower" Background="AliceBlue" 
               HorizontalAlignment="Center" Margin="15" Padding="20,5"
               MouseLeftButtonDown="Label_MouseLeftButtonDown"
               GiveFeedback="Label_GiveFeedback"/>
        <Label Content="Drag to here" Background="SpringGreen" 
               HorizontalAlignment="Center" Margin="15"
               Drop="Label_Drop"/>
    </StackPanel>

Here’s the code for the drag-and-drop operation.

        private void Label_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DataObject data = new DataObject(DataFormats.Text, ((Label)e.Source).Content);

            DragDrop.DoDragDrop((DependencyObject)e.Source, data, DragDropEffects.Copy);
        }

        private void Label_Drop(object sender, DragEventArgs e)
        {
            ((Label)e.Source).Content = (string)e.Data.GetData(DataFormats.Text);
        }

        private Cursor customCursor = null;

        private void Label_GiveFeedback(object sender, GiveFeedbackEventArgs e)
        {
            if (e.Effects == DragDropEffects.Copy)
            {
                if (customCursor == null)
                    customCursor = CursorHelper.CreateCursor("Mayflower - 18 Dec 1620");

                if (customCursor != null)
                {
                    e.UseDefaultCursors = false;
                    Mouse.SetCursor(customCursor);
                }
            }
            else
                e.UseDefaultCursors = true;

            e.Handled = true;
        }

714-001

#630 – PreviewTextInput and TextInput Events

In addition to the four main keypress events–PreviewKeyDown, KeyDownPreviewKeyUp and KeyUp–an UIElement can fire two other events related to keyboard input.  Both fire when the user presses a key, or combination of keys, that results in the control receiving some text.  They do not fire when keys are pressed that don’t result in the keyboard sending text (e.g. the Backspace key).

Here’s the updated sequence of events:

  • PreviewKeyDown – tunneling
  • KeyDown – bubbling
  • PreviewTextInput  – tunneling
  • TextInput – bubbling
  • PreviewKeyUp – tunneling
  • KeyUp – bubbling

Note that controls that normally accept text and do something with it will suppress the TextInput event, marking it as handled.  For example, the TextBox control takes the text input and adds it to to the TextBox, so it marks TextInput as already handled.  The TextBox is saying that it already “handled” the text, so it doesn’t need to pass the event on to anybody else.

#568 – Setting Text-Related Properties in Blend

You can set text-related properties for a control using the fields in the Text area of the Properties panel.

For example, you can set the font family, font style and font size for a Label control.

Or you can click on the tab with the paragraph mark to see properties relating to formatting, where we can set a TextAlignment property for centering a block of text.

 

#339 – Wrapping a Button’s Text Content to Multiple Lines

When a Button control automatically sizes to fit its content, it will grow to fit the text in the Content property.  However, the text will always remain on a single line.

        <Button Content="Click me if you want to see something cool.  In fact, click as many times as you like."
                HorizontalAlignment="Center" VerticalAlignment="Center"


If you constrain the button’s Width, however, the text will be clipped.

To get the text on a face of a Button to wrap, you can use a TextBlock as the button’s Content, rather than a simple text string.  You also set the TextWrapping property on the TextBlock.

        <Button HorizontalAlignment="Center" VerticalAlignment="Center"
                Margin="10" Width="120">
            <TextBlock Text="Click me if you want to see something cool.  In fact, click as many times as you like."
                       TextWrapping="Wrap"/>
        </Button>

#318 – TextBox Basics

A TextBox control is a control that displays some text and allows a user to edit it.

The Text property controls that text that is displayed in the TextBox.  It can be set, to indicate the text that should appear in the TextBox.  It can also be read, to retrieve text entered by the user.

    <StackPanel>
        <TextBox Name="txtKing" Text="I'm Henry VIII." Height="25" Width="150"/>
        <Button Content="Display Text" Click="Button_Click" Width="100" Margin="10"/>
    </StackPanel>

When the program starts, the TextBox contains the desired text:

The user can edit this text or enter new text.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(txtKing.Text);
        }

#259 – Setting Typography Properties for Text Rendered with an OpenType Font

WPF includes a Typography class, which allows setting various attached properties for textual elements.  These properties only affect text that is rendered using an OpenType font.

The Typography class lets you specify things like:

  • Superscripts and subscripts
  • Use of capitals and spacing between capitals
  • Ligatures (combined glyphs)
  • Swashes (decorative elements)
  • Alternate glyphs
  • Historical glyphs
  • Proportional spacing for numerals
  • Turning kerning off/on

For example, we can specify that some text be rendered using small capitals, as follows.

		<TextBlock Margin="20" Width="250" Height="55"
		    FontFamily="Constantia" FontSize="14"
		    TextWrapping="Wrap">
			We secure our friends not by accepting favors but by doing them.
			<Run Typography.Capitals="SmallCaps">--Thucydides</Run>
		</TextBlock>

#246 – Use FlowDocument Control to Host Entire Documents

The FlowDocument control provides richer functionality than Label and TextBlock for displaying text.

A FlowDocument contains a collection of blocks, each of which is a separate paragraph, list, section or table.  Below is a simple example.

		<FlowDocument FontFamily="Cambria" FontSize="16">
			<Paragraph FontFamily="Arial" FontSize="14">
				Excerpt from <Italic>White Fang</Italic>, by <Bold>Jack London</Bold>
			</Paragraph>

			<Paragraph>
				A second cry arose, piercing the silence with needle-like shrillness.  Both men located the sound.
				It was to the rear, somewhere in the snow expanse they had just traversed.  A third and answering
				cry arose, also to the rear and to the left of the second cry.
			</Paragraph>

			<Paragraph>
				“They’re after us, Bill,” said the man at the front.
			</Paragraph>

			<Paragraph>
				His voice sounded hoarse and unreal, and he had spoken with apparent effort.
			</Paragraph>
		</FlowDocument>

Note that the content in the document automatically flows to fit the available space.

#243 – Display Text Using a Label Control

We saw how to use the DrawText and DrawGlyphRun methods to draw text into a visual element at a very low-level.  A much easier way to display text in a user interface is to use the Label control.

Because Label inherits from UIElement, you can include it in XAML as child of a Panel control.

You set the text for the Label using its Content property.

Here are some examples of Label controls.

	<StackPanel Orientation="Vertical">
		<Label Content="Miss Barkley was quite tall."/>
		<Label Content="A man is never lost at sea." FontFamily="Verdana" FontSize="16" FontWeight="Bold"/>
		<Label Content="Listen, Robert, going to another country doesn't make any difference." Foreground="Blue" Background="Pink"/>
	</StackPanel>