Mouse events not called in VB WPF, does z-order matter? - wpf

There are many explanation in google, but I still struggling to implement mouse events.
I have tried to use MouseEnter="Canvas1_MouseEnter" in canvas properties, but it doesn't work.
I tried to add manually, . it doesn't work.
I also tried to use AddHandler Canvas1.MouseEnter, AddressOf Canvas1_MouseEnter in Private Sub main_Loaded(sender As Object, e As RoutedEventArgs) Handles main.Loaded method, it doesn't work too.
Then I tried to move AddHandler Canvas1.MouseEnter, AddressOf Canvas1_MouseEnterto Public Sub InitializeComponent() in MainWindow.g.i.vb file. it didn't work
I have no idea how to implement mouse events. :(
so I tried to implement MouseEnter to the form it self, the event called properly.
Does it mean z-order matter?
Here is the xaml file
<Window x:Name="main" x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="620.242" Width="827.423">
<Grid HorizontalAlignment="Left" Height="575" Margin="10,10,0,0" VerticalAlignment="Top" Width="807">
<Canvas x:Name="Canvas1" HorizontalAlignment="Left" Height="555" Margin="794,368,-774,-348" VerticalAlignment="Top" Width="787" />
</Grid>
</Window>
and the hanlder sub
'this code generated automatically
Private Sub Canvas1_MouseEnter(sender As Object, e As MouseEventArgs)
MessageBox.Show("event called")
End Sub
How could I implement mouse event to my canvas?
Any help appreciated :)

UI elements in WPF do not receive mouse events (without mouse capture) outside their rendered area. A Panel (like your Canvas) does not render anything outside the area of its child elements unless you define a non-null value for its Background property.
You may set a Background for your Canvas like shown below, which makes it render the entire area defined by its ActualWidth and ActualHeight with a transparent brush:
<Canvas x:Name="Canvas1" Background="Transparent"
MouseEnter="Canvas1_MouseEnter" ... />

Your canvas does not have the MouseEnter event. Use it like this
<Grid>
<Canvas HorizontalAlignment="Left" Height="158" Margin="191,85,0,0" VerticalAlignment="Top"
Width="221" Background="#FF575757" MouseEnter="Canvas_MouseEnter"/>
</Grid>
And then the Sub for the Event:
Private Sub Canvas_MouseEnter(sender As Object, e As MouseEventArgs)
MessageBox.Show("Hello?")
End Sub

Related

How to prevent playing sound during Window is opening?

XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl x:Name="TabControl1">
<TabItem Header="General"/>
<TabItem Header="Security" />
<TabItem Header="Details" />
</TabControl>
</Grid>
</Window>
vb.net
Class MainWindow
Private Sub TabControl1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles TabControl1.SelectionChanged
System.Media.SystemSounds.Asterisk.Play()
End Sub
End Class
I want to play sound when User clicks every TabItem.
The codes above play sound during Window is opening which I dont want.
So, how to prevent playing sound during Window is opening?
There is the property IsLoaded in Window, that can be used for that purpose.
You could check for the value of IsLoaded to determine whether to play the sound or not (it may well be that there are syntactic errors, my VB.NET is not that good ;) )
Class MainWindow
Private Sub TabControl1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles TabControl1.SelectionChanged
If IsLoaded Then
System.Media.SystemSounds.Asterisk.Play()
End If
End Sub
End Class
The first selection of the first tab should occur before Loaded is called, hence IsLoaded should be false and no sound should be played.

How to make Dispatcher.BeginInvoke run in background in VB.net?

I need to run some part of application in background and allow user to update the UI while the sub is running in the background. I searched and I found out that in WPF I should use Dispatcher. Problem is even when I use dispatcher still my GUI is not usable till the all subs will finish. I attached a code here so you can have better perspective of what I mean.
For example in this code when a user run the application, system should run a new thread that will change the text of first textbox while use can update the text of the second textbox.
I am wondering if I am doing this right or not. Can anybody help me with this?
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:testDispacher"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<Grid>
<StackPanel>
<TextBlock>UCtextbox:</TextBlock>
<src:ToBeRunByDispacherUC x:Name="UC1" />
<TextBlock>Windowstxtbox:</TextBlock>
<TextBox x:Name="txtBox2" Width="100" Height="30"/>
</StackPanel>
</Grid>
Class Window1
Delegate Sub runSub()
Dim setTxt As runSub
Public Sub New()
InitializeComponent()
setTxt = AddressOf UC1.setTxtBox
End Sub
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
UC1.IsEnabled = False
Dispatcher.Invoke(setTxt, Windows.Threading.DispatcherPriority.Background)
End Sub
End Class
<UserControl x:Class="ToBeRunByDispacherUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBox x:Name="txtBox1" Width="100" Height="30"/>
</Grid>
Partial Public Class ToBeRunByDispacherUC
Public Sub setTxtBox()
Dim j As Integer = 0
For i As Integer = 0 To 10
j += 1
System.Threading.Thread.Sleep(1000)
Next
txtBox1.Text = "End"
Me.IsEnabled = True
End Sub
End Class
The dispatcher should be used to update UI objects from a separate thread, it does not actually spawn up the thread for you. If you are using .NET 4.0 or higher, you can use the TPL library to spawn your thread, do your work, then update your UI object via the dispatcher from the background thread.
Task.Factory.StartNew(Sub() DoBackgroundWork())
Then, inside DoBackgroundWork whenever you want to update your UI...
Dispatcher.BeginInvoke(Sub() txtBox1.Text = "End")
you can call to the general application.
Application.Current.Dispatcher.Invoke()

Showing/Closing forms in wpf

Im developing an application using wpf template I have these 2 windows:
MainWindow.xaml and
JungleTimer.vb which is a Windows Form
I have a button in my main windows which shows JungleTimer form using this code:
Dim JungleTimer As New JungleTimer
JungleTimer.Show()
But as you see, clicking this button multiple times will show multiple JungleTime form.
I tried to use this code to check if JungleTimer is visible but it doesn't work:
Dim JungleTimer As New JungleTimer
If JungleTimer.Visible = False Then
JungleTimer.Show()
End If
I also need the code to close the JungleTimer form.
As you are creating a new JungleTimer each time you click the button you will always get a new instance of the window. What you need to do is declare a field within the class of the type JungleTimer. Initially this will be null (Nothing). When you click the button, check if this field has a value or is still null. If still null, set it to a new JungleTimer and show it. If it isn't null, activate the existing window without creating a new instance. you'll also need to detect when the window closes so that you can set the field back to null.
For a demo, create a new WPF application with two windows, MainWindow (the main window) and JungleTimer.
XAML for MainWindow:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel VerticalAlignment="Center">
<Button Width="100" Height="30" Click="Jungle_Click">Jungle Me</Button>
<Button Width="100" Height="30" Click="DeJungle_Click">De-Jungle Me</Button>
</StackPanel>
VB for MainWindow (sorry if it's clumsy, I haven't done VB for ten years or so):
Class MainWindow
Private WithEvents _jungleTimer As JungleTimer
Private Sub Jungle_Click(sender As Object, e As RoutedEventArgs)
If _jungleTimer Is Nothing Then
_jungleTimer = New JungleTimer
_jungleTimer.Show()
Else
_jungleTimer.Activate()
End If
End Sub
Private Sub DeJungle_Click(sender As Object, e As RoutedEventArgs)
If Not _jungleTimer Is Nothing Then
_jungleTimer.Hide()
_jungleTimer = Nothing
End If
End Sub
Private Sub CloseHandler() Handles _jungleTimer.Closed
_jungleTimer = Nothing
End Sub
End Class
XAML for JungleWindow:
<Window x:Class="JungleTimer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="JungleTimer" Height="300" Width="300">
<Grid>
<Label HorizontalAlignment="Center" VerticalAlignment="Center">
Jungle!
</Label>
</Grid>

WPF: Placing a textbox randomly on a canvas

I have a canvas of sorts derived from a 'Panel' used for custom drawing of lines and other geometries, all from VB code. I got this approach from a book and I'm not sure it's the best approach. The drawing part works so far for me.
But what I need is to put a textbox control on the control containing text which can be edited by the user. The textbox needs to be placed at coordinates determined dynamically and later deleted. There will probably be other controls handled so.
The following code does nothing:
tb = New TextBox()
tb.Text = "How now brown cow?"
tb.BorderThickness = New Thickness(3)
tb.BorderBrush = Brushes.CadetBlue
drawingSurface.Children.Add(tb)
This is the definition of my DrawingCanvas:
Public Class DrawingCanvas
Inherits Panel
Private visuals As New List(Of Visual)()
Private hits As New List(Of DrawingVisual)()
Protected Overrides Function GetVisualChild(ByVal index As Integer) As Visual
Return visuals(index)
End Function
Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
Get
Return visuals.Count
End Get
End Property
Public Sub AddVisual(ByVal visual As Visual)
visuals.Add(visual)
MyBase.AddVisualChild(visual)
MyBase.AddLogicalChild(visual)
End Sub
Public Sub DeleteVisual(ByVal visual As Visual)
visuals.Remove(visual)
MyBase.RemoveVisualChild(visual)
MyBase.RemoveLogicalChild(visual)
End Sub
Public Function GetVisual(ByVal point As Point) As DrawingVisual
Dim hitResult As HitTestResult = VisualTreeHelper.HitTest(Me, point)
Return TryCast(hitResult.VisualHit, DrawingVisual)
End Function
Public Function GetVisuals(ByVal region As Geometry) As List(Of DrawingVisual)
hits.Clear()
Dim parameters As New GeometryHitTestParameters(region)
Dim callback As New HitTestResultCallback(AddressOf Me.HitTestCallback)
VisualTreeHelper.HitTest(Me, Nothing, callback, parameters)
Return hits
End Function
Private Function HitTestCallback(ByVal result As HitTestResult) As HitTestResultBehavior
Dim geometryResult As GeometryHitTestResult = CType(result, GeometryHitTestResult)
Dim visual As DrawingVisual = TryCast(result.VisualHit, DrawingVisual)
If visual IsNot Nothing AndAlso geometryResult.IntersectionDetail = IntersectionDetail.FullyInside Then
hits.Add(visual)
MsgBox("Ouch")
End If
Return HitTestResultBehavior.Continue
End Function
End Class
Here is the XAML. I added a textbox to the DrawingCanvas just to see if something appears. Nothing did. In fact, I want to do this in code, not XAML. I thought I could hide or move it around dynamically.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Music"
Title="MainWindow" Height="539" Width="892">
<DockPanel>
<Menu DockPanel.Dock="Top" Name="MainMenu" VerticalAlignment="Top" Height="25">
<MenuItem Name="File" Header="File">
<MenuItem Name="Open" Header="Bla bla..."/>
</MenuItem>
</Menu>
<local:DrawingCanvas DockPanel.Dock="Bottom" x:Name="drawingSurface" RenderTransformOrigin="0.5,0.5" >
<TextBox Height="0" Name="TextBox1" Width="45" Text="How now brown cow?" />
</local:DrawingCanvas>
</DockPanel>
</Window>
Thanks for helping a nooby. A solution would be very useful for me. This was easy with windows forms, but I need the drawing speed of WPF.
I think you are a bit off here. In WPF you have a control called Canvas. I would suggest you use that instead of your own "DrawingCanvas", which I can't get to work btw. :( (For some reason I cant create code blocks so if someone can edit it I would be pleased)
Anyhow,
<local:DrawingCanvas DockPanel.Dock="Bottom" x:Name="drawingSurface" RenderTransformOrigin="0.5,0.5" >
<TextBox Height="0" Name="TextBox1" Width="45" Text="How now brown cow?" />
</local:DrawingCanvas>
Turns into:
<Canvas x:Name="drawingSurface">
</Canvas>
And then to add a textbox just do as your current code:
Dim tb as New TextBox
drawingSurface.Children.Add(tb)
This should give you what you need.
Heres the code for adding a rectangle to your canvas.
Private Sub DrawBackground()
Dim Rect As New Rectangle()
Rect.Height = 50
Rect.Width = 50
Rect.Fill = Brushes.Cornsilk
drawingSurface.SetTop(Rect, 30)
drawingSurface.SetLeft(Rect, 100)
drawingSurface.Children.Add(Rect)
End Sub
I'll add another answer that might be more in the line of what you are looking for. This is a class that inherits from Canvas that will allow you to draw stuff in the same way as say you do in your comment.
I also creates a textbox on a random location when it is created.
Public Class DrawingCanvas
Inherits Canvas
Public RandomTextBox As New TextBox
Protected Overrides Sub OnRender(dc As System.Windows.Media.DrawingContext)
Dim brush As Brush = Brushes.Black
Dim drawingPen As Pen = New Pen(Brushes.Green, 3)
dc.DrawRectangle(brush, drawingPen, New Rect(5, 5, Me.ActualWidth - 5, Me.ActualHeight - 5))
RandomTextBox.Text = "Herpdiderp"
If Not Me.Children.Contains(RandomTextBox) Then
Dim r As New Random()
RandomTextBox.Height = 23
RandomTextBox.Width = 100
Me.SetTop(RandomTextBox, r.Next(0, Me.ActualHeight - RandomTextBox.Height))
Me.SetLeft(RandomTextBox, r.Next(0, Me.ActualWidth - RandomTextBox.Width))
Me.Children.Add(RandomTextBox)
End If
End Sub
End Class
This is not a full answer. #WozzeC, you were right about using the canvas - almost.
I have managed to solve this in xaml alone - I want to eventually solve it in vb.net.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DockPanel HorizontalAlignment="Stretch" Name="DockPanel1" VerticalAlignment="Stretch" >
<Menu Height="23" DockPanel.Dock="Top" Name="Menu1" VerticalAlignment="Top" />
<Canvas Name="Canvas1" Background="Aquamarine">
<TextBox Canvas.Left="118" HorizontalScrollBarVisibility="Disabled" Canvas.Top="81" AcceptsReturn="True" Height="auto" Name="TextBox1" Width="68" Text="Herpdiderp" BorderThickness="0" Background="Aquamarine" />
</Canvas>
</DockPanel>
</Grid>
And here is a piece of code that expands the text as needed. I think it's almost totally cool. It expands both to the right and downwards, as if you're actually typing on the form. It adds a little too much on the right, but it's not visible in this version because the background color is the same.
Here is the event code that expands it to the right.
Imports System.Globalization
Class MainWindow
Private Sub TextBox1_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs) Handles TextBox1.TextChanged
Dim ft As New FormattedText(TextBox1.Text, CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, New Typeface("Verdana"), 16, Brushes.Black)
TextBox1.Width = ft.Width
End Sub
End Class
I tried this with my existing solution and the textbox does not appear. I made the DrawingCanvas into a plain Canvas and commented out all the code referring to the DrawingCanvas. And the textbox does appear. The problem is this: I need the functionality in the DrawingCanvas - which derives from Canvas. But because the baseclass methods are Protected, I can't get to them. I can only use them in a derived class, unless there is another way I don't know about.
Any ideas about how to solve this?

How to scroll through a listbox with down and up arrow keys in WPF?

I am using an AutoCompleteBox UserControl that uses a listbox inside of a Popup. Everything works fine, the user can type in, view suggested results, and click the result which retrieves information.
i have been trying to add arrow key event handlers so they can scroll down through the listbox that pops up with up and down arrows, and hit enter to select a result.
I have a PreviewKeyDown event on the TextBox the user types in, and inside of that:
If e.Key = Key.Up Then
txtNamesListBox.Focus()
End If
If e.Key = Key.Down Then
txtNamesListBox.Focus()
End If
The event fires and i can reach these functions by setting a break point, however it doesnt set the focus to the ListBox that pops up, and i cant scroll through the results at all. is this even possible and am i way off in my attempt? I am at a loss, thanks!
You need to get a reference of the ScrollViewer residing inside ListBox template and use it to scroll content.
Something like following should do. (NOTE: I have not tested this code.)
Example:
ScrollViewer scrollViewer = (ScrollViewer)txtNamesListBox.Template.FindControl("ScrollViewer");
// call methods on scrollViewer
Edit:
Worked out simpler soultion. Idea is wrapping the ListBox with a ScrollViewer and disabling ListBox's scrolling.
Example:
XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525"
Loaded="Window_Loaded">
<Grid>
<StackPanel>
<RepeatButton Click="ScrollUp">Scroll Up</RepeatButton>
<ScrollViewer Name="scrollViewer"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
MaxHeight="200">
<ListBox Name="txtNamesListBox"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"></ListBox>
</ScrollViewer>
<RepeatButton Click="ScrollDown">Scroll Down</RepeatButton>
</StackPanel>
</Grid>
</Window>
Code Behind:
Class MainWindow
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
txtNamesListBox.ItemsSource = Enumerable.Range(1, 50).Select(Function(i As Int32) i.ToString())
End Sub
Private Sub ScrollDown(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + 10)
End Sub
Private Sub ScrollUp(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - 10)
End Sub
End Class

Resources