InputPane does not work correctly - windows-phone-8.1

I'm currently developing an Universal Application, but here is a problem. I have a Frame with the TextBox for User Phone Number.
So, I want to change the height of my LayoutRoot (GRID) so it can fits in the free space.
I'm using InputPane.GetForCurrentView().Showing and InputPane.GetForCurrentView().Hiding for that purposes.
Here is my code.
public UserRegistrationAuthorization_PhoneNumber()
{
this.InitializeComponent();
LayoutRootInitialHeight = LayoutRoot.ActualHeight;
InputPane.GetForCurrentView().Showing += UserRegistrationAuthorization_PhoneNumber_Showing;
InputPane.GetForCurrentView().Hiding += UserRegistrationAuthorization_PhoneNumber_Hiding;
}
private void UserRegistrationAuthorization_PhoneNumber_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
LayoutRoot.Height = LayoutRoot.ActualHeight - args.OccludedRect.Height;
LayoutRoot.VerticalAlignment = VerticalAlignment.Top;
args.EnsuredFocusedElementInView = true;
}
private void UserRegistrationAuthorization_PhoneNumber_Hiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
// TODO: Get rid of that shit
LayoutRoot.Height = LayoutRootInitialHeight;
args.EnsuredFocusedElementInView = false;
}
When I click outside the TextBox keyboard hides and leaves after that a black hole on the screen. 2
But, the most interesting is that when I press that physical Back Button on my Lumia, keyboard hides normally and my LayoutRoot gets the Frame's initial height.
Is it a bug or I'm doing something wrong?

It happens because by the time you saving your LayoutRootInitialHeight in the constructor, LayoutRoot actually isn't loaded and it's ActualHeight == 0. Then you setting LayoutRoot.Height to 0, so it becomes not visible. So you should probably save your LayoutRootInitialHeight in LayoutRoot's Loaded event handler.
I would also suggest you not to change LayoutRoot's height at all. It causes your whole visual tree to be rendered from scratch and it's bad practise in general. Instead, modify RenderTransform of all necessary elements so they get moved to appropriate positions. RenderTransform is the right way to handle movements and animations on the screen, and you can achieve some nice visual effects with Next button moving up same as keyboard.
Roughly your code can look like this:
<Button Content="Next" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center">
<Button.RenderTransform>
<CompositeTransform x:Name="NextButtonTransform" TranslateY="0"/>
</Button.RenderTransform>
</Button>
...
private void UserRegistrationAuthorization_PhoneNumber_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
NextButtonTransform.TranslateY = -300;
EnsuredFocusedElementInView = true;
}
private void UserRegistrationAuthorization_PhoneNumber_Hiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
NextButtonTransform.TranslateY = 0;
args.EnsuredFocusedElementInView = false;
}
And more complicated way is to run some storyboard which makes your Next button move up and down in same speed with keyboard, always appearing on top of it. Although, since InputPane.GetForCurrentView().Showing gets fired after keyboard already shown fully, you should hook up all animations to TextBox.GotFocus and TextBox.LostFocus events. On mobile, keyboard is always shown when text box has focus, so it will work nicely.

Related

Is it possible to temporarily postpane displaying an AppBarButton's affiliated Flyout?

I've got a Flyout embedded within an AppBarButton like so:
<AppBarButton x:Name="appbarbtnOpenPhotosets" Icon="OpenFile" Label="Open Existing Photoset[s]" AutomationProperties.Name="Open File" Tapped="appbarbtnOpenPhotosets_Tapped" >
<Button.Flyout>
. . .
</Button.Flyout>
</AppBarButton>
I want to, under certain circumstances, first present the user with an opportunity to rename a file prior to seeing the Flyout. I tried seeing if that would work like this:
async private void appbarbtnOpenPhotosets_Tapped(object sender, TappedRoutedEventArgs args)
{
// Want to conditionally postpone the operation
bool myBucketsGotAHoleInIt = PhotraxUtils.GetLocalSetting(CAINT_BUY_NO_BEER);
if (myBucketsGotAHoleInIt)
{
MessageDialog dlgDone = new MessageDialog("Can you see me now?");
await dlgDone.ShowAsync();
args.Handled = false; // <= adding this made no difference
}
}
This works, in that I see the "Can you see me now?" dialog, but that prevents the Flyout from flying out. A Flyout that doesn't fly out is no more useful than a flying squirrel or fish that doesn't motate through the air.
So how can I temporarily suppress my flyout but then call it forth? The Flyout does not have an Open() method...Is there some other way to invoke it?
Flyouts attached to Buttons open automatically when you click the control.
If you don't want it to open automatically, you need to attach it to another control.
Example taken from official documentation:
<!-- Flyout declared inline on a FrameworkElement -->
<TextBlock>
<FlyoutBase.AttachedFlyout>
<Flyout>
<!-- Flyout content -->
</Flyout>
</FlyoutBase.AttachedFlyout>
</TextBlock>
Then you can show the Flyout whenever you want, calling FlayoutBase.ShowAttachedFlyout() and passing the FrameworkElement casted value of your control.
FlyoutBase.ShowAttachedFlyout(frameworkElement);
So, in your case:
async private void appbarbtnOpenPhotosets_Tapped(object sender, TappedRoutedEventArgs args)
{
// Want to conditionally postpone the operation
bool myBucketsGotAHoleInIt = PhotraxUtils.GetLocalSetting(CAINT_BUY_NO_BEER);
if (myBucketsGotAHoleInIt)
{
MessageDialog dlgDone = new MessageDialog("Can you see me now?");
await dlgDone.ShowAsync();
// New code
FlyoutBase.ShowAttachedFlyout((FrameworkElement)sender);
}
}
If you can't change the control, you should able to use the code I posted with Buttoninstead of TextBlock. I'm not sure about this, but you can try.

Force JScrollPane and JPanel to repaint

I have a JScrollPane that holds a JPanel. The layout on the JPanel is a GridBagLayout. On that JPanel, I add a number of custom components - each is a JPanel with 3 JLabels.
The first time in the program I lay all of this out, it works fine. When I invoke the code to add another custom component to the JPanel, the panel appears empty, but I can determine by examining the contents of the JPanel that my components are actually there. If I resize the JDialog in which this all sites, the JPanel will paint properly. It also works if I scroll the JScrollPane horizontally even a tiny bit.
I use the same method for the initial layout as I do when adding an item.
I've tried various combinations of repaint(), invalidate() and doLayout() but nothing seems to work all the time. I've run into this situation before and have never been able to fully solve it. Any suggestions?
Running under OpenJDK 7u25. Below is the code that lays out the scroll pane and panel.
private void displayRelatedBug(ArrayList<Bug> a_bugs) {
// sort the bugs by ID
ArrayList<Bug> l_sorted = new ArrayList<>(a_bugs);
Collections.sort(l_sorted);
pnlRelatedBugs.removeAll();
pnlRelatedBugs.setLayout(new GridBagLayout());
GridBagConstraints l_gbc = new GridBagConstraints();
l_gbc.gridx = 0;
l_gbc.gridy = 0;
l_gbc.gridwidth = 1;
l_gbc.gridheight = 1;
l_gbc.anchor = GridBagConstraints.NORTHWEST;
l_gbc.fill = GridBagConstraints.NONE;
l_gbc.insets = new Insets(3, 4, 0, 0);
for (Bug r : l_sorted) {
pnlRelatedBugs.add(new RelatedBugDisplay(r, this), l_gbc);
l_gbc.gridy++;
}
// add a filler at the bottom to push it up
l_gbc.weighty = 1.0;
pnlRelatedBugs.add(new MMPanel(), l_gbc);
// add a filler on the right to push them left
l_gbc.weighty = 0.0;
l_gbc.weightx = 1.0;
l_gbc.gridx++;
pnlRelatedBugs.add(new MMPanel(), l_gbc);
// try in vain to make it show up!!!
pnlRelatedBugs.invalidate();
pnlRelatedBugs.doLayout();
pnlRelatedBugs.repaint();
scrollerRelatedBugs.doLayout();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
pnlRelatedBugs.repaint();
scrollerRelatedBugs.repaint();
// this seems to help if the scroll bar is showing
scrollerRelatedBugs.getHorizontalScrollBar().setValue(1);
scrollerRelatedBugs.getHorizontalScrollBar().setValue(0);
}
});
}
Whenever you add/remove components from a visible panel, the basic code is:
panel.remove(...);
panel.add(...);
panel.revalidate();
panel.repaint();
Without a proper SSCCE we can't really tell what your code is doing.
If you do add/remove/replace/others actions with components on showing container, you must to revalidate and repaint your container, to which you add components for proper displaying.

MouseLeftButtonUp : exactly fire condition?

I have got some Rectangles, what I'm trying to implement is:
user touch the screen, he could slide between Rectangles. then his finger Lift off, and the last touched rectangle is selected.
(Lift off outside rectangle will trigger nothing)
Just like my lumia 920's keyboard, once you recognized that your finger was in a wrong place, you could slide to the right place, lift off, and the right character show on the screen.
many thanks to you heroes!
That's trickier than it seems, as the MouseLeftButtonUp event will be triggered only if the MouseLeftButtonDown has first been triggered on the control.
I see two ways to achieve this result:
Assign the same MouseLeftButtonDown and MouseLeftButtonUp event handler to all your rectangles. In the MouseLeftButtonDown, call the CaptureMouse method (it tells the control to continue tracking the mouse events even if the cursor isn't on top of the control anymore):
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
((UIElement)sender).CaptureMouse();
}
In the MouseLeftButtonDown, release the mouse, then use the VisualTreeHelper.FindElementsInHostCoordinates to find the rectangle on which the cursor was when the even was triggered:
private void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var element = (UIElement)sender;
element.ReleaseMouseCapture();
var mouseUpRectangle = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this), this.ContentPanel)
.OfType<Rectangle>()
.FirstOrDefault();
if (mouseUpRectangle != null)
{
Debug.WriteLine("MouseUp in " + mouseUpRectangle.Name);
}
}
(replace ContentPanel by the name of the container in which you've put all your controls)
Not tested but it might work. Subscribe to the MouseLeftButtonUp event of the container in which you've put all your rectangles. Then use the same logic to retrieve the rectangle at the coordinates of the pointer:
private void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var mouseUpRectangle = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this), this.ContentPanel)
.OfType<Rectangle>()
.FirstOrDefault();
if (mouseUpRectangle != null)
{
Debug.WriteLine("MouseUp in " + mouseUpRectangle.Name);
}
}
You can find more information in that article I wrote a few months ago.

Windows Phone: How to Navigate another page which is using DispatcherTimer?

I have two pages Main.xaml which includes only a button and another page timer.xaml which includes a timer. After pressing the button in the main page I want to go to the another page and start timer. I am using following code:
enter code here
**Main Page:**
private void Start_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/timer.xaml", UriKind.Relative));
}
**timer Page:**
public Page1()
{
InitializeComponent();
dispatcherTimer.Interval = new TimeSpan(0, 0, 1, 0, 0);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Start();
counter=0;
count.Text = counter.ToString();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
counter++;
count.Text = counter.ToString();
}
I can not see time in the timer page however, after pressing the button it will navigate to the timer page correctly but I can not see updates in my timer page. I am debugging my code and it seems that DistpacherTimer() works correctly but my timer page does not update. Do you know how can I fix this problem?
Finally, I could find the problem. the code that you can see above was not whole code that I have written. Actually, in my real code I was sending a list <> from main page to timer page. Passing data from one page to another page is a little tricky. My main problem was that I did not send the data from main page to timer page in correct way, so that caused problem. Thus, there was nothing wrong with my DispatcherTimer() class or Event handler.
In other words, the code above works well if you do not pass data (e.g. list, array and so on) from a page to another page in a wrong way.

Better choice: TextLayout or JTextComponent for an "ellipse with editable text" component?

If you've ever used Visio or a UML class diagram editor, you have an idea of what I'm trying to accomplish: Within a JFrame, users can add ellipses that enclose a small editable text field. These ellipses can be repositioned within the frame when the user drags them. Clicking on an ellipse causes the text to become editable: a carat appears, highlighting a substring is possible, etc.
I've got the basic structure set up: the 'ellipse' is a self-contained component, with methods called on it from the containing JFrame and its listeners. I've tried two approaches:
in the component's draw() method, use a TextLayout to find bounds, position the contained text within the ellipse, and draw it to the frame using TextLayout's draw(). This is fast. Dragging the components around in the JFrame, mouse-over and mouse-click behavior are all straightforward. However for the editing functionality it looks like I will need to write a lot of custom code to handle hit testing, carat positioning, text highlighting, line wrapping, etc.
having the component contain a reference to the containing JFrame, and adding or repositioning a TextComponent in that JFrame after drawing the ellipse. This has the advantage of all the built-in TextComponent behavior for editing and line wrapping. But the logistics are really sloppy, and positioning the TextComponent becomes messy too - especially when the user drags the component around.
I'm quite possibly thinking about this all wrong. Can anyone suggest a simple way to do this that I haven't yet stumbled across?
Why don't you combine both your approaches. As long as you are editing, display the text component, otherwise paint all text using a TextLayout. The following example code shows such an approach extending a simple JComponent. It draws a rectangular shape with some text in it and if you click inside it shows an editing possibility. As soon as you click outside again, the component vanished. Note that all the edit-handling functionality is missing in this basic example.
class TestComponent extends JComponent {
JTextArea jta = new JTextArea("12345");
public TestComponent() {
setPreferredSize(new Dimension(400, 400));
setLayout(null);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(final MouseEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (e.getX() >= 40 && e.getX() <= 200 && e.getY() >= 40 && e.getY() <= 80) {
TestComponent.this.add(jta);
jta.setBounds(42, 42, 156, 36);
} else {
TestComponent.this.remove(jta);
}
repaint();
}
});
}
});
}
#Override
public void paintComponent(Graphics _g) {
Graphics2D g = (Graphics2D) _g;
g.drawRect(40, 40, 160, 40);
TextLayout layout = new TextLayout("12345", g.getFont(), g.getFontRenderContext());
layout.draw(g, 42, 42 + layout.getAscent());
}
}