so im really not good at this xaml thing and i tried to look everywhere but couldn't find anything usefull for me, hopefully someone will be able to help me here.
So i have a datagrid with TemplateColumns where i have some controls in it such TextBox's and ComboBox's. What im trying to accomplish here is when i tab from one control i would like to focus on the next control in the same row but what is happening now is the column gets focus and only after that when i press tab again the control will be focus, in less words i have to tab twice to jump from one control to another. My datagrid looks like this:
<DataGridTemplateColumn Header="Omschrijving" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBox TabIndex="0" Name="txtOms" Text="{Binding txtOmschrijving}" Width="140" Height="24" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Since no one could give a hint or help i kinda digged down on the internetzz and found a solution, hopefully will help someone else in need:
Private Sub dgKasStaatRegels_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles dgKasStaatRegels.Loaded
Try
Dim CellFocusedChangedHandler As New RoutedEventHandler(AddressOf FocusChangedHandler)
[AddHandler](DataGridCell.GotKeyboardFocusEvent, CellFocusedChangedHandler)
Catch ex As Exception
WriteErrorLog("ucKasStaat", "dgKasStaatRegels_Loaded", ex)
End Try
End Sub
Private Sub FocusChangedHandler(sender As Object, args As RoutedEventArgs)
If args.RoutedEvent.RoutingStrategy = RoutingStrategy.Bubble Then
Dim DataGridCellObj As FrameworkElement = TryCast(args.OriginalSource, FrameworkElement)
If Keyboard.IsKeyDown(Key.Tab) Then
If DataGridCellObj IsNot Nothing Then
Dim txtb As TextBox = TryCast(DataGridCellObj, TextBox)
If txtb IsNot Nothing Then txtb.Focus()
Dim cb As ComboBox = TryCast(DataGridCellObj, ComboBox)
If cb IsNot Nothing Then
cb.Focus()
cb.IsDropDownOpen = True
End If
End If
End If
End If
End Sub
Public Shared Function FindParent(Of T As DependencyObject)(dependencyObject As DependencyObject) As T
Dim parent = VisualTreeHelper.GetParent(dependencyObject)
If parent Is Nothing Then
Return Nothing
End If
Dim parentT = TryCast(parent, T)
Return If(parentT, FindParent(Of T)(parent))
End Function
Quick explanation: on the datagrid load add a CellFocusedChangedHandler handler and in that sub just track if the object inside that row is a Textbox(in my case) and set its focus!
It worked for me!
Related
I'm new to LINQ to SQL and WPF and i'm using several online guides and YouTube Videos to pick this up (along with this application).
The problem:
On loading the window (and on other key data changes that refresh the Windows DataContext), I need to update what selected item the ComboBox is showing to the item with a matching ID from the Window DataContext binding (c_Instance.FK_RiskIndicator_ID)
I populate the ComboBox from a view which returns the display field and the ID (All ID's are GUIDs).
When the selection is changed, i call a Stored Procedure to update the DB with the ID of the selected item.
Private db As New AppDatabase_DBDataContext
Private CRCTypeView As BindingListCollectionView
Private InstanceDetailTypeView As BindingListCollectionView
Property guInstance_ID As Guid
Private Sub Refresh_PCA_Instance()
Dim c_Instance As IEnumerable(Of t_Instance)
c_Instance = (From Inst In db.t_Instances Where Inst.ID = guInstance_ID Select Inst)
If c_Instance.Count = 1 Then
Me.DataContext = c_Instance
Me.InstanceDetailTypeView = CType(CollectionViewSource.GetDefaultView(Me.DataContext), BindingListCollectionView)
ElseIf c_Instance.Count > 1 Then
Me.DataContext = c_Instance.First
Me.InstanceDetailTypeView = CType(CollectionViewSource.GetDefaultView(Me.DataContext), BindingListCollectionView)
End If
End Sub
Private Sub LoadCRCType()
Dim CRCTypeList As IEnumerable(Of vw_RiskIndicator)
CRCTypeList = (From RiskIndicator In db.vw_RiskIndicators
Order By RiskIndicator.IndexNum
Select RiskIndicator)
PCA_CRC_ComboBox.DataContext = CRCTypeList
Me.CRCTypeView = CType(CollectionViewSource.GetDefaultView(PCA_CRC_ComboBox.DataContext), BindingListCollectionView)
End Sub
Private Sub PCA_CRC_ComboBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles PCA_CRC_ComboBox.SelectionChanged
If PCA_CRC_ComboBox.SelectedItem IsNot Nothing Then
db.sp_Instance_RiskIndicator_UPSERT(guInstance_ID, PCA_CRC_ComboBox.SelectedValue)
Refresh_PCA_Instance() 'Refreshes the Window DataContext
End If
End Sub
XAML for the ComboBox:
<ComboBox x:Name="PCA_CRC_ComboBox" Grid.Row="1" Grid.Column="2"
ItemsSource="{Binding}"
SelectedValuePath="ID"
DisplayMemberPath="RiskIndicator"
PreviewKeyDown="RiskIndicator_ComboBox_PreviewKeyDown"/>
I had assumed i could do this with binding the SelectedItem, but i can't figure it out. I had assumed it would be something like:
<ComboBox x:Name="PCA_CRC_ComboBox" Grid.Row="1" Grid.Column="2"
SelectedItem="{Binding Path=FK_RiskIndicator_ID}" <!--THIS LINE HERE-->
ItemsSource="{Binding}"
SelectedValuePath="ID"
DisplayMemberPath="RiskIndicator"
PreviewKeyDown="RiskIndicator_ComboBox_PreviewKeyDown"/>
Any help with this would be much appreciated.
EDIT: Grouped the code together. I assume this is what was meant. Personally i think this layout if less explanatory.
I have a datagrid, with binding to observable collection, that allows the operator to insert a new row or to delete an exiting row by means of "canc" key.
Based on other post of this community, the delete operation has been implemented in this way:
XAML:
<DataGrid Name="DiameterGrid" ItemsSource="{Binding} AutoGenerateColumns="False"
CanUserAddRows="True" CanUserDeleteRows="True" SelectionMode="Single" SelectionUnit="Cell" PreviewKeyDown="RollGrid_PreviewKeyDown">
And code behind:
...
DiameterGrid.ItemsSource = offer.diameters
...
Private Sub RollGrid_PreviewKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
Dim myGrid As DataGrid = CType(e.Source, DataGrid)
Dim myRec As Diameter
If e.Key = Key.Delete Then
If myGrid.CurrentItem Is Nothing Then
MsgBox("Invalid record")
Else
myRec = CType(myGrid.CurrentItem, Diameter)
offer.diameters.Remove(myRec)
End If
End If
End Sub
The delete operation works properly, the problem is that when the cursor is positioned on the blank new row, I have the error:
An unhandled exception of type 'System.InvalidCastException' occurred in observable collection.exe
The error is raised on the cast operation myRec = CType(myGrid.CurrentItem, Diameter).
I tried to detect the position of the cursor on the blank row with the check:
myGrid.CurrentItem Is Nothing
and in many other different ways, but I couldn't find a solution.
Any suggestion? Answers in C# would also ok...
Thank you in advance for your help
I've got a big issue trying to modify the ItemSource for a ComboBox. When I try to debug the app by putting a breakpoint on MessageBox.Show(...) in the Sub cboTest_SelectionChanged (see below), I get this error:
An Unhandled exception of type 'System.InvalidCastException' occured in Data.exe
Additional Information:
Unable to cast object of type 'System.Data.DataRowView' to type 'System.Windows.Controls.ComboBoxItem'
How can I fix this?
Here's what I do. I create an ACCESS 2007 Database named SNIGDoFFE.accdb, my solution is named Data, I added the Database through the Add New Source Configuration wizard and I drag and drop the Field "Libelle" from the Table Ethnie to the the ComboBox, so it modifies my files like this:
In MainWindow.xaml.vb
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) Handles MyBase.Loaded
Dim SNIGDoFFEDataSet As Data.SNIGDoFFEDataSet = CType(Me.FindResource("SNIGDoFFEDataSet"), Data.SNIGDoFFEDataSet)
'Load data into the table Ethnie. You can modify this code as needed.
Dim SNIGDoFFEDataSetEthnieTableAdapter As Data.SNIGDoFFEDataSetTableAdapters.EthnieTableAdapter = New Data.SNIGDoFFEDataSetTableAdapter.EthnieTableAdapter()
SNIGDoFFEDataSetEthnieTableAdapter.Fill(SNIGDoFFEDataSet.Ethnie)
Dim EthnieViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("EthnieViewSource"), System.Windows.Data.CollectionViewSource)
'EthnieViewSource.View.MoveCurrentToFirst
'I comment this line to leave the combobox empty
End Sub
The code for cboTest_SelectionChanged is:
Private Sub cboTesT_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles cboTesT.SelectionChanged
MessageBox.Show(CType(cboTesT.SelectedItem, ComboBoxItem).Content)
End Sub
In XAML:
<ComboBox x:Name="cboTest" width="120" Margin"184,10,183,134" SelectionChanged="cboTest_SelectionChanged" DisplayMemberPath="Libelle" ItemSource="{Binding Source={StaticResource EthnieViewSource}}" />
In my Resources:
<Window.Resources>
<local:SNIGDoFFEDataSet x:Key"SNIGDoFFEDataSet" />
<CollectionViewSource x:Key="EthnieViewSource" Source="{Binding Ethnie, Source={StaticResources SNIGDoFFEDataSet}}" />
</Window.Resources>
The Solution is built correctly, the main Window loaded correctly, but when I select an item in the combobox, the program crashes. I try to remove all binding and create Item with and everything work, but by binding the source from my database, it crashes again.
The program crashes because you allow the exception to propagate out of your code. To stop the program from crashing, place everything in your SelectedChange event handler in a try catch block.
This should become a standard for all 'windows entry points' into your code. Whether it is an event or a command, if you didn't call it from your code, use a try catch block to stop exceptions from propagating out of your code.
Regarding the error, you are binding data to your ComboBox. When you interrogate the SelectedItem, it will be of the type of the data (in your case, of type System.Data.DataRowView), not a ComboBoxItem.
If you need the containing ComboBoxItem of the selected item, use the ItemContainerGenerator.ContainerFromItem() to find it, for example
Private Sub cboTesT_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles cboTesT.SelectionChanged
Try
Dim combo As ComboBox = CType(sender, ComboBox)
Dim item As ComboBoxItem = combo.ItemContainerGenerator.ContainerFromItem(combo.SelectedItem)
MessageBox.Show(item.Content)
Catch ex As Exception
' Log or display exception.
End Try
End Sub
If you are just after the data, CType the SelectedItem to your datatype (System.Data.DataRowView) and work with it directly.
I hope this helps.
Sorry Mark, to be so long
and again I'm sorry a lot
on msdn forum, they told me that as the binding source is a Table in a database I must use DataRowView instead of ComboBoxItem,
Dim drv As System.Data.DataRowView = CType(cboTest.SelectedItem, DataRowView)
MessageBox.show(drv("Libelle").ToString())
Don't hesitate to help me another time, please.
I'm converting an app of mine from WinForms to WPF. I came across this problem where in WinForms if I CLEAR a combobox, that does not fire combobox.selectedindexchange. It only fires when the user actually changes the index.
That's the functionality I need. However in WPF I can only find combobox.SelectionChanged. This unfortunately fires on both index change and clearing a combobox (any change).
In WPF how can I only trigger an event when the user changes the index? I'm assuming there's a solution I'm missing that's like the WinForms solution. I'm trying not to play games with global variables and having to track what was previously selected....that's a mess.
Mouseleave is also a mess.
I would greatly appreciate any help!
In your case you can implement in SelectionChanged event:
Private Sub OnSelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
Dim combo = TryCast(sender, ComboBox)
If combo.SelectedItem IsNot Nothing Then
'do something
End If
End Sub
You can remove event handler, invoke clear method and add again event handler.
Example code:
<StackPanel>
<ComboBox Name="myCB"
SelectionChanged="ComboBox_SelectionChanged">
<ComboBoxItem>test1</ComboBoxItem>
<ComboBoxItem>test2</ComboBoxItem>
<ComboBoxItem>test3</ComboBoxItem>
</ComboBox>
<Button Content="Clear" Click="Button_Click" />
</StackPanel>
MainWindow class:
Class MainWindow
Private Sub ComboBox_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
Console.WriteLine("Selected index: {0}", CType(sender, ComboBox).SelectedIndex)
End Sub
Private Sub Button_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
RemoveHandler Me.myCB.SelectionChanged, AddressOf ComboBox_SelectionChanged
Me.myCB.Items.Clear()
AddHandler Me.myCB.SelectionChanged, AddressOf ComboBox_SelectionChanged
End Sub
End Class
I am trying to get a reference to ElementHost control. For example in the below code I need to initially use the “testImage” content of the WPF user control to lunch the event. The WPF control is added at run-time, so does the ElementHost control, so I can’t use the WPF control’s name or ElementHost’s name.
My logic is to get the parent WPF user control of the “testImage”, and then get the parent ElementHost of the WPF’s user control.
But I am having troubles writing it in code. Please advise. Thanks.
<UserControl x:Class="WpfTest”
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300">
<Grid>
<Label FontSize="10" Height="24" Margin="74,16,0,0" Name="testLabel" VerticalAlignment="Top" />
<Image Name="testImage" Stretch="Uniform" HorizontalAlignment="Left" Width="64" Height="81" VerticalAlignment="Top" Margin="8,0,0,0"/>
</Grid>
</UserControl>
Here is some code that might help you. The key points are:
Name the ElementHost when you create it at runtime
Make use the the help function FindVisualChildByName() to search the WPF tree to get the desired control
I Hope this helps!
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim ElementHost1 As New System.Windows.Forms.Integration.ElementHost
Dim WpfTest1 As New WindowsApplication1.WPFTest
ElementHost1.Dock = DockStyle.Fill
ElementHost1.Name = "ElementHost1"
ElementHost1.Child = WpfTest1
Me.Controls.Add(ElementHost1)
End Sub
Private Sub GetImageReference_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim ElementHost1 As System.Windows.Forms.Integration.ElementHost = Me.Controls("ElementHost1")
Dim TheGrid As System.Windows.Controls.Grid = CType(ElementHost1.Child, WPFTest).MyGrid
Dim ImageTest As System.Windows.Controls.Image = FindVisualChildByName(TheGrid, "testImage")
Stop
End Sub
Public Function FindVisualChildByName(ByVal parent As System.Windows.DependencyObject, ByVal Name As String) As System.Windows.DependencyObject
For i As Integer = 0 To System.Windows.Media.VisualTreeHelper.GetChildrenCount(parent) - 1
Dim child = System.Windows.Media.VisualTreeHelper.GetChild(parent, i)
Dim controlName As String = child.GetValue(System.Windows.Controls.Control.NameProperty)
If controlName = Name Then
Return child
Else
Dim res = FindVisualChildByName(child, Name)
If Not res Is Nothing Then
Return res
End If
End If
Next
Return Nothing
End Function