Reference to GUI element by name as string - wpf

How can i reference to a GUI element by it's name as String for all elements, even the ones in grids inside tab items.
Now i have this code:
Module VisualExtensions
<System.Runtime.CompilerServices.Extension>
Public Iterator Function GetVisualChildren(Of T As Visual)(parent As DependencyObject) As IEnumerable(Of T)
Dim child As T = Nothing
Dim numVisuals As Integer = VisualTreeHelper.GetChildrenCount(parent)
For i As Integer = 0 To numVisuals - 1
Dim v As Visual = DirectCast(VisualTreeHelper.GetChild(parent, i), Visual)
child = TryCast(v, T)
If v IsNot Nothing Then
For Each item In GetVisualChildren(Of T)(v)
Yield item
Next
End If
If child IsNot Nothing Then
Yield child
End If
Next
End Function
End Module
Which is called by this piece of code:
Try
For Each s In output
Dim nameOfControl = s
Dim window = Windows.Application.Current.Windows(0)
Dim visuals = GetVisualChildren(Of FrameworkElement)(window)
Dim child = visuals.OfType(Of FrameworkElement)()
Dim match = child.FirstOrDefault(Function(x) x.Name = nameOfControl)
match.Visibility = Visibility.Collapsed
Next
Catch ex As NullReferenceException
Finally
End Try
If s is "veleprodajaTab" it works well (code collapses that tab) and xml for that tab is:
<TabItem x:FieldModifier="public" x:Name="veleprodajaTab" Header="Maloprodaja" FontSize="10" VerticalAlignment="Bottom">
But if s is "buttonRefresh" it does not work, it can't reference to that object. I get a nullreferenceexception in debug child is Nothing
Code for "buttonRefresh" (with grids surounding it):
<TabItem x:FieldModifier="public" x:Name="maloprodajaTab"...
<Grid x:FieldModifier="public" x:Name="maloprodajaTabGrid"...
<Grid x:FieldModifier="public" x:Name="gridFckp"
<Button x:FieldModifier="public" x:Name="buttonRefresh" Content="Refresh" HorizontalAlignment="Left" Margin="276,6,0,0" VerticalAlignment="Top" Width="75" Grid.ColumnSpan="2"/>
</grid>
</grid>
I can't seem to reference to anything inside **TabItem **
If i try to set window.buttonRefresh.Visibility = Visibility.Collapsed code works but there is a problem with referencing to childs inside grids

You can't since the TabControl is virtualized. You need to navigate the Visual Tree passing it the name of the element you need. Have a look here:
Find WPF controls by name or type
So you can do something like this:
private T FindChildByname<T>(DependencyObject parent, string name) where T : FrameworkElement
{
T child = default(T);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var ch = VisualTreeHelper.GetChild(parent, i);
child = ch as T;
if (child != null && child.Name == name)
break;
else
child = FindChildByname<T>(ch, name);
if (child != null) break;
}
return child;
}
And call it like:
Button button = FindChildByname<Button>(maloprodajaTabGrid, "buttonRefresh");

Related

Find control on active window or inside grid instead of MainWindow

With this code i can get control only on MainWindow:
Public Shared Function FindChild(Of T As DependencyObject)(ByVal parent As DependencyObject, ByVal childName As String) As T
If parent Is Nothing Then Return Nothing
Dim foundChild As T = Nothing
Dim childrenCount As Integer = VisualTreeHelper.GetChildrenCount(parent)
For i As Integer = 0 To childrenCount - 1
Dim child = VisualTreeHelper.GetChild(parent, i)
Dim childType As T = TryCast(child, T)
If childType Is Nothing Then
foundChild = FindChild(Of T)(child, childName)
If foundChild IsNot Nothing Then Exit For
ElseIf Not String.IsNullOrEmpty(childName) Then
Dim frameworkElement = TryCast(child, FrameworkElement)
If frameworkElement IsNot Nothing AndAlso frameworkElement.Name = childName Then
foundChild = CType(child, T)
Exit For
End If
Else
foundChild = CType(child, T)
Exit For
End If
Next
Return foundChild
End Function
Public sub findControl()
Dim foundTKU As TextBox = FindChild(Of TextBox)(Application.Current.MainWindow, "TKU_" & row("gorivo"))
End sub
How would I implement the same thing but instead MainWindow to look in active window or inside WPF grid which is called "controlGrid"
Just pass an instance of the active window or the control to the method instead of passing Application.Current.MainWindow:
Public sub findControl()
Dim foundTKU As TextBox = FindChild(Of TextBox)(controlGrid, "TKU_" & row("gorivo"))
End sub

Reference to a element inside a grid in wpf

I have a function which querys the database for Strings (which are x:Name="" in my application)
Following is the code:
Try
For Each s In output
Dim nameOfControl = s
Dim window = Windows.Application.Current.Windows(0)
Dim visuals = GetVisualChildren(Of FrameworkElement)(window)
Dim child = visuals.OfType(Of FrameworkElement)().FirstOrDefault(Function(x) x.Name = nameOfControl)
child.Visibility = Visibility.Collapsed
Next
Public Iterator Function GetVisualChildren(Of T As Visual)(parent As DependencyObject) As IEnumerable(Of T)
Dim child As T = Nothing
Dim numVisuals As Integer = VisualTreeHelper.GetChildrenCount(parent)
For i As Integer = 0 To numVisuals - 1
Dim v As Visual = DirectCast(VisualTreeHelper.GetChild(parent, i), Visual)
child = TryCast(v, T)
If v IsNot Nothing Then
For Each item In GetVisualChildren(Of T)(v)
Yield item
Next
End If
If child IsNot Nothing Then
Yield child
End If
Next
End Function
I cannot seem to figure out why this does not work for all elements, ie. my window consists of a TabControl which i can call by "x name", same goes for toolBar, everything works well even with TabItem's but i cannot reference to <Button x:Name="buttonRefresh" which is inside one of TabItem's grids.
I recieve a error NullReferenceException on line bellow:
child.Visibility = Visibility.Collapsed
If i insert a breakpoint at that line i get this {System.Windows.Controls.TabItem Header:FirstTab Content:} for child so i'm asuming i need to change something about this piece of code
Dim window = Windows.Application.Current.Windows(0)
Your problem is here:
Dim child = visuals.OfType(Of FrameworkElement)().FirstOrDefault(Function(x) x.Name = nameOfControl)
The FirstOrDefault method will return Nothing if the predicate results in False. In your case, it has done so because on the next line, child is null, hence the NRE:
child.Visibility = Visibility.Collapsed
You can't assign to the Visibility property on child because it's Nothing.
Your predicate is Function(x) x.Name = nameOfControl, so there must not be a value in the collection returned by visuals.OfType(Of FrameworkElement)() whose Name property equals nameOfControl.
To address this, you need to debug it and see what's really going on. I would break your statement into two lines:
Dim child = visuals.OfType(Of FrameworkElement)()
Dim match = child.FirstOrDefault(Function(x) x.Name = nameOfControl)
Put a break point on the second line so it breaks before that statement runs. Then look in your locals/autos window (assuming Visual Studio), and see what's actually in the collection.
One other thing to note here: when you compare x.Name to nameOfControl with the equals sign =, it does so in case sensitive manner, so if you have MyName and myname, those two are not equal. You can use this instead if you want it to be compare in a case-insensitive manner:
Function(x) x.Name.Equals(nameOfControl, StringComparison.CurrentCultureIgnoreCase)

Accessing elements inside a DataTemplate in a TabItem

Well, I have a problem with a function that I get from there
[VB.NET]
Public Class TreeHelper
Public Shared Function FindVisualChildByName(Of T As FrameworkElement)(parent As DependencyObject, name As String) As T
Dim child As T = Nothing
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(parent) - 1
Dim ch = VisualTreeHelper.GetChild(parent, i)
child = TryCast(ch, T)
If child IsNot Nothing AndAlso child.Name = name Then
Exit For
Else
child = FindVisualChildByName(Of T)(ch, name)
End If
If child IsNot Nothing Then
Exit For
End If
Next
Return child
End Function
End Class
And the XAML part:
<TabItem x:Name="itemControls"
Height="50"
Margin="0"
VerticalAlignment="Top"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Padding="6,1">
<TabItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image x:Name="iconKB"
Width="25"
Height="25"
Stretch="Fill" />
</StackPanel>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
So, I tried to edit iconKB image's source with the following syntax:
TreeHelper.FindVisualChildByName(Of Image)(itemControls, "iconKB").Source = New BitmapImage(New Uri("pack://application:,,,/Resources/icons/Keyboard.png"))
But for some reason it doesn't change. It keeps blank. (And the problem is not in New BitmapImage(New Uri("pack://application:,,,/Resources/icons/Keyboard.png")) it's completely checked with another image controls)
Thanks in advance.
It's because it is defined only inside namescope of the DataTemplate. Think about it, when you run your application you could have plenty of them and all of them can't be called iconKB.
EDIT: Ok i checked your code. It's ok. The thing that makes it don't behave correctly is that you try to find an element that is not yet in the VisualTree because the tab is not opened. So the image is not found.
If you write it in Loaded event handler it will work.
Private Shadows Sub TSLoaded() Handles tabSettings.Loaded
TreeHelper.FindVisualChildByName(Of Image)(itemControls, "iconKB").Source = New BitmapImage(New Uri("pack://application:,,,/Resources/icons/Keyboard.png"))
TreeHelper.FindVisualChildByName(Of Image)(itemMouse, "iconMouse").Source = New BitmapImage(New Uri("pack://application:,,,/Resources/icons/Mouse.png"))
TreeHelper.FindVisualChildByName(Of Image)(itemAudio, "iconAudio").Source = New BitmapImage(New Uri("pack://application:,,,/Resources/icons/Audio.png"))
TreeHelper.FindVisualChildByName(Of Image)(itemVideo, "iconVideo").Source = New BitmapImage(New Uri("pack://application:,,,/Resources/icons/Video.png"))
TreeHelper.FindVisualChildByName(Of Image)(itemSettings, "iconSettings").Source = New BitmapImage(New Uri("pack://application:,,,/Resources/icons/Settings.png"))
End Sub

Casting to a type dynamically in VB

I've been hunting through stackoverflow for a while to answer this.
I've got a Listview who's items are Listviews whose children are actually a list(of string) that is a member of the parent listviewitem.
Drag and drop functionality is the goal. However this is proving hard for a variety of reasons, one of which is casting. I need to get the type before I do a direct cast to make it work - at least I think that will get me over one problem.
However I can't get this syntax to even begin to work, so I'll start here:
Dim itemType = listView.ItemContainerGenerator.ItemFromContainer(listViewItem)
Dim g As Type = GetType(itemtype)
This is the entire drag n drop implementation I'm trying:
Dim startpoint As Point
Public Sub List_PreviewMouseLeftButtonDown(sender As Object, e As MouseEventArgs)
' Store the mouse position
startpoint = e.GetPosition(Nothing)
End Sub
Private Sub List_MouseMove(sender As Object, e As MouseEventArgs)
Dim mousePos As Point = e.GetPosition(Nothing)
Dim diff As Vector = startpoint - mousePos
If e.LeftButton = MouseButtonState.Pressed And Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance Or Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance Then
Dim listView As ListView = DirectCast(sender, ListView)
Dim listViewItem As ListViewItem = FindAncestor(Of ListViewItem)(DirectCast(e.OriginalSource, DependencyObject))
Dim itemType = listView.ItemContainerGenerator.ItemFromContainer(listViewItem)
Dim g As Type = GetType(itemtype)
Dim item As String = DirectCast(listView.ItemContainerGenerator.ItemFromContainer(listViewItem), String)
Dim dragData As New DataObject("myFormat", item)
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Move)
End If
End Sub
Private Shared Function FindAncestor(Of T As DependencyObject)(current As DependencyObject) As T
Do
If TypeOf current Is T Then
Return DirectCast(current, T)
End If
current = VisualTreeHelper.GetParent(current)
Loop While current IsNot Nothing
Return Nothing
End Function
Private Sub DropList_DragEnter(sender As Object, e As DragEventArgs)
If Not e.Data.GetDataPresent("myFormat") OrElse sender = e.Source Then
e.Effects = DragDropEffects.None
End If
End Sub
Private Sub DropList_Drop(sender As Object, e As DragEventArgs)
If e.Data.GetDataPresent("myFormat") Then
Dim contact As String = TryCast(e.Data.GetData("myFormat"), String)
Dim listView As ListView = TryCast(sender, ListView)
listView.Items.Add(contact)
End If
End Sub
Here is the nested listView:
<!--DataContext="{StaticResource RcpdInsertViewSource}" This is a collectionviewsource.
RCPDInsert has a list(of string) member that is created from a single string property
and whose order needs to be alterable.
Eg rcpdInsert.template="[stuff] [more stuff]" so rcpdInsert.templateList = list(of String) from template.split("] [") -->
<ListView Grid.Column="1" Grid.Row="1" ItemsSource="{Binding}"
PreviewMouseLeftButtonDown="List_PreviewMouseLeftButtonDown"
PreviewMouseMove="List_MouseMove"
Drop="DropList_Drop"
DragEnter="DropList_DragEnter"
AllowDrop="True">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBox Text="{Binding Path=cpID}"></TextBox>
<TextBox Text="{Binding Path=fieldRef}"></TextBox>
<ListView ItemsSource="{Binding Path=InsertsList}" >
<ListView.ItemTemplate>
<DataTemplate DataType="DataClasses1:RcpdInsert.template" >
<StackPanel Orientation="Horizontal" Grid.Row="0">
<TextBlock Text="{Binding}" Margin="5" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Goal: Drag and drop reordering of child listviews, ideally being able to pull individual items from one child listView to another. When saved, the code behind will actually concat the strings back together and update the template member.
For context here are the relevant members of RcpdInsert:
Sub SetupInsertList()
_insertsList = template.Split(" ").ToList()
For Each item In InsertsList
Dim t = item
RcpdList.Add(RcpdSet.RpcdListShared.Where(Function(x) x.insertID = t).ToList())
Next
End Sub
Public Property RcpdList As New List(Of List(Of Rcpd))
Private Property _insertsList As New List(Of String)
Public Property InsertsList As List(Of String)
Get
If _insertsList.Count = 0 Then setupInsertList()
Return _insertsList
End Get
Set(value As List(Of String))
Dim combine As String = value.Aggregate("", Function(current, i) current + (i & " "))
template = combine
End Set
End Property
The casting is one issue with this, I'm hoping being able to do this part means that the others will be easier to resolve.
Thanks in advance to anyone who can help :)

Button Is TextBlock?

I am using this function to recurse thru all the controls inside a ListBoxItem and I have both a Button and a TextBlock inside the DataTemplate. It always picks the Button instead of the TextBlock. Can anyone see whats wrong with my function?
Private Function FindVisualChild(ByVal obj As DependencyObject) As TextBlock
Dim result As TextBlock = Nothing
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
Dim child As DependencyObject = TryCast(VisualTreeHelper.GetChild(obj, i), DependencyObject)
If Not child Is Nothing AndAlso TypeOf child Is DependencyObject Then
If TypeOf child Is TextBlock Then
Dim tbl As TextBlock = TryCast(child, TextBlock)
If Not tbl Is Nothing Then result = tbl
Else
Dim tbl As TextBlock = FindVisualChild(child)
If Not tbl Is Nothing Then result = tbl : Exit For
End If
End If
Next
Return result
End Function
Seems simple, but it works! Does not explain why it was confused with types below this line
Private Function FindVisualChild(ByVal obj As DependencyObject) As TextBlock
Dim result As TextBlock = Nothing
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
Dim child As DependencyObject = TryCast(VisualTreeHelper.GetChild(obj, i), DependencyObject)
If TypeOf child Is Button Then Continue For 'fixes it
If Not child Is Nothing AndAlso TypeOf child Is DependencyObject Then
If TypeOf child Is TextBlock Then
Dim tbl As TextBlock = TryCast(child, TextBlock)
If Not tbl Is Nothing Then result = tbl
Else
Dim tbl As TextBlock = FindVisualChild(child)
If Not tbl Is Nothing Then result = tbl : Exit For
End If
End If
Next
Return result
End Function

Resources