Keyboard overlaps textbox - c#

I'm using a list of textboxes for a registering document in a WP8 app.
The number of textboxes is quite large, so the user has to scroll between them.
To navigate between one field to another, I added two applicationbarIcons, next and previous. Pressing on next will change the focus to the next textbox from list, and scroll the content of the scroll viewer with the height of the textbox (in this case 50).
However, sometimes, when switching the focus to the element bellow, the keyboard covers the text box. (the content doesn't scroll up).
Is there a way to force the textbox to move above the keyboard, even if it is in a scroll view?
<ScrollViewer x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<TextBlock Text="{Binding Source={StaticResource LocalizedStrings}, Path=LocalizedResources.STRING_CONTACT}" Margin="10,5" FontWeight="SemiBold" Foreground="#878780"></TextBlock>
<StackPanel Margin="10,5" Height="190" Background="#F4F3F4">
<TextBox LostFocus="firstNameTxt_LostFocus_1" GotFocus="firstNameTxt_GotFocus_1" Margin="0,-7" FontSize="23" x:Name="firstNameTxt" BorderThickness="0" Background="Transparent" InputScope="PersonalFullName"><TextBox>
<TextBox LostFocus="firstNameTxt_LostFocus_1" GotFocus="firstNameTxt_GotFocus_1" Margin="0,-7" FontSize="23" x:Name="lastNameTxt" BorderThickness="0" Background="Transparent" InputScope="PersonalFullName"></my:DefaultTextBox>
<TextBox LostFocus="firstNameTxt_LostFocus_1" GotFocus="firstNameTxt_GotFocus_1" Margin="0,-7" FontSize="23" x:Name="MobileTxt" BorderThickness="0" InputScope="Number" Background="Transparent" ></TextBox>
<TextBox LostFocus="firstNameTxt_LostFocus_1" GotFocus="firstNameTxt_GotFocus_1" Margin="0,-7" FontSize="23" x:Name="EmailTxt" BorderThickness="0" Background="Transparent">
</StackPanel>
</ScrollViewer>
Code behind:
void left_Click(object sender, EventArgs e)
{
int index = this.controls.IndexOf(currentControl) - 1;
if (index == -1)
{
this.Focus();
return;
}
currentControl = this.controls[index];
ContentPanel.ScrollToVerticalOffset(ContentPanel.VerticalOffset - 50);
currentControl.Focus();
}

This is a common issue on WP8. When a textbox is focused, it will translate Application 's RootVisual to bring it into view. This doesn't work well in some cases (when clipboard is on, or in your case). A workaround is manually translating RootVisual to a desired vertical offset on GotFocus and LostFocus events of TextBox.
private void TranslateRootVisualY(int yNew)
{
var rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
rootFrame.RenderTransform = new CompositeTransform() {TranslateY = yNew};
}
In your case, you can eliminate the automatic translation and make ScrollViewer scroll to desired offset in GotFocus event:
private void firstNameTxt_GotFocus_1(object sender, RoutedEventArgs e)
{
TranslateRootVisualY(0);
Dispatcher.BeginInvoke(() =>{
double destOffset;
//...calculate destination offset
ContentPanel.ScrollToVerticalOffset(destOffset);
});
}
destOffset can be calculated from sender and other function like GetRectFromCharacterIndex

Related

Commandbar not getting focus -- gotta use keyboard

My little project is progressing well, however I'm stumbling over something which is probably stupid...
Somehow, when I open the application, nothing gets focus, I have to use the "tab" key to be able to move the focus to the commandbar and to be able to use the keyboard shortcuts.
And then....
WHen I use the Scrollviewer to move the image or zoom, I cannot use the keyboard shortcuts again, until I use the "tab" to move it to the commandbar.
I've tried
cmdbar.Focus(FocusState.Programmatic);
a bit everywhere in the app where I think that it may be useful, to no avail. I've also tried to use the Keyboard Accelerators, but it does not help. Any tips?
Here is my XAML code:
<Page.Resources>
<DataTemplate x:Key="myResourceTemplate">
<TextBlock Text="{Binding}" MaxHeight="10" FontSize="8" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" LineHeight="9" Height="Auto" />
</DataTemplate>
</Page.Resources>
<Page.BottomAppBar>
<CommandBar x:Name="cmdbar" ClosedDisplayMode="Compact" HorizontalAlignment="Left" HorizontalContentAlignment="Left" VerticalAlignment="Center" KeyUp="kb_openkey" Opacity="1" Visibility="Visible" Background="#260000FF">
<CommandBar.Content>
<Grid/>
</CommandBar.Content>
<AppBarButton Icon="ZoomIn" Label="AppBarButton" Tapped="Zoomin_Click"/>
<AppBarButton Icon="ZoomOut" Label="AppBarButton" Tapped="Zoomout_Click"/>
<AppBarToggleButton x:Name="randomstatus" Icon="Shuffle" Label="Random" Tapped="Togglerandom"/>
<... a bunch of other buttons >
</CommandBar>
</Page.BottomAppBar>
<Grid x:Name="imggrid" Background="Black" BorderBrush="Black" KeyUp="kb_openkey">
<ScrollViewer x:Name="imageView_scroller" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled" ZoomMode="Enabled" RequestedTheme="Dark" KeyUp="kb_openkey">
<Image x:Name = "ctlImage" Grid.Column ="0" VerticalAlignment = "Stretch" HorizontalAlignment = "Stretch" Stretch = "Uniform"
PointerWheelChanged="ctlImage_PointerWheelChanged"
ManipulationMode = "TranslateX, TranslateY, Scale"
ManipulationStarted = "ctlImage_ManipulationStarted"
ManipulationDelta = "ctlImage_ManipulationDelta"
ManipulationCompleted = "ctlImage_ManipulationCompleted"
KeyUp="kb_openkey"
>
<Image.RenderTransform>
<CompositeTransform x:Name="image_Transform" ></CompositeTransform >
</Image.RenderTransform >
</Image>
</ScrollViewer>
</Grid>
And here is how I handle keyboard input:
void kb_openkey(object sender, KeyRoutedEventArgs e)
{
if ((int)e.Key >= 1 && (int)e.Key <= 255)
{
switch ((int)e.Key)
{
case 70: //A
....dothis....;
break;
case 65: //A
.... dothat....;
break;
}
}
}
you do not need to set focus in order to use KeyboardAccelrators as shortcuts. Hence you do not need the keyup or keydown events on your image or command bar, unless they have some other task irrelevant of setting focus.
you should use KeyBoardAccelrators in your command bar with AccessKey and any options modifiers like Ctrl or Shift
an example of AccessKey on AppBarButton
<AppBarButton
Icon="Copy"
Label="Copy"
ToolTipService.ToolTip="Copy (Ctrl+C)"
Click="OnCopy"
AccessKey="C">
<AppBarButton.KeyboardAccelerators>
<KeyboardAccelerator
Modifiers="Control"
Key="C" />
</AppBarButton.KeyboardAccelerators>
</AppBarButton>
you can find more details in the link of docs I provided above.
Update :
when you tap on another UI element the focus from previous element is removed automatically, you do not need a KeyUp event on your image as well as commandbar, you just just use a global CoreWindow.KeyDown which can help you in any key related commands you want to accomplish
#touseefbsb, very useful!!! Thanks! This handles the key no matter what has got focus and is being clicked on.
So my code is, for reference:
In XAML, in the page section, add:
Loaded="initkbd"
Unloaded="unloadkbd"
And in the C# portion, add:
//Add the key handler method to the KeyDown handlers
private void initkbd(object sender, RoutedEventArgs e)
{
Window.Current.CoreWindow.KeyDown += kb_openkey;
cmdbar.Content = "Added to keys";
}
//Remove the keyhandler method from the list
private void unloadkbd(object sender, RoutedEventArgs e)
{
Window.Current.CoreWindow.KeyDown -=kb_openkey;
}
and then, the key handler looks like this:
private void kb_openkey(CoreWindow sender, KeyEventArgs e)
{
//Mark the event as handled
e.Handled = true;
int keypressed = (int) e.VirtualKey;
//Than handle the key, based on its keycode
if ((int)keypressed >= 1 && (int)keypressed <= 255)
{
switch (keypressed)
{
case 70: //F
//do something when F is presed
break;
case 76: //L dialog to show items
//Do something when L is pressed
break;
}
}
}

How can I drag and drop controls using a common method?

I have the following XAML code, which creates two stack panels within a big parent Stack Panel. I would like to be able to drag each small stack panel within the parent bigStack panel.
XAML
<StackPanel BorderThickness="1" BorderBrush="Black" x:Name="bigStack">
<StackPanel x:Name="smallStack1" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" ManipulationStarting="objectManipulationStarting">
<TextBlock Text="John Doe"/>
<TextBlock Text="CEO"/>
</StackPanel>
<StackPanel x:Name="smallStack2" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" ManipulationStarting="objectManipulationStarting">
<TextBlock Text="Jane Doe"/>
<TextBlock Text="CTO"/>
</StackPanel>
</StackPanel>
C# backend:
private TranslateTransform dragtranslation ;
private void objectManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
dragtranslation.X += e.Delta.Translation.X;
dragtranslation.Y += e.Delta.Translation.Y;
}
private void objectManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
{
var stackDragged = e.OriginalSource as StackPanel;
dragtranslation = new TranslateTransform();
stackDragged.RenderTransform = this.dragtranslation ;
}
Original Code found here (Official Microsoft UWP Documentation) but adapted (obviously wrongly) to suit my needs
PROBLEM 1
1) Drag smallStack1 for the first time: OK
2) Drag smallStack2 for the second time: Reverts back to the original position
PROBLEM 2
1) Drag smallStack1 for the first time: OK
2) Drag smallStack2 for the first time: OK
3) Drag either of the smallStacks again: Reverts back to the original position
You can check the problems in the .gif below:
WHAT I WISH TO ACCOMPLISH
Drag the controls using a common method, because I plan to dynamically create more controls inside the bigStack panel.
You are basically reinstantiating TranslateTransform everytime you click on an Item. That is the reason why when you click on the item second time, it navigates back to 0,0 which is the original position for TranslateTransform.
To Handle this in a easier way, this is what I would do.
1) I would give explicit TranslateTransform to the smallStackPanel's
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel BorderThickness="1" BorderBrush="Black" x:Name="bigStack">
<StackPanel x:Name="smallStack1" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" >
<StackPanel.RenderTransform>
<TranslateTransform />
</StackPanel.RenderTransform>
<TextBlock Text="John Doe"/>
<TextBlock Text="CEO"/>
</StackPanel>
<StackPanel x:Name="smallStack2" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" >
<StackPanel.RenderTransform>
<TranslateTransform />
</StackPanel.RenderTransform>
<TextBlock Text="Jane Doe"/>
<TextBlock Text="CTO"/>
</StackPanel>
</StackPanel>
</Grid>
And then all i need to do in codebehind is handle ManipulationDelta.
private void objectManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var stackDragged = e.OriginalSource as StackPanel;
(stackDragged.RenderTransform as TranslateTransform).X += e.Delta.Translation.X;
(stackDragged.RenderTransform as TranslateTransform).Y += e.Delta.Translation.Y;
}
Output:
Update
To add TranslateTransform from Code
StackPanel sp = new StackPanel();
sp.RenderTransform = new TranslateTransform();
Good Luck.

Dynamically added controls in stackpanel is not visible in wpf c#

I am dynamically adding textboxes based on a button click inside the stackpanel.But the textboxes are not visible in the UI .
Here is the code used for creating textboxs inside stackpanel.
public void GenerateControls()
{
TextBox txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";
txtNumber.Background= Brushes.Red;
panel1.Children.Add(txtNumber);
}
why its not visible..??and here is the XAML part of stackpanel
<StackPanel Name="panel1" Grid.Column="1" HorizontalAlignment="Left" Height="151" Margin="427,60,0,0" Grid.Row="2" VerticalAlignment="Top" Width="216">
<StackPanel Height="144">
</StackPanel>
</StackPanel>
If you are going to be adding controls dynamically, do not restrict the height (or even width) of the container you are adding to.
Update your XAML to have auto height/width.
<StackPanel Name="panel1"
Grid.Column="1"
Height="Auto"
Width="Auto"
Margin="427,60,0,0"
Grid.Row="2"
VerticalAlignment="Top"
HorizontalAlignment="Left" >
<StackPanel Height="144">
</StackPanel>
</StackPanel>
Also, once you add a new child, make sure you are updating the StackPanel layout.
public void GenerateControls()
{
TextBox txtNumber = new TextBox();
txtNumber.Name = "txtNumber";
txtNumber.Text = "1776";
txtNumber.Background= Brushes.Red;
panel1.Children.Add(txtNumber);
panel1.UpdateLayout();
}
In your xaml code, there is a stackpanel in your 'panel', it will be the 1st child of 'panel'.
And its height is 144px. your 'panel1' is 151 px.
So when you add textboxes into 'panel', they will be displayed behind the 144px stackpanel.
There is only 7px to display them. So they will not display on your window.

How to make the canvas detect touch input properly in C#?

Basically, I'm trying to make the canvas listen for a touch input (tap) and will increment the number of taps on screen. It isn't working when I touch the screen on my device. I debugged my code and nothing seems out of the ordinary except that the touch is not detected. I checked ZIndex and the canvas is in front of the screen to be touchable. How do I make it work?
XAML:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Margin="188,10,187,397"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="-18,535,-18,0" VerticalAlignment="Top" Click="Button_Click"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
</Grid>
C#:
int taps = 0; // create var to detect number of times, user touches the screen
// Constructor
public MainPage()
{
InitializeComponent();
}
// method to register the touch as the finger is placed on the screen
private void Canvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
counter.Text = "TOUCHED!";
}
//method register the touch as the finger is lifting up from the screen
private void Canvas_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
taps++;
counter.Text = taps.ToString(); //convert var from int to string
}
//method register the touch as the finger leaves the area of the screen
private void Canvas_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
//Canvas c = sender as Canvas;
MessageBox.Show("You left the screen without lifting your finger. That does not count as a tap!", "Caution!", MessageBoxButton.OK);
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
I don't know why you want to do it with Canvas - it won't work as you have nothing in this Canvas, so it can't register your click/tap, Canvas is also hard to adjust to screen. I think it can be done simpler way if you want to do it with MouseUp/Down - subscribe directly to Grid containing your elements instead of filling this Grid with additional Canvas:
In XAML:
<Grid x:Name="ContentPanel" Margin="12,0,12,0" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="7*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Grid.Row="0"/>
<Button Content="Reset" HorizontalAlignment="Stretch" VerticalAlignment="Center" Click="Button_Click" Grid.Row="1"/>
<TextBlock Name="Touched" FontSize="50" HorizontalAlignment="Center" TextWrapping="Wrap" Text="Touched" VerticalAlignment="Center" Visibility="Collapsed" Grid.Row="2"/>
</Grid>
In code behind:
private int taps = 0;
public MainPage()
{
InitializeComponent();
ContentPanel.MouseLeftButtonDown += ContentPanel_MouseLeftButtonDown;
ContentPanel.MouseLeftButtonUp += ContentPanel_MouseLeftButtonUp;
}
private void ContentPanel_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
taps++;
counter.Text = taps.ToString(); //convert var from int to string
Touched.Visibility = Visibility.Collapsed;
}
private void ContentPanel_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Touched.Visibility = Visibility.Visible;
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
As you can see I've subscribed to Grid events (which covers whole screen) - but to make it work I had to set its Background Brush to Transparent, otherwise it will work only if you touch text.
There are many other ways to make your App work, but I hope this will help.
Is there a reason why you don't use the touch-events?
Instead of using MouseLeftButtonDown and MouseLeftButtonUp you should use TouchDown and TouchUp.
Only when you don't handle the touch events or the manipulation events they will be mapped to mouse events. In my experience with touch a single tap also not always gets mapped to MouseLeftButtonDown. As far as I know you could also with mouse events only recoginse one finger. When you want to count more fingers it's necessary to use the TouchDown/TouchUp events.
The problem lies in the overlapping style of the grid
so either make grid rows or define a stackpanel inside the grid, something like this.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<stackpanel>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" Margin="0,0,0,0"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</stackpanel>
</Grid>
Try and check now.
You should set your Background property. If you don't want any background set it to Transparent:
<Canvas ZIndex="99" Background="Transparent" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" Tapped="Canvas_CountMyTaps"/>
(If you want the canvas to be on Top be sure to make it have a greater ZIndex than the other elements that it overlaps)
If not set (the default value is null) the element won't capture any taps/click etc, it will be as if they "fall through".
Also, consider using the Tapped event which is a "higher level" event that will respond to clicks, taps with the finger, stylus, etc.

Conditional TextBlock formatting

I am new to both C# and XAML and I am making some sort of reading application.
So I need a TextBlock that word wraps if the title needs more than 1 row to fit. But when it becomes more that 2 rows to fit, wrap a ScrollView on it.
By doing this I could align the rest element tightly whenever it is either 1 or 2(max) row height.
How do I achieve this in XAML or C#?
If you can use a TextBox instead of a TextBlock, it would be easier. A TextBox supports scrolling and has a LineCount property that you can key off of. So for example, I put the a few controls into a StackPanel:
<Grid>
<StackPanel HorizontalAlignment="Left" Height="100" Margin="105,127,0,0" VerticalAlignment="Top" Width="184">
<TextBox Height="23" TextWrapping="Wrap" Text="TextBox" Name="TextBox1"/>
<Button Content="Button" Click="Button_Click_2"/>
</StackPanel>
</Grid>
Then I had some code to update the text. When I hit 2 lines, I grew the TextBox and when I got to three lines, I added scrollbars:
private void Button_Click_2(object sender, RoutedEventArgs e)
{
TextBox1.Text += "More Text";
if (TextBox1.LineCount >= 2)
{
TextBox1.Height = 38;
}
if (TextBox1.LineCount >= 3)
{
TextBox1.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
}
}

Categories

Resources