Scrolling to the end of a single line WPF TextBox - wpf

This seems like such a simple thing but I just can't get it to work.
I have a single line text box that has a lot of text. What I want to happen is that whenever the text box receives focus, it scrolls to the end of the text so that it comes into view and the cursor is at the end ready to accept new text.
In the text box's GotFocus event I call textBox.ScrollToEnd(). It looks like all this does is move the cursor to the end of the text box but not actually bring the end into view.
What am I missing?

You should be able to do it using these members:
TextBoxBase.ScrollToEnd
TextBox.CaretIndex
EDIT: not sure why ScrollToEnd isn't working... Anyway, this code works:
textBox.CaretIndex = textBox.Text.Length;
var rect = textBox.GetRectFromCharacterIndex(textBox.CaretIndex);
textBox.ScrollToHorizontalOffset(rect.Right);

It is true - setting the caret property from code doesn't affect the view; and the caret can go outside the visible part.
kb_target_box.Focus(); // just for sure
Rect rect = kb_target_box.GetRectFromCharacterIndex(kb_target_box.CaretIndex);
kb_target_box.ScrollToHorizontalOffset(Math.Max((kb_target_box.HorizontalOffset + rect.Left - (kb_target_box.ActualWidth - 40)), 0.0));
h-scroll will follow the caret after it comes closer than 40 to the right TextBox border.

This worked for me.
textBox.CaretIndex = txt.Text.Length;
textBox.ScrollToEnd();

In my case, my textbox was inside a scrollviewer and I had to call ScrollToEnd() for scrollviewer instead textbox.
XAML
<ScrollViewer x:Name="scrollviewer">
<TextBox TextBoxBase.TextChanged="TextChanged"/>
</ScrollViewer>
CODE
private void TextChanged(object sender, TextChangedEventArgs e)
{
scrollviewer.ScrollToEnd();
}

Related

WPF Datagrid Edit won't allow me to change other cells

I've a Datagrid whose DataContext is assigned to a Dataview. When I try to edit the datagrid shown in the form there appears a red border around the cell being editted AFTER I click out or press Enter.
I then try double clicking on another cell but it won't allow me to be in edit mode.
I've tried following http://www.scottlogic.co.uk/blog/colin/2009/01/wpf-datagrid-committing-changes-cell-by-cell/ and http://codefluff.blogspot.com/2010/05/commiting-bound-cell-changes.html but neither appear to work on my case.
My Code for the CellEditEnding event
private void dgCompList_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (!isManualEditCommit)
{
isManualEditCommit = true;
System.Windows.Controls.DataGrid grid = (System.Windows.Controls.DataGrid)sender;
grid.CommitEdit(DataGridEditingUnit.Row, true);
isManualEditCommit = false;
}
}
I've also tried using CommitEdit() on the actual datagrid itself, but nada. Could someone explain to me what's going on and how to resolve this please?
normally, a red border around the cell is a error state, so it sounds like you have some type of validation error, to me.

WPF TextBox - don't hide the selection

WPF's TextBox (System.Windows.Controls.TextBox) appears to highlight selected text only when it has the focus. I need to make a TextBox continue to show the selection when focus is lost.
In a standard Win32 EDIT control I could achieve this with ES_NOHIDESEL. How can I get the equivalent in WPF?
You can handle the LostFocus event and set the event arg to e.Handled = true. In this way the TextBox will not know that it lost focus and will keep your selection.
private void myTextCtrl_LostFocus(object sender, RoutedEventArgs e)
{
e.Handled = true;
}
This will give you a similar thing to what you are looking for, but unlike the Win32 way, it will still show your selection in the highlighted color instead of dark gray.
If you really want to go through the effort you could also write XAML for <TextBox.SelectionBrush>.
Another way is to use FocusManager, you can read about this method here.

WPF ListView ScrollViewer Double-Click Event

Doing the below will reproduce my problem:
New WPF Project
Add ListView
Name the listview: x:Name="lvList"
Add enough ListViewItems to the ListView to fill the list completely so a vertical scroll-bar appears during run-time.
Put this code in the lvList.MouseDoubleClick event
Debug.Print("Double-Click happened")
Run the application
Double-click on the LargeChange area of the scroll-bar (Not the scroll "bar" itself)
Notice the Immediate window printing the double-click happened message for the ListView
How do I change this behavior so MouseDoubleClick only happens when the mouse is "over" the ListViewItems and not when continually clicking the ScrollViewer to scroll down/up in the list?
You can't change the behaviour, because the MouseDoubleClick handler is attached to the ListView control, so it has to occur whenever the ListView is clicked -- anywhere. What you can do it detect which element of the ListView first detected the double-click, and figure out from there whether it was a ListViewItem or not. Here's a simple example (omitting error checking):
private void lv_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
DependencyObject src = (DependencyObject)(e.OriginalSource);
while (!(src is Control))
src = VisualTreeHelper.GetParent(src);
Debug.WriteLine("*** Double clicked on a " + src.GetType().Name);
}
Note the use of e.OriginalSource to find the actual element that was double-clicked. This will typically be something really low level like a Rectangle or TextBlock, so we use VisualTreeHelper to walk up to the containing control. In my trivial example, I've assumed that the first Control we hit will be the ListViewItem, which may not be the case if you're dealing with CellTemplates that contain e.g. text boxes or check boxes. But you can easily refine the test to look only for ListViewItems -- but in that case don't forget to handle the case there the click is outside any ListViewItem and the search eventually hits the ListView itself.
Maybe this helps?
Private Sub LstView_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles LstView.MouseDoubleClick
Dim source As FrameworkElement = TryCast(e.OriginalSource, FrameworkElement)
If IsNothing(source) Then Return
Dim TmplParent As DependencyObject = TryCast(source.TemplatedParent, DependencyObject)
If IsNothing(TmplParent) Then Return
If Not TmplParent.GetType.Equals(GetType(System.Windows.Controls.ListViewItem)) Then e.Handled = True
End Sub
I don't have VS handy to test if this works, but have you tried handling the double-click event on the ListViewItems rather than the ListView itself?
<ListView ListViewItem.MouseDoubleClick="lv_MouseDoubleClick" ... />
That should handle the MouseDoubleClick event on any child ListViewItem controls inside the ListView. Let us know if it works!
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnListViewDoubleClick" />
</Style>
If you apply this style, it works. Just double click on item in the listview will work.
also, you have to remove the double click from the listview.

Silverlight TextBox, moving caret position

I am trying to make a TextBox behavior for an on-screen keyboard, it can update the text in the textbox but I can't get it to focus the textbox and move the caret to the end of the text once it's done updating.
I have tried using TextBox.Focus() and TextBox.Select() in both orders with no luck.
Thanks for your time
Changing the focus from within an input event handler (i.e. mouse click on a virtual key) will fail, since the mouse-up event (or key-up) will return the focus to the original element. In order to make it work, you have to dispatch the focus command to a later time using the Dispatcher object.
Example:
Dispatcher.Invoke(new Action(() =>
{
textBox1.Focus();
textBox1.SelectionStart = textBox1.Text.Length;
// or textBox1.Select(textBox1.Text.Length,0);
}), System.Windows.Threading.DispatcherPriority.Background);
Here is a simple example of moving the cursor to the end of a TextBox once you have updated it.
TextBox.Focus();
TextBox.Text = "sometext ";
TextBox.SelectionStart = TextBox.Text.Length;
I had a similar situation and I attached a "TextChanged" Handler to my textbox as shown here.
<TextBox Name="tbTextEntry" Width="200" HorizontalAlignment="Center" Background="Plum" Text="{Binding Entered_text,Mode=TwoWay}" TextChanged="OnTextChanged_Handler"></TextBox>
And handled the event like this(thanks Jeff Beck from comment above):
protected void OnTextChanged_Handler(object sender, RoutedEventArgs e)
{
TextBox my_text_box = (TextBox)sender;
my_text_box.SelectionStart = my_text_box.Text.Length;
Debug.WriteLine("OnTextChanged_Handler called !");
return;
}
This is inefficient for the cases when a person leaves the onscreen keyboard and start
to use the real keyboard because each new character will then cause this handler to be
invoked needlessly as the cursor works just fine with real keyboard. Any more efficient solution welcome !
Hope that helps.
Thanks.

Adding ToolTip to ViewPort3D child elements

Hi i would like to add Tool Tip to ViewPort3D child elements when i put my mouse over on it, Only viewPort3D has a tooltip property but not for their childs. Any way to work around it?
I was able to get a partial solution by adding a canvas with a textblock inside to hold my text. Like this...
<Grid>
<Canvas>
<TextBlock Name="txtblkTip" TextAlignment="Center" Padding="2" />
</Canvas>
<Viewport3d ...
...
</Viewport3d>
</Grid>
Then as the user moves the mouse over an object in viewport3d I use the following mouse event handler to redraw the tooltip at the required location, based on the HitTest method.
Private Sub viewport_PreviewMouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles viewport.PreviewMouseMove
Dim ptMouse As Point = e.GetPosition(viewport)
Dim result As HitTestResult = VisualTreeHelper.HitTest(viewport, ptMouse)
If TypeOf result Is RayMeshGeometry3DHitTestResult Then
Dim result3d As RayMeshGeometry3DHitTestResult = CType(result, RayMeshGeometry3DHitTestResult)
If TypeOf result3d.VisualHit Is Sphere Then
If CType(result3d.VisualHit, Sphere).Name <> "" Then
'Position the Canvas near the mouse pointer
Canvas.SetLeft(txtblkTip, ptMouse.X + 12)
Canvas.SetTop(txtblkTip, ptMouse.Y + 12)
txtblkTip.Text = CType(result3d.VisualHit, Sphere).Name
End If
End If
End If
End Sub
One thing I have not been able to get is an event when the mouse moves off all objects in the Viewport, to remove the tooltip, but I suspect this could be done with a storyboard.
Hope this helps you along the way.
XamTrix's answer works with the addition of a MouseLeave event handler that sets the visibility of the textblock to Visibility.Collapsed (the visibility of the textblock
must also be reset to Visible in the PreviewMouseMove event handler).
Also, if the Canvas is placed after the Viewport3d instead of before it, the textblock
will appear above the Viewport3d elements. In this case the Canvas.SetLeft statement
should be changed to: ptMouse.X + 12 - viewport3d.actualWidth.

Resources