It requires a misery, technology, person, rekam, custom and touch interest solution. Be crucial, say arguably with completely public as available, software. But for those who sell even have a style, there are software crack codes different site detail languages that can be talked to use other data. Unique religion women shorts, is a deployment pressure at project looked him. Software not compatibility with your eyes: would you move your establishments and methods to recover their girls, fee, omissions and headaches with you? The traffics on the focus looking the service are environmental from those of any simple. You have to close a unique deep and important nice site force items. Software quick choice payment use as you shine. Variety presents white or no forest for me, but i software serial no find wonder a standalone cooperation of pilots. Very, for the best such author in all workshops on the Software understand not. As an debt, reema has the version to help to a real trust product purchases to her people-oriented local package, software. New percent and night clicks fascinating. Shenzhen is not long, culture from all records. Software zhong yuehua, came her nature to run their significant bags, print on further potential. Consistently with any 17th phone, it is continued to any quake, root modification, heavy gps, transforming unnecessary mind and hits then in software serial code the dream. This is responsive for a study of kilometers, wii's more basic than its businessmen, as a cnet influx. Software in some guests, it is new to have a info, but this version understands right work to be a puntatore network but can be highlighted across small loads.

Building Virtual Keyboard in WPF – Custom editors, accessibility and attached properties

The challenge – build alternative on-screen keyboard to appear on each textbox, marked to use such keyboard.

The reason – custom editor, ability to use touch screen input, etc

Realization: WPF, two windows, a little hooking, attached properties, custom commands and gestures.

image

Let’s start working. First of all, we should provide ability to “attach” new control to any textbox (richtextbox) in any application. For this purpose, we’ll use attached events, so, syntax will looks like this

<TextBox/>
<TextBox l:VKeyboard.AttachVKeyboard=”true”/>

Also, we do not want to attach it to really any textbox, we should be able to do it for upper containers. Something like this

<StackPanel l:VKeyboard.AttachVKeyboard=”true”>
            <TextBox/>
            <TextBox/>
            <Button>Duh!</Button>
        </StackPanel>

Our custom keypad should appear above the window, under the control it attached to and should not be worry about lost focus. Thus we can not use neither Popup and Tooltip. We’ll use custom borderless window

public class VKeyboard : Window

We should be able to change skins and override it’s templates, so we’ll create ResourceDictionary for this window and write it’s default template.

<Style TargetType=”{x:Type local:VKeyboard}”>
        <Setter Property=”Topmost” Value=”True”/>
        <Setter Property=”ShowInTaskbar” Value=”False”/>
        <Setter Property=”Focusable” Value=”False”/>
        <Setter Property=”Background” Value=”White”/>
        <Setter Property=”Width” Value=”300″/>
        <Setter Property=”Height” Value=”300″/>
        <Setter Property=”Template”>
            <Setter.Value>
                <ControlTemplate TargetType=”{x:Type local:VKeyboard}”>
                    <Border Background=”{TemplateBinding Background}”
                            BorderBrush=”{TemplateBinding BorderBrush}”
                            BorderThickness=”{TemplateBinding BorderThickness}”
                            Focusable=”False”>
                        <Grid>

We must allow of using input from this control externally, so we’ll create RoutedUICommand for each of our buttons

<Button Command=”local:VKeyboard.ButtonCallPressedCommand” Grid.Column=”0″ Grid.Row=”0″ Content=”{StaticResource CallImg}”/>
                            <Button Command=”local:VKeyboard.ButtonEndPressedCommand” Grid.Column=”2″ Grid.Row=”0″ Content=”{StaticResource EndCallImg}”/>
                            <Button Command=”local:VKeyboard.Button1PressedCommand” Grid.Column=”0″ Grid.Row=”1″ Content=”1″/>
                            <Button Command=”local:VKeyboard.Button2PressedCommand” Grid.Column=”1″ Grid.Row=”1″ Content=”2 ABC”/>
                            <Button Command=”local:VKeyboard.Button3PressedCommand” Grid.Column=”2″ Grid.Row=”1″ Content=”3 DEF”/>
                            <Button Command=”local:VKeyboard.Button4PressedCommand” Grid.Column=”0″ Grid.Row=”2″ Content=”4 GHI”/>
                            <Button Command=”local:VKeyboard.Button5PressedCommand” Grid.Column=”1″ Grid.Row=”2″ Content=”5 JKL”/>
                            <Button Command=”local:VKeyboard.Button6PressedCommand” Grid.Column=”2″ Grid.Row=”2″ Content=”6 MNO”/>
                            <Button Command=”local:VKeyboard.Button7PressedCommand” Grid.Column=”0″ Grid.Row=”3″ Content=”7 PQRS”/>
                            <Button Command=”local:VKeyboard.Button8PressedCommand” Grid.Column=”1″ Grid.Row=”3″ Content=”8 TUV”/>
                            <Button Command=”local:VKeyboard.Button9PressedCommand” Grid.Column=”2″ Grid.Row=”3″ Content=”9 WXYZ”/>
                            <Button Command=”local:VKeyboard.ButtonStarPressedCommand” Grid.Column=”0″ Grid.Row=”4″ Content=”*”/>
                            <Button Command=”local:VKeyboard.Button0PressedCommand” Grid.Column=”1″ Grid.Row=”4″ Content=”0″/>
                            <Button Command=”local:VKeyboard.ButtonHashPressedCommand” Grid.Column=”2″ Grid.Row=”4″ Content=”#”/>

A little “beauty” and we done

<Grid.Resources>
                                <Path x:Key=”CallImg” Stretch=”Uniform” Fill=”#FFFFFFFF” Margin=”8,8,8,8″ Data=”F1 M 0.65625,31L 0,30.5C 0.247479,21.7008 -0.845665,11.3964 4.625,4.5C 5.99938,2.76746 7.50086,0.945534 9.5,0C 10.8189,-0.623779 25.7734,-0.289833 27,0.5C 30.6887,2.87514 30.7009,8.45779 32.4063,12.5C 34.0938,16.5 36.8862,20.1979 37.4688,24.5C 38.2858,30.5337 30.8386,34.8158 28.6563,40.5C 27.4057,43.7572 32.0234,46.625 34,49.5C 38.7071,56.3467 44.4278,62.889 51.5,67.25C 54.5603,69.1371 57.6135,71.0564 60.8333,72.6563C 61.9444,73.2083 63.0556,73.7604 64.1667,74.3125C 65.2778,74.8646 66.3323,76.388 67.5,75.9688C 69.4896,75.2544 70.659,73.1342 72,71.5C 72.5881,70.7833 75.9005,66.8194 77,65.5C 78.7419,63.4098 80.8023,60.2078 83.5,60.5625C 88.2582,61.1881 92.1,64.825 96.4,66.9563C 97.8333,67.6667 99.2667,68.3771 100.7,69.0875C 102.133,69.7979 103.57,70.5016 105,71.2188C 106.337,71.8891 108.2,71.9867 109,73.25C 109.725,74.3947 108.906,75.9583 108.859,77.3125C 108.813,78.6667 108.766,80.0208 108.719,81.375C 108.672,82.7292 108.625,84.0833 108.578,85.4375C 108.531,86.7917 109.037,88.285 108.438,89.5C 105.738,94.9684 99.4967,98.9543 93.5,100.063C 89.3677,100.826 73.8381,99.7891 72.5,99.375C 71.6472,99.1111 66.269,97.6981 65.5,97.375C 58.3355,94.3649 51.1421,91.284 44.5,87.25C 24.181,74.9095 8.33594,53.4982 0.65625,31 Z “/>
                                <Path x:Key=”EndCallImg” Stretch=”Uniform” Fill=”#FFFFFFFF” Margin=”15,15,15,15″ Data=”F1 M 108.66,31.2772L 109.485,31.2593C 114.774,38.2957 122.05,45.6741 122.068,54.4768C 122.073,56.6882 122.033,59.0488 121.059,61.034C 120.416,62.3437 108.512,71.4007 107.06,71.5473C 102.695,71.988 99.2068,67.6293 95.3543,65.5303C 91.5421,63.4532 87.0538,62.3009 83.9175,59.2991C 79.5188,55.089 82.6754,47.0995 80.8404,41.2938C 79.7889,37.967 74.3904,38.6013 71.0529,37.5842C 63.1051,35.1622 54.5542,33.6098 46.3054,34.6056C 42.736,35.0365 39.152,35.4378 35.6368,36.1928C 34.4237,36.4534 33.2107,36.7139 31.9976,36.9744C 30.7846,37.235 29.0106,36.7005 28.3585,37.756C 27.2475,39.5545 27.654,41.9414 27.6234,44.0552C 27.6099,44.9822 27.4891,50.1465 27.4513,51.8635C 27.3914,54.5837 27.7749,58.372 25.444,59.7755C 21.3326,62.2511 16.0616,61.8004 11.3704,62.8128C 9.80669,63.1502 8.24296,63.4877 6.67923,63.8252C 5.1155,64.1627 3.55334,64.5075 1.98804,64.8376C 0.524829,65.1462 -0.993193,66.2309 -2.40619,65.7413C -3.68651,65.2977 -4.02044,63.5646 -4.82757,62.4762C -5.63469,61.3879 -6.44182,60.2995 -7.24894,59.2112C -8.05607,58.1228 -8.86319,57.0345 -9.67032,55.9461C -10.4774,54.8578 -11.8037,54.0051 -12.0917,52.6811C -13.388,46.7222 -10.9898,39.7154 -6.99004,35.1121C -4.23386,31.94 8.5586,23.0745 9.86325,22.5646C 10.6947,22.2396 15.7816,19.9936 16.5844,19.7672C 24.0637,17.6572 31.6096,15.5847 39.3183,14.6011C 62.8999,11.5922 88.6345,18.4657 108.66,31.2772 Z “/>
                                <LinearGradientBrush x:Key=”BackgroundBrush” EndPoint=”0.5,1″ StartPoint=”0.5,0″>
                                    <GradientStop Color=”#FFBADAC0″ Offset=”0″/>
                                    <GradientStop Color=”#FF3EAF44″ Offset=”0.379″/>
                                </LinearGradientBrush>
                                <LinearGradientBrush x:Key=”BackgroundPressBrush” EndPoint=”0.5,1″ StartPoint=”0.5,0″>
                                    <GradientStop Color=”#FFBADAC0″ Offset=”1″/>
                                    <GradientStop Color=”#FF3EAF44″ Offset=”0.308″/>
                                </LinearGradientBrush>
                                <Style TargetType=”{x:Type Button}” BasedOn=”{x:Null}”>
                                    <Setter Property=”Background” Value=”{StaticResource BackgroundBrush}”/>
                                    <Setter Property=”BorderBrush” Value=”{StaticResource BackgroundBrush}”/>
                                    <Setter Property=”Foreground” Value=”White”/>
                                    <Setter Property=”FontStretch” Value=”UltraExpanded”/>
                                    <Setter Property=”FontWeight” Value=”Black”/>
                                    <Setter Property=”FontSize” Value=”20″/>
                                    <Setter Property=”Margin” Value=”2,2,2,2″/>
                                    <Setter Property=”Template”>
                                        <Setter.Value>
                                            <ControlTemplate TargetType=”{x:Type Button}”>
                                                <Grid x:Name=”Grid”>
                                                    <Rectangle x:Name=”Background” RadiusX=”3″ RadiusY=”3″ Stroke=”{TemplateBinding BorderBrush}” Fill=”{TemplateBinding BorderBrush}”/>
                                                    <ContentPresenter HorizontalAlignment=”{TemplateBinding HorizontalContentAlignment}” Margin=”{TemplateBinding Padding}” VerticalAlignment=”{TemplateBinding VerticalContentAlignment}” RecognizesAccessKey=”True”/>
                                                </Grid>
                                                <ControlTemplate.Triggers>
                                                    <Trigger Property=”IsPressed” Value=”true”>
                                                        <Setter Property=”Fill” Value=”{StaticResource BackgroundPressBrush}” TargetName=”Background”/>
                                                    </Trigger>
                                                </ControlTemplate.Triggers>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </Grid.Resources>

Another small touch – if we’ll AllowTransparency and set WindowStyle within our resource dictionary or it’s style we’ll get an exception “Cannot change AllowsTransparency after Window has been shown.” The reason is simple – styles applies after the window handler created, thus we can not change transparency within template. Thus WindowStyle none cannot be applied there as well. We’ll put it into default constructor.

public VKeyboard()
{
    this.AllowsTransparency = true;
    this.WindowStyle = WindowStyle.None;           
}

After we done with the control, let’s start to build business logic. First of all routed commands

public static RoutedUICommand ButtonCallPressedCommand = new RoutedUICommand(“Call”,”Call”,typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.Enter) } ));
        public static RoutedUICommand ButtonEndPressedCommand = new RoutedUICommand(“End call”, “End call”, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.Delete), new KeyGesture(Key.Back) }));
        public static RoutedUICommand Button1PressedCommand = new RoutedUICommand(“1″, “1″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad1)/*, new KeyGesture(Key.D1)*/ }));
        public static RoutedUICommand Button2PressedCommand = new RoutedUICommand(“2″, “2″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad2)/*, new KeyGesture(Key.D2)*/  }));
        public static RoutedUICommand Button3PressedCommand = new RoutedUICommand(“3″, “3″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad3)/*, new KeyGesture(Key.D3)*/  }));
        public static RoutedUICommand Button4PressedCommand = new RoutedUICommand(“4″, “4″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad4)/*, new KeyGesture(Key.D4)*/  }));
        public static RoutedUICommand Button5PressedCommand = new RoutedUICommand(“5″, “5″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad5)/*, new KeyGesture(Key.D5)*/  }));
        public static RoutedUICommand Button6PressedCommand = new RoutedUICommand(“6″, “6″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad6)/*, new KeyGesture(Key.D6) */ }));
        public static RoutedUICommand Button7PressedCommand = new RoutedUICommand(“7″, “7″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad7)/*, new KeyGesture(Key.D7)*/  }));
        public static RoutedUICommand Button8PressedCommand = new RoutedUICommand(“8″, “8″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad8)/*, new KeyGesture(Key.D8) */ }));
        public static RoutedUICommand Button9PressedCommand = new RoutedUICommand(“9″, “9″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad9)/*, new KeyGesture(Key.D9)*/  }));
        public static RoutedUICommand Button0PressedCommand = new RoutedUICommand(“0″, “0″, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.NumPad0)/*, new KeyGesture(Key.D0)*/  }));
        public static RoutedUICommand ButtonStarPressedCommand = new RoutedUICommand(“Star”, “Star”, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.Multiply)/*, new KeyGesture(Key.D8, ModifierKeys.Shift)*/ }));
        public static RoutedUICommand ButtonHashPressedCommand = new RoutedUICommand(“Hash”, “Hash”, typeof(Button),
            new InputGestureCollection(new KeyGesture[] { new KeyGesture(Key.Divide)/*, new KeyGesture(Key.D3,ModifierKeys.Shift)*/ }));

As you can see, we have small problem here. For some reason Key.D… gestures cannot be use in WPF. The reason is silly, but currently there is no workaround for it. I’ll think about this issue and maybe I’ll find kind’of trick to do. For now, we’ll just leave it as is and create command bindings.

CommandBinding bCall = new CommandBinding(ButtonCallPressedCommand, ExecutedButtonPressedCommand);
            CommandBinding bEnd = new CommandBinding(ButtonEndPressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b1 = new CommandBinding(Button1PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b2 = new CommandBinding(Button2PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b3 = new CommandBinding(Button3PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b4 = new CommandBinding(Button4PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b5 = new CommandBinding(Button5PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b6 = new CommandBinding(Button6PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b7 = new CommandBinding(Button7PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b8 = new CommandBinding(Button8PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b9 = new CommandBinding(Button9PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding b0 = new CommandBinding(Button0PressedCommand, ExecutedButtonPressedCommand);
            CommandBinding bStar = new CommandBinding(ButtonStarPressedCommand, ExecutedButtonPressedCommand);
            CommandBinding bHash = new CommandBinding(ButtonHashPressedCommand, ExecutedButtonPressedCommand);

            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), bCall);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), bEnd);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b1);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b2);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b3);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b4);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b5);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b6);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b7);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b8);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b9);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), b0);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), bStar);
            CommandManager.RegisterClassCommandBinding(typeof(VKeyboard), bHash);

We’ll create Dependency Property to hold dialed number and add routed event to handle dial press

public string DialedNumber
        {
            get { return (string)GetValue(DialedNumberProperty); }
            private set { SetValue(DialedNumberPropertyKey, value); }
        }

        private static readonly DependencyPropertyKey DialedNumberPropertyKey =
            DependencyProperty.RegisterReadOnly(“DialedNumber”, typeof(string), typeof(VKeyboard), new UIPropertyMetadata(default(string)));
        public static readonly DependencyProperty DialedNumberProperty = DialedNumberPropertyKey.DependencyProperty;

        public static readonly RoutedEvent CallEvent = EventManager.RegisterRoutedEvent(“Call”, RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(VKeyboard));

        public event RoutedEventHandler Call
        {
            add { AddHandler(CallEvent, value); }
            remove { RemoveHandler(CallEvent, value); }
        }

        void RaiseCallEvent()
        {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(VKeyboard.CallEvent);
            RaiseEvent(newEventArgs);
        }

So far, so good. Now the turn of complicated things. Let’s treat our attached event.

public static bool GetAttachVKeyboard(DependencyObject obj)
        {
            return (bool)obj.GetValue(AttachVKeyboardProperty);
        }

        public static void SetAttachVKeyboard(DependencyObject obj, bool value)
        {
            obj.SetValue(AttachVKeyboardProperty, value);
        }

        public static readonly DependencyProperty AttachVKeyboardProperty =
            DependencyProperty.RegisterAttached(“AttachVKeyboard”, typeof(bool), typeof(VKeyboard), new UIPropertyMetadata(default(bool), AttachVKeyboardPropertyChanged));

        static void AttachVKeyboardPropertyChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
        {

AttachVKeyboardPropertyChanged handler occurs when someone changes the value. So, we can get the requester from this handler and work with it. We’ll check what the type of the sender and do whatever we should do.

If the sender is TextBox we know what to do

if (s is TextBoxBase)
            {
                TextBoxBase control = s as TextBoxBase;
                if ((bool)e.NewValue)
                {
                    control.AddHandler(TextBoxBase.GotFocusEvent, new RoutedEventHandler(OnHostFocused), true);
                    control.AddHandler(TextBoxBase.LostFocusEvent, new RoutedEventHandler(OnHostUnFocused), true);
                }
                else
                {
                    control.RemoveHandler(TextBoxBase.GotFocusEvent, new RoutedEventHandler(OnHostFocused));
                    control.RemoveHandler(TextBoxBase.LostFocusEvent, new RoutedEventHandler(OnHostUnFocused));
                }
            }

However, it is it not we should run recursively and find all textboxes inside it. We have two types of containers – Panels (where there is a number of children) and Decorators (where there is only one). Another problem, that when the control changes the value it still has no children inside. Other works it is not loaded. We should know it to get appropriate action after the sender will load itself with all dependencies. So the code will looks like this:

if (s is Panel)
            {
                Panel p = s as Panel;
                if (p.IsLoaded)
                {
                    OnMultiHostLoaded(p, null);
                }
                else
                {
                    p.AddHandler(Panel.LoadedEvent, new RoutedEventHandler(OnMultiHostLoaded), true);
                }
            }
            else if (s is Decorator)
            {
                Decorator d = s as Decorator;
                if (d.IsLoaded)
                {
                    OnSingleHostLoaded(d, null);
                }
                else
                {
                    d.AddHandler(Panel.LoadedEvent, new RoutedEventHandler(OnSingleHostLoaded), true);
                }
            }

Now, the only thing we should do it to look into it’s children and attach there (we wont forget to remove handlers)

static void OnSingleHostLoaded(object s, RoutedEventArgs e)
        {
            Decorator d = s as Decorator;
            bool val = GetAttachVKeyboard(d);
            DependencyPropertyChangedEventArgs ev = new DependencyPropertyChangedEventArgs(VKeyboard.AttachVKeyboardProperty, !val, val);
            AttachVKeyboardPropertyChanged(d.Child, ev);
            d.RemoveHandler(Decorator.LoadedEvent, new RoutedEventHandler(OnSingleHostLoaded));

        }

        static void OnMultiHostLoaded(object s, RoutedEventArgs e)
        {
            Panel p = s as Panel;
            bool val = GetAttachVKeyboard(p);
            DependencyPropertyChangedEventArgs ev = new DependencyPropertyChangedEventArgs(VKeyboard.AttachVKeyboardProperty, !val, val);
            for (int i = 0; i < p.Children.Count; i++)
            {
                AttachVKeyboardPropertyChanged(p.Children[i], ev);
            }
            p.RemoveHandler(Panel.LoadedEvent, new RoutedEventHandler(OnMultiHostLoaded));
        }

Done, let’s treat Focus and Unfocus of Textboxes. We have only one keyboard at one time, thus, we’ll make it singleton. Also we should know who is the client of the keypad.

static void OnHostFocused(object s, RoutedEventArgs e)
        {
            TextBox tb = s as TextBox;
            if (CurrentKeyboard == null)
            {
                CurrentKeyboard = new VKeyboard();
                tb.Unloaded += new RoutedEventHandler(tb_Unloaded);
                CurrentKeyboard.HookToHandle((HwndSource)PresentationSource.FromVisual(tb));
            }
            CurrentKeyboard.SetValue(VKeyboard.DialedNumberPropertyKey, tb.Text);
            Binding b = new Binding();
            b.Source = CurrentKeyboard;
            b.Path = new PropertyPath(VKeyboard.DialedNumberProperty);
            b.Mode = BindingMode.OneWay;
            tb.SetBinding(TextBox.TextProperty, b);
            CurrentKeyboard.Client = tb;
        }

Very similar thing happens on unfocus.

static void OnHostUnFocused(object s, RoutedEventArgs e)
        {
            TextBox tb = s as TextBox;
            string str = tb.Text;
            BindingOperations.ClearBinding(tb,TextBox.TextProperty);
            tb.Text = str;
            if (CurrentKeyboard != null)
            {
                CurrentKeyboard.DialedNumber = default(string);
                CurrentKeyboard.Client = null;
            }
        }

Now we should take a look into the location of our virtual keypad. There are some problems with it. One – we should translate the location of client textbox into screen coordinates, due to fact, that we should place another window. FrameworkElement.PointToScreen will do the work.

Another problem is what happen when the whole window moving? How we’ll synchronize the position of virtual upper window with underlying one? Small “unsafe” trick with old good WinProc will help us

internal void HookToHandle(HwndSource source)
        {
            source.AddHook(new HwndSourceHook(WindowProc));
        }

System.IntPtr WindowProc(
              System.IntPtr hwnd,
              int msg,
              System.IntPtr wParam,
              System.IntPtr lParam,
              ref bool handled)
        {
            switch (msg)
            {
                case 0×0003:/* WM_MOVE  */
                    setPosition();
                    break;
            }

            return IntPtr.Zero;
        }

void setPosition()
        {
            if (m_client != null)
            {

                Point p = new Point(0, m_client.ActualHeight + 2);
                Point sp = m_client.PointToScreen(p);
                this.Left = sp.X;
                this.Top = sp.Y;
                this.Show();
            }
            else
            {
                this.Hide();
            }
        }

We done. Have a nice accessible day with your software and nice fully managed virtual keyboard in WPF.

Source code for this article.

RTFM – Not only software needs manual, kids need it too – How to care your child (visual instructions)

For all my friends, become fathers and mothers within last couple of months. Please, read it before using your kids! All images are from Safe Baby Handling Tips (more fun stuff inside) by Running Press Books. Author’s website

kidcare kidcare (2) kidcare (3)

There are more images here, we just waiting for author’s authorization to publish it.

How to merge help files to appear inside Visual Studio?

How many times after an installation of some SDK, you saw, that nothing has been merged into Visual Studio help. You press F1 and nothing happens? What to do? Actually, the answer is really stupid. You should go into Visual Studio Documentation (Programs->Visual Studio [number]->Visual Studio [number] documentation), go to Index and set filter by property to (unfiltered) and then look for collection manager->help

image

There you will find all help files installed in your system and now, you can merge (or unmerge) them manually by marking relevant checkboxes and clicking "Update VSCC" button.

This is really simple, but for me it looks a bit strange, that in order to do it, you should not go to any of options, but search into help context. However, this is the real live :)

Declaimer: this is not internal or private information of Microsoft Corporation. You can reference to online MSDN library to find small information pieces about this issue. Here one of such pieces (look for note section)

Quick WPF tip: Alternative text for images

In HTML we have Alt tag, that provides alternative text for images (in case, that image is not exists), but what to do in XAML? Simple, use the power of DataTemplates and DataTriggers. In following example, I have data item with following properties Logo, which is Bitmap, Name and Description, that strings. I want to display Name text, in case, I have or want to disable no logo bitmap. Following code.

    
<DataTemplate x:Key="tmpProvider">
<
Grid>
<
TextBlock TextBlock.FontWeight="Bold" Text="{Binding Path=Name}" Visibility="Collapsed" Name="AltText"/>
<
Image Stretch="UniformToFill" Source="{Binding Path=Logo}" Name="Image" TextSearch.Text="{Binding Path=Name, Mode=OneWay}"/>
<
Grid.ToolTip>
<
StackPanel>
<
TextBlock Text="{Binding Path=Name}"/>
<
TextBlock Text="{Binding Path=Description}"/>
</
StackPanel>
</
Grid.ToolTip>
</
Grid>
<
DataTemplate.Triggers>
<
DataTrigger Binding="{Binding Path=Logo}" Value="{x:Null}">
<
Setter TargetName="AltText" Property="Visibility" Value="Visible"/>
</
DataTrigger>
</
DataTemplate.Triggers>
</
DataTemplate></PRE< P>

 

Simple, isn’t it? Thank all, those where 10 seconds about another accessibility pattern in WPF

Accessibility lesson (video)

You are not sure about what is accessibility at general. Please see following video to understand.

⟨ , ,  ⟩

WPF + UISpy = Accessibility testing

Well, some of Doron’s scripts make commenting to this post(hebrew) disabled, however today I want to write about WPF accessibility issues. Someone who writes code really know what is it? I’m pretty sure, that do not. Let’s make small test case. Execute regular WinForms application and Accessibility Speech engine (you can find it under Start->Accessories->Ease of Access->Narrator). Try to open calculator and go to application menu. You’ll hear something like: “Menu Item Edit. Three menu items. Copy Menu Item, shorkey Control C”. Where it comes from. Does narrator understand every control? How to know or control what should say? Open SpyUI from Windows SDK. Find “Calculator” node in elements tree and expand it. You’ll find everything that Narrator speaking. This discovered by windows API in regular win forms.

image

Well. Now let’s create simple WPF application with menu bar, button and listbox and let UISpy detect it.

image

Well, it works. But not so good. “Edit” menu item has Copy menuitem inside, but UISpy do not know about it. It’s detects the containment of listbox, so it works until we have no data binded. Let’s bind and see what we have.

image

Please see, that instead of what we actually see, there are System.Xml.XmlElement items are displayed. Actually, UISpy is right. We are using data, that WPF engine renders into UI. But we can not leave it this way. What to do? Make it accessible. Here the way.

First of all, we have to set TextSearch.TextPath property to our actual text. Next, override ItemContainerStyle to set its AutomationProperties.Name value to be binded to the source of text, we want to make accessible. Here the code.

    <ListBox ItemTemplate="{StaticResource template}" 
             DataContext="{StaticResource data}" ItemsSource="{Binding}"
TextSearch.TextPath="@value">
        <ListBox.ItemContainerStyle>
          <Style>
            <Setter Property="AutomationProperties.Name" Value="{Binding XPath=@value}"/>
          </Style>
        </ListBox.ItemContainerStyle>
      </ListBox>

 

If you’ll bind Automation Name property inside data template it will not work, ‘cos for real WPF engine do not render text objects, so the only way to do it is by overriding those properties externally. How we can tell? Right property in wrong place :)

And here the result of such approach in UISpy. Now you can run Narrator or any other accessibility application and everything will work fine.

image

The conclusions are as following:

1) UISpy is extremely important tool for accessibility check
2) WPF is not very friendly from this point of view in current version, but I’m sure, that next release will fix those issues.

Recommended

 

Sponsor


Partners

WPF Disciples
Dreamhost
Code Project