Accessing elements inside a DataTemplate in a TabItem - wpf

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

Related

Reference to GUI element by name as string

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");

How can i make text printed to a label bold, italics or underlined?

Dim enteredtext AS STRING = mytextbox.text
mylabel.content = enteredtext
How can i make the entered text that a user enters in a textbox be printed to a label as either bold italic or underlined. These three options shall be enabled with a radio button.
You can set the font style for them.
e.g.:
mylabel.FontStyle = FontStyles.Italic
You can refer the below code.
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<RadioButton x:Name="rdBold" Checked="RadioButton_Checked" Content="Bold" GroupName="format"/>
<RadioButton x:Name="rdItalics" Checked="rdItalics_Checked" Content="Italics" GroupName="format"/>
<RadioButton x:Name="rdUnderline" Checked="rdUnderline_Checked" Content="Underline" GroupName="format"/>
</StackPanel>
<TextBox x:Name="txtBx" Width="200" Height="20"/>
<TextBlock x:Name="txtBlk" Text="{Binding ElementName=txtBx,Path=Text}"/>
</StackPanel>
</Grid>
Class MainWindow
Private Sub RadioButton_Checked(sender As Object, e As RoutedEventArgs)
txtBlk.FontWeight = FontWeights.Bold
txtBlk.FontStyle = FontStyles.Normal
txtBlk.TextDecorations = Nothing
End Sub
Private Sub rdItalics_Checked(sender As Object, e As RoutedEventArgs)
txtBlk.FontWeight = FontWeights.Normal
txtBlk.FontStyle = FontStyles.Italic
txtBlk.TextDecorations = Nothing
End Sub
Private Sub rdUnderline_Checked(sender As Object, e As RoutedEventArgs)
txtBlk.FontWeight = FontWeights.Normal
txtBlk.FontStyle = FontStyles.Normal
txtBlk.TextDecorations = TextDecorations.Underline
End Sub
End Class
As above if you are only doing this sometimes and wish to do it in the code behind you can use mylabel.FontStyle = FontStyles.Italic or if the label always needs to be bold you can look at Font section in the properties tab and choose colur, size, bold, italics, underline, strikeout etc from there.

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 :)

Programmatically bind an image's canvas point in WPF

In my program I am dynamically creating images to be added into a canvas in a WPF window.
My question is: How can I bind the canvas.left and canvas.right point to a class property.
If the image existed before run-time I would make and bind it like this in XAML/WPF:
<Image Height="26" HorizontalAlignment="Left" Canvas.Left="{Binding left}" Canvas.Top="{Binding top}" Name="Image1" Stretch="Fill" VerticalAlignment="Top" Width="28" Source="/imageTest;component/Images/blue-pin-md.png" />
What I have in VB.net:
'Create array of images
Dim myImages(5) as myImage
For i = 0 to myImages.count - 1
myImages(i) = New myImage
'set datacontext if I can bind
myImages(i).image.DataContext = myImages(i)
canvas1.Children.Add(myImages(i).image)
Next
myImage class:
Imports System.ComponentModel
Public Class myImage
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Private Property _image as New Image
Private Property _left As Double
Private Property _top As Double
Private Shared Property r As New Random()
Public Sub New()
_image.Width = 28
_image.Height = 26
_image.Source = New System.Windows.Media.Imaging.BitmapImage(New Uri("/imageTest;component/Images/blue-pin-md.png", UriKind.Relative))
'the below works without binding if I just want to set and leave them in one place but I would like to bind them so that I can move them relative to other data
'_left = r.Next(0, System.Windows.SystemParameters.PrimaryScreenWidth)
'_top = r.Next(0, System.Windows.SystemParameters.PrimaryScreenHeight)
End Sub
Public ReadOnly Property image As Image
Get
Return _image
End Get
End Property
Public ReadOnly Property left As Double
Get
Return _left
End Get
End Property
Public ReadOnly Property top As Double
Get
Return _top
End Get
End Property
'a possible move method that would take advantage of the binding
Public Sub move()
_top += 1
_left += 1
NotifyPropertyChanged("left")
NotifyPropertyChanged("top")
End Sub
Not sure how to write it in VB, but in C# it would look like this:
var leftBinding = new Binding
{
Path = new PropertyPath("left"),
Source = myImages[i]
};
var topBinding = new Binding
{
Path = new PropertyPath("top"),
Source = myImages[i]
};
myImages[i].image.SetBinding(Canvas.LeftProperty, leftBinding);
myImages[i].image.SetBinding(Canvas.TopProperty, topBinding);
Or perhaps simpler, with DataContext:
myImages[i].image.DataContext = myImages[i];
myImages[i].image.SetBinding(Canvas.LeftProperty, "left");
myImages[i].image.SetBinding(Canvas.TopProperty, "top");
Thanks to Clemens C# code I was able to get it working. Below is the code that I used. The .SetBindings(,) was the key for me.
For i = 0 To myImages.Count - 1
myImages(i) = New myImage
myImages(i).image.DataContext = myImages(i)
myImages(i).image.SetBinding(Canvas.LeftProperty, "left")
myImages(i).image.SetBinding(Canvas.TopProperty, "top")
canvas1.Children.Add(myImages(i).image)
Next

Binding ListView Throws Window must be the root of the tree. Cannot add Window as a child of Visual

I am attempting to bind a ObservableCollection to a ListView but when I attempt to debug the program VS2010 is throwing the exception "Window must be the root of the tree. Cannot add Window as a child of Visual"
If I was to remove the attempted ItemsSource binding the WPF window opens (obviously without any data in the listView). I have create a Sub in my codebehind that parses an XML file and adds values to the ObservableCollection, I then bind the ListView.
I have stepped through the code, and the error appears to be in the XAML as the Sub completes without error, the program errors after the CodeBehind has completed, everytime.
My XAML file is as follows:
<Window x:Class="test_ListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="test_ListView" Height="300" Width="300">
<Grid>
<ListView Height="105" HorizontalAlignment="Left" Name="lst_EmergencyContacts" VerticalAlignment="Top" Width="478">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="160" />
<GridViewColumn DisplayMemberBinding="{Binding emContactsNumber}" Header="Number" Width="70" />
<GridViewColumn DisplayMemberBinding="{Binding emContactsPhoneCoverage}" Header="Phone Coverage" Width="95" />
<GridViewColumn DisplayMemberBinding="{Binding distListPhone}" Header="Distance" Width="60" />
<GridViewColumn Header="More" Width="60" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
While I believe the error is within the XAML, I will include the CodeBehind details, just in case (as the program will run without the error if the Sub isnt called that binds the ListView).
Public Property emContactsName() As String
Get
Return em_Name
End Get
Set(ByVal value As String)
em_Name = value
End Set
End Property
Private em_Name As String
Public Property emContactsNumber() As String
Get
Return em_Number
End Get
Set(ByVal value As String)
em_Number = value
End Set
End Property
Private em_Number As String
Public Property emContacts27Mhz() As String
Get
Return em_27Mhz
End Get
Set(ByVal value As String)
em_27Mhz = value
End Set
End Property
Private em_27Mhz As String
Public Property emContactsUhf() As String
Get
Return em_Uhf
End Get
Set(ByVal value As String)
em_Uhf = value
End Set
End Property
Private em_Uhf As String
Public Property emContactsVhf() As String
Get
Return em_Vhf
End Get
Set(ByVal value As String)
em_Vhf = value
End Set
End Property
Private em_Vhf As String
Public Property IsSelected() As Boolean
Get
Return m_IsSelected
End Get
Set(ByVal value As Boolean)
m_IsSelected = value
End Set
End Property
Private m_IsSelected As Boolean
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
getEmergencyContactsData("\", "EmergencyContacts.xml")
End Sub
Private Sub getEmergencyContactsData(ByVal docPath As String, ByVal docName As String)
Try
Dim xDoc As XmlDocument = New XmlDocument
Dim nList As XmlNodeList
Dim node As XmlNode
xDoc.Load(docPath + docName)
nList = xDoc.SelectNodes("/items/item")
Dim emergencyContactsCollection As New ObservableCollection(Of test_googleLookup)()
For Each node In nList
Dim eID = node.Attributes.GetNamedItem("id").Value
Dim eName = node.ChildNodes.Item(0).InnerText
Dim eNumber = node.ChildNodes.Item(1).InnerText
Dim eRadio27Mhz = node.ChildNodes.Item(2).InnerText
Dim eRadioUhf = node.ChildNodes.Item(3).InnerText
Dim eRadioVhf = node.ChildNodes.Item(4).InnerText
emergencyContactsCollection.Add(New test_googleLookup() With {.emContactsName = eName, .emContactsNumber = eNumber, .emContacts27Mhz = eRadio27Mhz, .emContactsUhf = eRadioUhf, .emContactsVhf = eRadioVhf, .IsSelected = 0})
Next
' Add to resources
Resources("emergencyContactsCollection") = emergencyContactsCollection
lst_EmergencyContacts.ItemsSource = emergencyContactsCollection
Catch
End Try
End Sub
I can see when stepping through the debug that the XML file is being correctly parsed and the values being added to the ObservableCollection.
If anyone can provide any assistance it would be greatly appreciated.
Thanks.
Try changing emergencyContactsCollection into a public property (ie.,EmergencyContacts) of test_ListView. Set the DataContext of lst_EmergencyContacts to be EmergencyContactsCollection.
<Window x:Class="test_ListView" ... DataContext="{Binding RelativeSource={RelativeSource Self}}">
<ListView Name="lst_EmergencyContacts" ... DataContext="{Binding Path=EmergencyContacts}">
Managing your data binding in this way often makes things much easier. Even better would be to separate out the data bound Properties to their own ViewModel.
I ended up using ListBoxes, below is the code I used, in case anyone is interested:
<ListBox Grid.Row="1" Grid.Column="0" Grid.RowSpan="4" Grid.ColumnSpan="3" HorizontalAlignment="Stretch" ItemsSource="{DynamicResource emergencyContactsCollection}" Name="lst_emergencyServices" VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding emerContIsSelected, Mode=TwoWay}" />
<TextBlock Text="{Binding emerContName}" />
<TextBlock Text="{Binding emerContNumber}" />
<TextBlock Text="{Binding emerContRadio27mhz}" />
<TextBlock Text="{Binding emerContRadioUhf}" />
<TextBlock Text="{Binding emerContRadioVhf}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thank you for your time and assistance.
Matt

Resources