How to show floating virtual keyboard (user contro

2019-07-27 02:13发布

问题:

I have been doing development work in WPF application which uses an MVVM pattern for a couple of days now. I'm very new to WPF and MVVM pattern as well.

In my scenario, I have a user control view (named EPayView.xaml) which has a textbox that will accept a phone number. The view has a corresponding viewmodel (named EPayViewModel.cs). In the MainWindow.xaml, I have a user control (floating virtual keyboard) which is derived from namespace controls WpfKb.Controls. The MainWindow.xaml also has a corresponding viewmodel (named MainViewModel.cs)

Having said that, I have done research on how to use attached dependency properties which lead me to this solution. Set focus on textbox in WPF from view model (C#) which I believe this is where I could bind the property IsFocused in the textbox of EPayView.xaml.

Below are the codes that I have already incorporated in my solution.

EpayView.xaml (textbox xaml markup)

<TextBox Text="{Binding PhoneNo}" Grid.Row="5" Margin="10,0,10,0" VerticalContentAlignment="Center" FontSize="12" x:Name="Email" behaviors:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>

MainWindow.xaml (xaml markup)

<Window x:Class="SmartPole540.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
    xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
    xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
    xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
    xmlns:square="clr-namespace:SmartPole.View.Square;assembly=SmartPole.View"
    xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
    Title="CitiPulse" 
    WindowStartupLocation="Manual"
    PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
    Name="mainWindow">

    <userControls:RollPanel.BottomContent>
        <square:SquareView Canvas.Top="1010" DataContext="{Binding DataContext.SquareViewModel, 
            RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type userControls:RollPanel}}}"/>
    </userControls:RollPanel.BottomContent>

    <controls:FloatingTouchScreenKeyboard
            x:Name="floatKb" Width="500" Height="250" PlacementTarget="{Binding ElementName=MainGrid}"
            Placement="Center" AreAnimationsEnabled="False" Visibility="Visible"
            IsOpen="{Binding IsChecked, ElementName=kbButton}"/>
</Window>

In the above code, the user control RollPanel.BottomContent host the EPayView.xaml view inside another view which is RollPanel.xaml

EpayViewModel.cs contains the static class FocusExtension for the IsFocused attached property (refer to this solution - Set focus on textbox in WPF from view model (C#)). And, EPayViewModel.cs already implemented INotifyPropertyChanged which is wrapped inside a concrete class ObservableObject that accepts type of T. This is also same with MainViewModel.cs

public class EPayViewModel : ObservableObject<EPayViewModel>, IPaymentViewModel, IActiveViewModel
{ ... }

public class MainViewModel : ObservableObject<MainViewModel>
{ ... }

As such, my goal is that when the textbox in EPayView.xaml has the focus, the floating virtual keyboard (floatKb) in the MainWindow.xaml will be shown.

I'm stuck on how to proceed (I was thinking if a call to FocusExtension static class in EPayViewModel inside my MainViewModel.cs will suffice?), any help is greatly appreciated.

Cheers,

回答1:

As AnjumSKhan already said, to react to some event in a MVVM way, you'll have to use Command. Command can be called within an EventTrigger, you will need to add a Reference to System.Windows.Interactvity component. Let's assume you have a simple View and View Model and you need to show this View when the TextBox in a MainWindow got focus.

View (NewWindow.xaml)

<Window x:Class="My.NewWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="NewWindow" Height="300" Width="300">
<TextBlock Text="{Binding Message}"/>

View Model

public class NewWindowViewModel
{
    private string _message;
    public string Message
    {
        get { return _message; }
        set { _message = value; }
    }
}

You also have a MainWindow, it is a main view for an app and it contains the target TextBox. You may see that there is an EventTrigger added to the TextBox and it has a property InvokeCommandAction which is binded to the MainWindowViewModel's command called ShowCommand.

Main Window

<Window x:Class="My.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="350" Width="525">
<TextBox Height="40" Text="{Binding Text}">
    <Interactivity:Interaction.Triggers>
        <Interactivity:EventTrigger EventName="GotFocus">
            <Interactivity:InvokeCommandAction Command="{Binding ShowCommand}"/>
        </Interactivity:EventTrigger>
    </Interactivity:Interaction.Triggers>
</TextBox>

In the Show method of MainWindowViewModel NewWindow view is created and got new NewWindowViewModel instance as a DataContext. RelayCommand class is presented in my answer to this question

MainWindowViewModel

public class MainWindowViewModel
{
    private string _text;
    public string Text
    {
        get { return _text; }
        set { _text = value; }
    }

    private ICommand _increaseCommand;
    public ICommand ShowCommand
    {
        get
        {
            if (_increaseCommand == null)
            {
                _increaseCommand = new RelayCommand(
                p => true,
                Show);
            }
            return _increaseCommand;
        }
    }

    private void Show(object obj)
    {
        var w = new NewWindow();
        var nvm = new NewWindowViewModel();
        nvm.Message = "Test";
        w.DataContext = nvm;
        w.Show();
    }
}

What is left is to create a new MainWindowViewModel and setup a DataContext for MainWindow.

public MainWindow()
{
    InitializeComponent();
    var mvm = new MainWindowViewModel();
    mvm.Text = "Focus me!";
    DataContext = mvm;
}

Hope it will help.



标签: wpf xaml mvvm