Referencing XAML Custom Control names and properties within VB Code Behind - vb.net

Sorry for the basic questions, but I've been searching the web for a number of days and can't find the answer to these questions.
I've created a Custom Control, and I will be placing a large number of instances of that Custom Control on my xaml page. In working with that Custom Control in the VB Code Behind, how do I do the following?
How do I reference the name of the Custom Control (in my VB code) which was clicked with the MouseLeftButtonDown event? For example, if I have 10 instances of my Custom Control in xaml, each with a different x:name (say 1-10), when a particular instance is clicked, how can I see which one was clicked? I've tried a number of things including e.OriginalSource.Name (which returns the component within the control which was clicked and not the name of the instance of the control).
My Custom Control consists of numerous parts and pieces (Rectangles, Lines, Text, etc). Each of these items is a part of my layer. In VB code, once I can reference a particular Control, how can I hide or change certain parts of that control (such as hiding a Line, and changing the text). Also, I need to modify more than just the control which was clicked, so I need to be able to access properties of all of the controls, not just what was clicked. For example, if I click Control instance Test1, I also need to modify Test2, Test3, and Test5 in some way.
Here is some test code I through together as part of a Silverlight project using MS Blend 2. My control is much larger, and I need 200 - 250 instances/copies of that custom control, so I really need to know which control instance/copy was clicked.
My UserControl:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="MyControl1"
x:Name="UserControl"
d:DesignWidth="60" d:DesignHeight="59">
<Grid x:Name="LayoutRoot" MouseLeftButtonDown="OnMouseClick">
<Rectangle x:Name="Rectangle1" Fill="#FFFFFFFF" Stroke="#FF000000"/>
<TextBox Background="{x:Null}" x:Name="TextBox1" Text="Test" TextWrapping="Wrap"/>
<Ellipse x:Name="Circle1" Fill="{x:Null}" Stroke="#FF000000"/>
<Path Margin="1,29,0,29" x:Name="Line1" Fill="{x:Null}" Stretch="Fill" Stroke="#FF000000" Data="M74,80 L132,80"/>
<Path Margin="0,0,1,14" x:Name="Line2" VerticalAlignment="Bottom" Height="1" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000" Data="M73,95 L131,95"/>
<Path Margin="0,0,0,4" x:Name="Line3" VerticalAlignment="Bottom" Height="1" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000" Data="M73,105 L132,105"/>
</Grid>
</UserControl>
My xaml App using the Custom Control:
<Grid x:Name="LayoutRoot">
<Tester:MyControl1 HorizontalAlignment="Left" Margin="56,54,0,0" VerticalAlignment="Top" Width="60" Height="60" x:Name="Test1"/>
<Tester:MyControl1 HorizontalAlignment="Left" Margin="116,54,0,0" VerticalAlignment="Top" Width="60" Height="60" x:Name="Test2"/>
<Tester:MyControl1 HorizontalAlignment="Left" Margin="176,54,0,0" VerticalAlignment="Top" Width="60" Height="60" x:Name="Test3"/>
<Tester:MyControl1 HorizontalAlignment="Left" Margin="236,54,0,0" VerticalAlignment="Top" Width="60" Height="60" x:Name="Test4"/>
<Tester:MyControl1 HorizontalAlignment="Left" Margin="296,54,0,0" VerticalAlignment="Top" Width="60" Height="60" x:Name="Test5"/>
</Grid>
My Custom Control VB Code:
Partial Public Class MyControl1
Public Sub New()
MyBase.New()
Me.InitializeComponent()
' Insert code required on object creation below this point.
End Sub
Private Sub OnMouseClick(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
Dim int_Temp As Integer
Dim str_InstanceName As String
str_InstanceName = "1.What code here tells me the name of the instance which was checked? Test1, Test2, etc. for example."
int_Temp = MessageBox.Show(str_InstanceName, "Testing", MessageBoxButton.OK)
'2.What code here lets me manipulate parts of my control instances (and not just the instance which was clicked)?
'I want to hide Test1.Line1 and Test2.Line3 and Test3.Circle1 and change the background of Test5.Rectangle1 for example.
End Sub
End Class
Thanks in advance, and sorry to all the C# experts that I need this in VB.

It looks like you're talking about a User Control, not a Custom Control. There is a little bit of a difference when working with the two. You can read more about those differences here:
Custom Controls Vs. User Controls
In this case, you want to be looking at the 'sender' object in your event handler. You want to cast the sender as your User Control (this will be safe since you're only using that event handler on controls of your type).
Private Sub OnMouseClick(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
Dim senderAsControl As MyControl1 = sender As MyControl1
' Get the instance name from the sender
Dim instanceName As String = senderAsControl.Name
' You can also access your children from the sender once cast
senderAsControl.Rectangel1.IsVisible = False ' Hide the rectangle
End Sub
I can't compile the code to double check myself at the moment...but it should give you the idea.

Related

How to prevent marshalling errors when accessing UI elements from non-UI threads?

I've got this function that receives a digital output pin address uscita as Integer and status high as boolean, and updates the pin-out and fills color red in a LED-like Ellipse on the UI.
Private Function comandaUscita(ByVal uscita As Integer, ByVal high As Boolean) As Boolean
If high Then
Select Case uscita
Case 1
If gpin1 IsNot Nothing Then gpin1.Write(GpioPinValue.High)
LED1.Fill = redBrush
Case 2
If gpin1 IsNot Nothing Then gpin2.Write(GpioPinValue.High)
LED2.Fill = redBrush
...
End Select
Return True
End If
End Function
The function is called every 500ms via a timer.
Debugging shows that the function is working only once.
From the second time and for all other times, the function throws an exception
The application called an interface that was marshalled for a
different thread. (Exception from HRESULT: 0x8001010E
(RPC_E_WRONG_THREAD))
How can I prevent that without using the f... dispatchers and similar cumbersome stuff.
Additionally, how to refresh the UIelements, given that no .Refresh method exists, again without using the f... dispatchers?
The old winforms didn't have these issues... ah, the old times.
My UI XAML looks something like this:
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="60"
Margin="10,10,10,569"
Width="340">
<TextBlock x:Name="DelayText1"
Text="Uscita digitale 1"
Margin="10"
TextAlignment="Center"
FontSize="26.667"
HorizontalAlignment="Right" />
<Ellipse x:Name="LED1"
Fill="LightGray"
Stroke="White"
Width="25"
Height="25"
Margin="10,-40,10,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
RenderTransformOrigin="3.524,-1.209"/>
</StackPanel>
Data Binding!
Create an LED ViewModel object, bind one to each LED UI element. Give the LED ViewModel a "Fill" property of the appropriate type. Then, every 500 ms when your timer ticks, update the property and fire off the PropertyChanged event. When the UI element sees the PropertyChanged event, it will refresh "Fill"
It's a bit of an architecture change, but you won't need to worry about using the dispatcher.
This website http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx has an OK example of binding a string to a button's text. See "Binding a property" - you're going to need to do something similar, but you'll be binding to "Fill"
This question: How can I bind a background color in WPF/XAML? is also doing something pretty similar to what you're trying to do.
Followup to address questions in comments
In your viewmodel, you would need to create a property of type "Brush" and then in the XAML, bind it to the Fill property of the ellipse like:
//The viewmodel:
public sealed class LedViewModel: INotifyPropertyChanged {
// Update this property and fire the PropertyChanged
// event to propagate the change to the UI.
public Brush LedFill { get; set; }
. . .
//In the XAML:
<Ellipse x:Name="LED Whatever"
Fill="{Binding Path=LedFill}"
. . .
Sorry - I just realized that my example is c# and you're doing VB - the XAML part will be the same - The syntax for your ViewModel will be a little different from mine.
Ok, so what I did:
Public NotInheritable Class LedViewModel
Implements INotifyPropertyChanged
Private _LEDFill As Brush
Public Property LEDfill As Brush
Get
Return _LEDFill
End Get
Set(value As Brush)
_LEDFill = value
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler
Implements INotifyPropertyChanged.PropertyChanged
End Class
in the XAML
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Height="60" Margin="10,10,10,569" Width="340">
<TextBlock x:Name="DelayText1" Text="Uscita digitale 1" Margin="10" TextAlignment="Center" FontSize="26.667" HorizontalAlignment="Right" />
<Ellipse x:Name="LED1" Fill="{Binding Path=LEDfill}" Stroke="White" Width="25" Height="25" Margin="10,-40,10,0" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="3.524,-1.209"/>
</StackPanel>
And in the code? LED1.fill = whatever?

Get ListBox DataContext from UserControl

I have a UserControl with a button inside it. The UserControl its DataContext is one of my models so I can bind to it. However, for the button I want to be able to call a method from my viewmodel. The DataContext of the ListBox is this ViewModel.
Because my ContextMenu also needs the same DataContext I've bound them like this:
Command="{Binding Path=DataContext.AttendEventCommand, ElementName=EventListBox}"
Calling the EventListBox element and using its DataContext to call the AttendEventCommand. However I would like to call the AttendEventCommand from a button on the UserControl. I tried doing it the same way but sadly it doesn't work.
My data context is set like this:
DataContext="{Binding Path=EventList, Source={StaticResource Locator}}
My listbox code:
<ListBox x:Name="EventListBox" ItemsSource="{Binding Occurrences}" Margin="0,50,0,0" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<uctl:EventListItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="auto" Height="auto">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu IsZoomEnabled="True" x:Name="ContextMenu">
<toolkit:MenuItem x:Name="Going" Header="{Binding AttendingText}" Command="{Binding Path=DataContext.AttendEventCommand, ElementName=EventListBox}" CommandParameter="{Binding}"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</uctl:EventListItem>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And my UserControl's button:
<Button Grid.RowSpan="3" Grid.Column="5" Opacity="0" Command="{Binding Path=DataContext.AttendEventCommand, ElementName=EventListBox}" CommandParameter="{Binding}"/>
I believe your problem is not that what you want to do is not working; instead, your design appears to be wrong.
What you have now is like this:
You have a Window that has a DataContext and a ListBox. The ListBox has an ItemsSource, which we'll assume is some IEnumerable<Occurrence>.
The appearance of each item in your ListBox is an EventListItem, which is a UserControl that contains at least one Button.
You want this Button's Command to call a method on your Window's DataContext.
This last sentence is wrong. The fact that the item has the button implies that it does something that is related to the item, not the window contents. If this is not true, then the visual design of the window and listbox items should probably be reconsidered.
If the button is in fact affecting the item, then you should not call a method on your Window's DataContext, but instead call a method on your item's DataContext.
The solution is to wrap your model object Occurrence in a view model class of its own. Let's call it OccurrenceViewModel. Your ListBox's ItemsSource would be some form of IEnumerable<OccurrenceViewModel>. Because it's a view model, it's allowed to implement Command methods, which can then in one way or another manipulate the Occurrence, either directly or preferably by passing it to some class that implements the use case.
The DataContext of your EventListItem will be a Model of your ItemsSource because it is part of the DataTemplate. So you have to set it explicitly.
Refer to How to implement a navigation button for some of the code I'll be using as a solution.
Lets assume your custom UserControlis very basic like so:
<UserControl>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Button Command="{Binding DataContext.SimpleCommand}" CommandParameter="1"></Button>
</Grid>
</UserControl>
Where SimpleCommand is the Command you want to call in the Top View Model.
Then you have to change your DataTemplate to
<DataTemplate>
<Grid>
<uctl:EventListItem
DataContext="{Binding ElementName=myListBox}"/>
<!-- more xaml -->
</Grid>
</DataTemplate>
Your <toolkit:ContextMenu> should work as is.
Put a break point at the Execute function of the ICommand and you will see it will get
hit there everytime.
public void Execute(object parameter)
{
// logic to execute when user hits the command
int debug_var = 0; // put a break point here
}

Get the selected index in a LongListSelector

I'm using a LongListSelector for a WP8 app. I searched on several websites but did not found if there was a way to know the index of the item the user taped in the List.
If anyone has an idea, it would be nice.
Thanks
`
<phone:LongListSelector x:Name="ListeNotes" Height="535" Width="426" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="36" Margin="54,0,0,0">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Titre}"/>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
`
I'd create in code-beind or ViewModel (depending on what are you using), public variable
Public Int32 itemSelectedIndex {get;set;} //This is a public variable, therefore add it inside your class
and bind it to SelectedIndex as following:
<phone:LongListSelector x:Name="ListeNotes" Height="535" Width="426" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="36" Margin="54,0,0,0" SelectedIndex ="{Binding itemSelectedIndex, UpdateSourceTrigger = PropertyChanged}">
don't forget to add datacontext reference in code behind
Public void MainWindow()
{
InitializeComponents();
this.DataContext = this;//this makes sure that you can bind public varibles to XAML
}
Afterwards you simply reference itemSelectedIndex anywhere in your code and it will return selected value (e.g. System.Windows.MessageBox.Show(itemSelectedIndex.ToString());
In the handler:
SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ListeNotes.SelectionChanged`
The "sender" is the ListBox. So all you have to do is cast "sender" to ListBox, and use its SelectedIndex property.
Dim listBox As ListBox = CType(sender, ListBox)
Dim tappedIndex = listBox.SelectedIndex
you can get it via
Dim num As Integer = (sender as LonglistSelector).Datasource.IndexOf((sender as LonglistSelector).SelectedItem)
The solution was not found...
In fact I just said the user can't have twice the same Note object, and I use the IndexOf method in my List(Of Note) to get the index of the SelectedItem in the LongListSelector.

XAML button content changing based on VB variable

Beginner here. I want to create a button in XAML which has text based on a variable in VB. I have the variable set in VB with
Dim ButtonName1 = "Test"
What will I need to do to make the button content display ButtonName1?
It is really quite easy. Your Button has a Name Property that you can use to reference it in your code behind, note in this example it is Button1 you just need to assign your string to the Button's Content Property
Xaml
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="Button1" VerticalAlignment="Top" Width="75" />
xaml.vb
Dim ButtonName1 = "Test"
Button1.Content = ButtonName1

Binding events to buttons in an ItemsControl

I have a windows phone 7 app with some xaml that looks like:
<Grid x:Name="ContentGrid" Grid.Row="1">
<ItemsControl x:Name="MyView" ItemTemplate="{StaticResource MyInner}"/>
</Grid>
The item template here looks like:
<DataTemplate x:Key="MyInner">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{StaticResource MyInner_Item}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
And finally, the MyInner_Item template looks like:
<DataTemplate x:Key="MyInner_Item">
<Button x:Name="MyButton">
<Button.Template>
<ControlTemplate>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="myborder">
<Image Source="{Binding Path=ImageUri}" Width="{Binding Path=Width}" Height="{Binding Path=Height}" Stretch="Fill" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
So, it's an ItemsControl, which contains an ItemsControl, which contains buttons. This essentially creates a 2D array of buttons.
What I want to do is add an event handler to the Click event of the buttons.
Here's the catch: the code that sits behind this is written in F#. I can't, to the best of my knowledge, specify my event handler in the XAML, as F# doesn't talk to WPF in any nice way. So I need to add my event handler(s) manually in code.
Is there an easy way of doing this?
Currently, I have some F# which looks like:
let myView : ItemsControl = this?MyView
do myView.ItemsSource <- viewModel.BoardData
Here, the BoardData is a list of lists.
I was wondering if it's possible to loop through the controls in the ItemsControl, to add my event handlers? I'm having a bit of trouble doing this though. For example, in the following:
let container = myView.ItemContainerGenerator.ContainerFromItem(myView.Items.[0])
...sets container to null. In fact, all the methods I've tried from myView.ItemContainerGenerator returns null.
So, how should I go about attaching my event handler, so that I can respond to the buttons being clicked?
I have not done any Windows 7 Phone development, but I have done plenty of XAML + Silverlight development with C# and now I'm getting into doing some F# development. The approach I would take is by not using event handler's at all. Since you are using a button, make a class that derives from ICommand and add that type as a public property on your ViewModel so you could bind it to the Command property of the button. The benefits of using the ICommand interface over event handlers is that you could also have a condition on when the button is enabled to do the action.
Also, take notice that when you are doing binding expressions within (i.e. ItemTemplate) items in an ItemsControl control, the scope of what properties you can bind to are reduced to the properties of the current item. So all of the properties of the ViewModel are out of scope, unless you specify it fully i.e. <Button Command={Binding Source=ViewModel, Path=Property1.Property2.etc} />. Let me know if this helped.