Getting value of a dynamically generated text box in a listbox, Windows Phone 8 - vb.net

In my Windows Phone 8 app, I have a ListBox control. This list box contains a grid, which contains a pair of TextBlock (title of the field) and a TextBox (user input) controls. This list is generated based on the results of a service the app connects to. What I need to do is access each textbox within the list, and bring back the value of it. To do this, I have bound a unique ID of each item to the Tag property of the TextBox, and I am using the LostFocus event to capture the user input. Once this has been captured and added to a collection in the code behind, the data is processed when the user clicks a button under the list. This works fine for every item except the last one.
The problem is that the LostFocus doesn't work if the button is clicked. The button click method seems to take precedence over the textbox LostFocus method. So if there is only 1 item in the list, the value isn't recorded. Here is the code:
<ItemsControl x:Name="itmScreen4">
<TextBlock Margin="0,10,0,10">Fields</TextBlock>
<ListBox x:Name="lstCustom" ItemsSource="{Binding}" Visibility="Visible">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="grdCustom">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="txtCustTitle" Text="{Binding Name}" Foreground="#4C4C4C" FontSize="18" Margin="0,15,0,0"></TextBlock>
<TextBox Tag="{Binding DataID}" x:Name="txtCust" Grid.Row="1" Style="{StaticResource TextBox}" Width="450" LostFocus="txtCust_LostFocus"></TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="btnSubmit" Content="Submit" Background="#404040"></Button>
</ItemsControl>
For the final item in the list (or if there's only one item in the list), the txtCust_LostFocus method doesn't get called when the btnSubmit method is called. Any ideas of how I can capture that final textbox value?
I have tried some other methods (casting a ListBoxItem and performing a FindName on it, for example), but haven't found anything that works. Google hasn't been much help either. Thanks in advance.
EDIT:
Here is the code behind. I am binding a custom class to the list as below.
Class definition here (I have removed some properties for readabality purposes):
Public Class CustomDataRequest
Public Sub New()
End Sub
Public Property ID As Integer
Public Property Name As String
End Class
Use in the code here:
Public Sub ShowCustomData()
Dim CustomDataList As New List(Of CustomDataRequest)()
For Each item In _CustomDataRequestList
If item.ID= _CurrentID Then
CustomDataList.Add(item)
End If
Next
lstCustom.ItemsSource = CustomDataList.ToArray
End Sub
The txtCust_LostFocus method is just capturing the fields at the minute. Once I can actually get it called, I can then add the data to the collection:
Private Sub txtCust_LostFocus(sender As Object, e As RoutedEventArgs)
Dim elem = DirectCast(sender, FrameworkElement)
Dim txt = DirectCast(elem.FindName("txtCust"), TextBox)
Dim text As String = txt.Text
Dim tag As String = txt.Tag
End Sub
The problem is that it never gets called once the button has been tapped:
Protected Sub btnSubmit_Tap(sender As Object, e As Input.GestureEventArgs) Handles btnSubmit.Tap
ValidateData()
End Sub

Here is the answer to this. The TextBox should have TwoWay binding mode set, to bind the text value to a corresponding Value property in the class. It should also have UpdateSourceTrigger set to Explicit for binding efficiency, as detailed here.
Public Class CustomDataRequest
Public Sub New()
End Sub
Public Property ID As Integer
Public Property Name As String
'New property here
Public Property Value As String
End Class
Textbox code:
<TextBox Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=Explicit}" x:Name="txtCust" Grid.Row="1" BorderBrush="#BBBBBB" Style="{StaticResource TextBox}" Width="450" TextChanged="Textbox_Changed"></TextBox>
In the code behind, the following method should be called from the Textbox TextChanged (translated into VB):
Private Sub Textbox_Changed(sender As Object, e As TextChangedEventArgs)
Dim txt As TextBox = TryCast(sender, TextBox)
Dim bindingExpr As BindingExpression = txt.GetBindingExpression(TextBox.TextProperty)
bindingExpr.UpdateSource()
End Sub
This solves the problem of the textbox value not binding when the focus is still on the textbox. Thanks to venerik for the answer to this question.

Related

Keep the values in page when navigating to other pages

I am developing a UWP app where there is a "page" and in that page, there are several "textbox"es which values are entered by a user (numbers). When it is navigated to other pages to take other values and came back to this page the values entered in "textbox"es are all lost. I used "NavigationCacheMode", no success.
Could you please help how to maintain values in UI elements (textboxes) in current page when navigating to other pages.
I would appreciate if the solution were in VB.NET.
Thank you very much.
You would have to save the date by yourself in this scenario. For example, you could choose the ApplicationData.LocalSettings to save the data when navigating to other page.
You could save the data on the page's OnNavigatedFrom method and get the data in its OnNavigatedTo method like the following:
<StackPanel>
<TextBox x:Name="textbox1"></TextBox>
<TextBox x:Name="textbox2"></TextBox>
<Button Content="navigate" Click="Button_Click"></Button>
</StackPanel>
Public NotInheritable Class MainPage
Inherits Page
Dim localSettings As Windows.Storage.ApplicationDataContainer = Windows.Storage.ApplicationData.Current.LocalSettings
Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
MyBase.OnNavigatedTo(e)
Dim value1 As Object = localSettings.Values(textbox1.Name)
Dim value2 As Object = localSettings.Values(textbox2.Name)
If value1 IsNot Nothing And value2 IsNot Nothing Then
textbox1.Text = value1.ToString()
textbox2.Text = value2.ToString()
End If
End Sub
Protected Overrides Sub OnNavigatedFrom(e As NavigationEventArgs)
MyBase.OnNavigatedFrom(e)
localSettings.Values(textbox1.Name) = textbox1.Text
localSettings.Values(textbox2.Name) = textbox2.Text
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Frame.Navigate(GetType(BlankPage1))
End Sub
End Class

How to give a "ctrl + " shortcut key for a button in vb.net

how to add a " ctrl + " shortcut to button in vb.net. For example I need to perform click event of save button when ctrl + s is pressed.
Winforms Solution
In your Form class, set its KeyPreview property to true, example of it being set in the Form constructor, either set it here or via the Designer:
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.KeyPreview = True
End Sub
Then all you need to do is handle the KeyDown event for the Form, like this:
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
If (e.Control AndAlso e.KeyCode = Keys.S) Then
Debug.Print("Call Save action here")
End If
End Sub
WPF Solution (Not using MVVM pattern)
Add this to your .xaml file
<Window.Resources>
<RoutedUICommand x:Key="SaveCommand" Text="Save" />
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource SaveCommand}" Executed="SaveAction" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="S" Modifiers="Ctrl" Command="{StaticResource SaveCommand}" />
</Window.InputBindings>
Alter your button definition to include Command="{StaticResource SaveCommand}", for example:
<Button x:Name="Button1" Content="Save" Command="{StaticResource SaveCommand}" />
In your Code Behind (.xaml.vb) put your function to call the save routine, such as:
Private Sub SaveAction(sender As Object, e As RoutedEventArgs)
Debug.Print("Call Save action here")
End Sub

Silverlight: Get first TextBox in DataTemplate

So far I have looked at several questions and answers for how to get a TextBox within a DataTemplate, but none of it is working for me.
I have xaml like so (minimal example). The data template is in my section for static resources, and the ItemsControl is in the content section:
<DataTemplate x:Key="GridTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<sdk:IntegerTextBox DataField="Model.DataField" Width="90" SelectAllOnFocus="True" />
</Grid>
</DataTemplate>
<ItemsControl x:Name="MyControl" ItemsSource="{Binding MyList}" ItemTemplate="{StaticResource GridTemplate}" />
I need to be able set focus to the first IntegerTextBox in the Grid in the code behind. IntegerTextBox inherits the TextBox class.
I first tried writing my own method to recursively search through all UIElements for the first TextBox, but I found that the content within the DataTemplate isn't searchable in this way. The ItemsControl's children always return Nothing:
Private Function FirstTextBox(ByVal uiElement As UIElement) As TextBox
Dim textBox As TextBox = TryCast(uiElement, TextBox)
If textBox IsNot Nothing Then Return textBox
Dim panel As Panel = TryCast(uiElement, Panel)
If panel IsNot Nothing Then
For Each child As UIElement In panel.Children
textBox = FirstTextBox(child)
If textBox IsNot Nothing Then Return textBox
Next
End If
Dim itemsControl As ItemsControl = TryCast(uiElement, ItemsControl)
If itemsControl IsNot Nothing Then
For i As Integer = 0 To itemsControl.Items.Count
textBox = FirstTextBox(CType(itemsControl.ItemContainerGenerator.ContainerFromIndex(i), UIElement))
If textBox IsNot Nothing Then Return textBox
Next
End If
Return textBox
End Function
I have tried this, similar to here and here, but the ContentPresenter is Nothing:
Dim contentPresenter = CType(MyControl.ItemContainerGenerator.ContainerFromIndex(0), ContentPresenter)
Dim textbox As TextBox = CType(CType(contentPresenter.ContentTemplate.LoadContent(), Panel).Children.First(Function(c) TypeOf c Is TextBox), TextBox)
I tried getting the DataTemplate as shown here, then loading the content and searching the children for a TextBox as shown here, but it never finds a TextBox.
I have worked a couple days on this, but I cannot see what I am doing wrong. Is it some obvious mistake, or am I approaching the problem incorrectly? Thanks.
EDIT - This is how I got it to work by adding 100 ms delay:
Private Function FindDescendant(Of TDescendant As DependencyObject)(ByVal obj As DependencyObject) As TDescendant
Dim all = VisualTreeExtensions.GetVisualDescendants(obj)
Dim first = all.OfType(Of TDescendant)().FirstOrDefault()
Return first
End Function
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As ComponentModel.DoWorkEventArgs)
System.Threading.Thread.Sleep(100)
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As ComponentModel.RunWorkerCompletedEventArgs)
Dim firstTextBox = FindDescendant(Of IntegerTextBox)(MyControl)
If firstTextBox IsNot Nothing Then firstTextBox.Focus()
End Sub
Private Sub SetFocus()
Dim bw As New ComponentModel.BackgroundWorker
bw.WorkerReportsProgress = True
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
bw.RunWorkerAsync()
End Sub
You are approaching the problem incorrectly. Accessing the DataTemplate and searching for a TextBox will get you nowhere.
The template is only a blueprint, only when it is used somewhere (like for each item in your ItemsControl) its content is instantiated (once for each item).
One of several possible solutions to set focus to the first item's textbox:
In your code-behind add an eventhandler to the itemsControl:
MyControl.GotFocus += (sender, args) =>
{
var firstItemTextBox = MyControl.FindDescendant<IntegerTextBox>();
if ( firstItemTextBox != null ) firstItemTextBox.Focus();
};
some helper code:
//you need System.Windows.Controls.Toolkit.dll from the SilverlightToolkit for the class VisualTreeExtensions
using System.Windows.Controls.Primitives;
public static class ControlExtensions
{
public static TDescendant FindDescendant<TDescendant>(this DependencyObject element)
{
return element.GetVisualDescendants().OfType<TDescendant>().FirstOrDefault();
}
}
And btw: why do you wrap the IntergerTextBox in a separate Grid for each item?

Issue when binding combobox and listbox to List (VB.NET)

I'm trying to bind a ComboBox and a ListBox to a List(Of String) in Vb.Net (VS2013), this is for a WinForms application, the thing is that after setting the DataSource property on both the ComboBox and the ListBox, selecting one item on either one of them affects the other control, for example, after the controls have been populated with the information if I select an item from the ListBox, then the same item gets selected in the ComboBox, and this goes the same way for the ComboBox, if I select an item from it, then that item gets also selected in the ListBox, so my question is... how can I bind the combobox and listbox to the same List(Of String) without affecting the behavior on the controls, the purpose is to keep all the controls within that form syncronized based on the contents of the List, I declared the List in a module like this:
Public listaAreas As New List(Of String)
then the controls are populated like this on form load:
cmbArea.DataSource = listaAreas
lstAreas.DataSource = listaAreas
And I run this method whenever I need to update the information:
Private Sub RefreshLists()
lstAreas.DataSource = Nothing
lstAreas.DataSource = listaAreas
cmbArea.DataSource = Nothing
cmbArea.DataSource = listaAreas
End Sub
Please let me know if I'm missing some information, this is my first post but I think it is clear enough so you get the idea of what I'm trying to accomplish here ... =)
Thanks in advance!
Try setting up separate BindingSources and try using a BindingList(Of String) instead of just a List, which won't report item changes:
Private listaAreas As New BindingList(Of String)
Private cbSource As New BindingSource(listaAreas, String.Empty)
Private lbSource As New BindingSource(listaAreas, String.Empty)
Public Sub New()
InitializeComponent()
cmbArea.DataSource = cbSource
lstAreas.DataSource = lbSource
End Sub
The same currency position is being used in your code, but by defining two separate binding sources, each one will have its own position property.

WPF: Avoid Canvas scrollviewer reset

I've the following problem: when an item in the canvas is selected (to be erased), the scrollviewer is always reset to 0: this is due to the focus in the example code. If the focus() is removed, the scrollviewer is Ok, but the selected item now can't be erased!>
Mainwindow.Xaml code:
<Border Grid.Row="1" BorderThickness="1" BorderBrush="Navy" Margin="2" Padding="2" >
<ScrollViewer Name="Posizione_scrollbar" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
Protected Overrides Sub OnPreviewMouseDown(ByVal e As System.Windows.Input.MouseButtonEventArgs)
MyBase.OnPreviewMouseDown(e)
' usual selection business
Dim designer As DesignerCanvas = TryCast(VisualTreeHelper.GetParent(Me), DesignerCanvas)
If designer IsNot Nothing Then
If (Keyboard.Modifiers And (ModifierKeys.Shift Or ModifierKeys.Control)) <> ModifierKeys.None Then
If Me.IsSelected Then
designer.SelectionService.RemoveFromSelection(Me)
Else
designer.SelectionService.AddToSelection(Me)
End If
ElseIf Not Me.IsSelected Then
If MainViewModel.Instance.ActiveDiagram.STMonitor = False Then
designer.SelectionService.SelectItem(Me)
End If
End If
'Here is the problem: the canvas scrollbar is resetted to 0!
Me.Focus()
End If
'True per avere la gestione col tasto sinistro del mouse
e.Handled = True
End Sub
You should try to handle RequestBringIntoView event raised when the item receives focus and prevent this event to be bubbled to ScrollViewer. One good place to mark these events as handled is at Canvas level.