Click Menu to call UserControl to MainWindow's Grid - wpf

I want to click the menu “Now Playing” then call UserControl to MainWindow's Grid
MainWindow.xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Menu Height="30" VerticalAlignment="Top">
<MenuItem Height="30" Header="Now Playing" x:Name="NowPlaying" Click="NowPlaying_Click" />
<MenuItem Height="30" Header="Music Library" Click="MenuItem_Click" />
<MenuItem Height="30" Header="Play Schedule" />
<MenuItem Height="30" Header="Control" />
<MenuItem Height="30" Header="Option" />
</Menu>
<Grid Margin="0,30,0,0" x:Name="MainBoard">
(UserControl"NowPlaying")
</Grid>
</Grid>
MainWindow.xaml.vb
Private Sub NowPlaying_Click(sender As Object, e As RoutedEventArgs) Handles NowPlaying.Click
Dim MainWindow = TryCast(System.Windows.Window.GetWindow(Me), MainWindow)
Dim MainWindowBoard = MainWindow.MainBoard
Dim NowPlayingBoard As UserControl = New NowPlaying
#???
End Sub
UserControl.xaml:
<Grid>
<Grid Width="800" Margin="0,0,0,100"/>
<Grid Margin="0,350,0,0" Background="#FFFFDADA">
<Slider Margin="10,10,10,0" VerticalAlignment="Top" Height="30"/>
</Grid>
</Grid>

You add controls to a Panel e.g. Grid by using the Panel.Children property.
Your code can be simplified. As the event handler is defined in the code-behind, you already have direct access to all its fields and properties by simply using Me. Calling Window.GetWindow is redundant.
Private Sub NowPlaying_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim nowPlayingControl As UserControl = New NowPlaying()
Me.MainBoard.Children.Add(nowPlayingControl)
End Sub

My Answer:
Private Sub NowPlaying_Click(sender As Object, e As RoutedEventArgs) Handles NowPlaying.Click
Dim NowPlayingBoard As UserControl = New NowPlaying
Me.MainBoard.Children.Clear()
Me.MainBoard.Children.Add(NowPlayingBoard)
End Sub

Related

Click a Button from UserControl to set a value to MainWindow's columndefinition

I want to write a code from UserControl's Button to set value to MainWindow's ColumnDefinition.
When clicked button I need change to this:
<ColumnDefinition Width="0" MaxWidth="400" MinWidth="10" x:Name="MainMenu" x:FieldModifier="public" />
Below is my code:
MainWindow.xaml
<Grid Margin="0,0,0,0">
<local:DockPanelTop />
<Grid Margin="5,45,5,25" x:Name="MainGrid" x:FieldModifier="public">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" MaxWidth="400" MinWidth="10" x:Name="MainMenu" x:FieldModifier="public" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
DockPanelTop.xaml
<UserControl x:Class="DockPanelTop"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp4"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<DockPanel LastChildFill="False" VerticalAlignment="Top" Background="Gray" Margin="0,0,0,0" Height="40">
<Button x:Name="HideMenu" Content="Hidden
Menu" Width="50" Margin="5" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="9" />
</DockPanel>
</UserControl>
DockPanelTop.xaml.vb
Public Class DockPanelTop
Private Sub HideMenu_Click(sender As Object, e As RoutedEventArgs) Handles HideMenu.Click
("When clicked set value to columndefinition width to "0" ")
End Sub
End Class
I find nothing the code sample on web in VB to the DockPanelTop.xaml.vb :( Please help me.
Sry, bad English.TY.
You can get a reference to the parent window using the shared Window.GetWindow method. Try this:
Private Sub HideMenu_Click(sender As Object, e As RoutedEventArgs)
Dim window = TryCast(System.Windows.Window.GetWindow(Me), MainWindow)
If (window IsNot Nothing) Then
Dim columnDef = window.MainGrid.ColumnDefinitions(0)
Dim width = New GridLength(0)
columnDef.Width = New GridLength(0)
columnDef.MinWidth = 0.0
End If
End Sub
My Answer:
Public Sub HideMenu_Click(sender As Object, e As RoutedEventArgs) Handles HideMenu.Click
Dim window = TryCast(System.Windows.Window.GetWindow(Me), MainWindow)
Dim columnDef0 = window.MainGrid.ColumnDefinitions(0)
Dim columnDef0Width = window.MainGrid.ColumnDefinitions(0).Width
If (window IsNot Nothing) Then
If columnDef0Width.Value <= 10 Then
columnDef0.Width = New GridLength(150)
columnDef0.MinWidth = 10.0
ElseIf columnDef0.Width.Value > 10 Then
columnDef0.Width = New GridLength(10)
columnDef0.MinWidth = 10.0
End If
End If
End Sub

Why this DropdownMenu doesn't work in WPF

I'm trying to make a DropdownMenu in WPF with the code-behind in VB. For any reason that I can't realise this DropdownMenu is not working as I would like to.
This is the MainWindow.xaml file:
<Window x:Class="MainWindow"
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"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d" Height="600" Width="1080" Foreground="White" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<materialDesign:ColorZone Mode="PrimaryMid" Grid.ColumnSpan="2" HorizontalAlignment="Stretch">
<Grid>
<materialDesign:PopupBox PlacementMode="BottomAndAlignRightEdges" HorizontalAlignment="Right" Margin="10"/>
</Grid>
</materialDesign:ColorZone>
<Grid HorizontalAlignment="Stretch" Grid.Row="1" Background="{StaticResource PrimaryHueMidBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="326*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="GhostWhite">
<Image Source="Assets/logo.png"/>
</Grid>
<ScrollViewer HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Grid.Row="1">
<StackPanel x:Name="Menu" Margin="10"/>
</ScrollViewer>
</Grid>
</Grid>
</Window>
This is the MainWindow.xaml.vb file:
Imports MaterialDesignThemes.Wpf
Imports Project.ViewModel
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
Dim menuRegister = New List(Of SubItem)()
menuRegister.Add(New SubItem("Customer"))
menuRegister.Add(New SubItem("Providers"))
menuRegister.Add(New SubItem("Employees"))
menuRegister.Add(New SubItem("Products"))
Dim item6 = New ItemMenu("Register", menuRegister, PackIconKind.Register)
Dim menuSchedule = New List(Of SubItem)()
menuSchedule.Add(New SubItem("Services"))
menuSchedule.Add(New SubItem("Meetings"))
Dim item1 = New ItemMenu("Appointments", menuSchedule, PackIconKind.Schedule)
Dim menuReports = New List(Of SubItem)()
menuReports.Add(New SubItem("Customers"))
menuReports.Add(New SubItem("Providers"))
menuReports.Add(New SubItem("Products"))
menuReports.Add(New SubItem("Stock"))
menuReports.Add(New SubItem("Sales"))
Dim item2 = New ItemMenu("Reports", menuReports, PackIconKind.FileReport)
Dim menuExpenses = New List(Of SubItem)()
menuExpenses.Add(New SubItem("Fixed"))
menuExpenses.Add(New SubItem("Variable"))
Dim item3 = New ItemMenu("Expenses", menuExpenses, PackIconKind.ShoppingBasket)
Dim menuFinancial = New List(Of SubItem)()
menuFinancial.Add(New SubItem("Cash flow"))
Dim item4 = New ItemMenu("Financial", menuFinancial, PackIconKind.ScaleBalance)
Dim item0 = New ItemMenu("Dashboard", New UserControl(), PackIconKind.ViewDashboard)
Menu.Children.Add(New UserControlMenuItem(item0))
Menu.Children.Add(New UserControlMenuItem(item6))
Menu.Children.Add(New UserControlMenuItem(item1))
Menu.Children.Add(New UserControlMenuItem(item2))
Menu.Children.Add(New UserControlMenuItem(item3))
Menu.Children.Add(New UserControlMenuItem(item4))
End Sub
End Class
This is the ItemMenu Class:
Imports MaterialDesignThemes.Wpf
Namespace ViewModel
Public Class ItemMenu
Public Sub New(ByVal header As String, ByVal subItems As List(Of SubItem), ByVal icon As PackIconKind)
header = header
subItems = subItems
icon = icon
End Sub
Public Sub New(ByVal header As String, ByVal screen As UserControl, ByVal icon As PackIconKind)
header = header
screen = screen
icon = icon
End Sub
Public Property Header As String
Public Property Icon As PackIconKind
Public Property SubItems As List(Of SubItem)
Public Property Screen As UserControl
End Class
End Namespace
This is the SubItem Class:
Namespace ViewModel
Public Class SubItem
Public Sub New(ByVal name As String, ByVal Optional screen As UserControl = Nothing)
name = name
screen = screen
End Sub
Public Property Name As String
Public Property Screen As UserControl
End Class
End Namespace
This is the UserControlMenuItem.xaml file:
<UserControl x:Class="UserControlMenuItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d" >
<Grid>
<materialDesign:PackIcon Kind="{Binding Path=Icon}" Width="15" Height="15" Margin="10 16" Foreground="White"/>
<ListBoxItem x:Name="ListViewItemMenu" Content="{Binding Path=Header}" Padding="37 14" FontSize="15" Foreground="White"/>
<Expander x:Name="ExpanderMenu" Header="{Binding Path=Header}" IsExpanded="False" Width="210" HorizontalAlignment="Right" Background="{x:Null}" Foreground="White">
<ListView x:Name="ListViewMenu" ItemsSource="{Binding Path=SubItems}" Foreground="White" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" Padding="20 5"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Expander>
</Grid>
This is the UserControlMenuItem.xaml.vb file:
Imports Project.ViewModel
Partial Public Class UserControlMenuItem
Inherits UserControl
Public Sub New(ByVal itemMenu As ItemMenu)
InitializeComponent()
ExpanderMenu.Visibility = If(itemMenu.SubItems Is Nothing, Visibility.Collapsed, Visibility.Visible)
ListViewItemMenu.Visibility = If(itemMenu.SubItems Is Nothing, Visibility.Visible, Visibility.Collapsed)
Me.DataContext = itemMenu
End Sub
End Class
This is the Window when the app is running:
What I'm missing here?
Design cloned from DropDownMenu
I solved this problem. It was a detail I didn't realise. These are the corrected Classes:
The ItemMenu Class:
Imports MaterialDesignThemes.Wpf
Namespace ViewModel
Public Class ItemMenu
Public Sub New(ByVal header As String, ByVal subItems As List(Of SubItem), ByVal icon As PackIconKind)
Me.Header = header
Me.SubItems = subItems
Me.Icon = icon
End Sub
Public Sub New(ByVal header As String, ByVal screen As UserControl, ByVal icon As PackIconKind)
Me.Header = header
Me.Screen = screen
Me.Icon = icon
End Sub
Public Property Header As String
Public Property Icon As PackIconKind
Public Property SubItems As List(Of SubItem)
Public Property Screen As UserControl
End Class
End Namespace
The SubItem Class:
Namespace ViewModel
Public Class SubItem
Public Sub New(ByVal name As String, ByVal Optional screen As UserControl = Nothing)
Me.Name = name
Me.Screen = screen
End Sub
Public Property Name As String
Public Property Screen As UserControl
End Class
End Namespace
Just remained make reference to the Class with "Me" (self in other languages like Python) and then assign a value to the property:
Me.Header = header 'Example in ItemMenu Class
Me.Name = name 'Example in SubItem Class

How do I add, save and load items to a listview in WPF

About two weeks ago I started to develop in WPF and, since I only developed in WinForms, I ran into common problems but managed to find the solution for them. However, currently I'm stuck at something: adding items with multiple columns (via visual basic code, not xaml) into a listview.
I'm not sure if it's best to use a Listview or a DataGrid control for this but basically I want the user to be able to add favourite songs to a list, save them to a file and load the file whenever the app is opened. The listview currently has three columns: Artist, Song and Status.
When I was programming in WinForms, I used to do this:
Dim Song As New ListViewItem
Form1.ListView1.Items.Add(Song)
Song.Text = TextBox1.Text
Song.SubItems.Add(TextBox2.Text)
Song.SubItems.Add(TextBox3.Text)
Then, to save:
Dim Song As New ListViewItem
Form1.ListView1.Items.Add(Song)
Song.Text = TextBox1.Text
Song.SubItems.Add(TextBox2.Text)
Song.SubItems.Add(TextBox3.Text)
Try
Dim myWriter As New IO.StreamWriter(PATH_DATABASE)
For Each myItem As ListViewItem In Form1.ListView1.Items
myWriter.WriteLine(myItem.Text & "|" & myItem.SubItems(1).Text & "|" & myItem.SubItems(2).Text & "|" & myItem.SubItems(3).Text
Next
myWriter.Close()
Catch ex As Exception
MsgBox("Error: " & ex.Message, vbCritical, "Error")
End Try
I've been searching and I've found that I need to use binding so I've tried this:
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Windows
Public Structure Song
Public _artist As String
Public _title As String
Public _status As String
Property Artist() As String
Get
Return _artist
End Get
Set(ByVal Value As String)
_artist = Value
End Set
End Property
Property Title() As String
Get
Return _title
End Get
Set(ByVal Value As String)
_title = Value
End Set
End Property
Property Status() As String
Get
Return _status
End Get
Set(ByVal Value As String)
_status = Value
End Set
End Property
End Structure
Public Class WINDOW_AddSong
Dim songs As New ObservableCollection(Of Song)
Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
Dim Song As New ListViewItem
For Each wnd As Window In Application.Current.Windows
If wnd.GetType Is GetType(MainWindow) Then
DirectCast(wnd, MainWindow).Listview1.Items.Add(Alimento)
Alimento.Content = New Song() With {._artist = "Lol", ._title = "Lol2", ._status = "Lol3"}
End If
Next
End Sub
End Class
And in the XAML listview:
<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding Artist}"/>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title}"/>
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding Status}"/>
This works however I'm not sure if this is the WPF-way of doing things.
However, I'm stuck at the saving process:
Try
Dim myWriter As New IO.StreamWriter(PATH_DATABASE)
For Each wnd As Window In Application.Current.Windows
If wnd.GetType Is GetType(MainWindow) Then
For Each myItem As ListViewItem In DirectCast(wnd, MainWindow).Listview1.Items
myWriter.WriteLine(Song, Artist, Status)
Next
End If
Next
myWriter.Close()
Catch ex As Exception
MsgBox("Error: " & ex.Message, vbCritical, "Error")
End Try
This doesn't work.
Also, PATH_DATABASE is just a directory.
To summarise, could an expert review my code and check if I'm doing things "right" and, if possible, could you help me with the saving and loading process?
Thanks.
I just updated your structure (I used a class, just force of habit) to use automatic property values which are available in recent versions of VS. The default implementation of properties is built in along with the backer fields. It is used just as before.
You are on the right track with ObservableCollection but you you need to implement INotifyPropertyChanged by adding the Event PropertyChanged and providing the Sub OnPropertyChanged to raise the event.
Set up the bindings on the controls and set the ItemsSource ot the ListView to your ObservableList.
To save Import System.Text so you can use a StringBuilder. The loop through the list to build the string to save to a text file.
I didn't spend any time trying to make the XAML Windows look nice. Pardon the ugliness.
Class MainWindow
Public Songs As New ObservableCollection(Of Song)
Protected Sub OnLoad(sender As Object, e As RoutedEventArgs)
Songs.Add(New Song("Nirvana", "Smells Like Teen Spirit", "Open"))
Songs.Add(New Song("John Lennon", "Imagine", "In Stock"))
Songs.Add(New Song("U2", "One", "Unknown"))
Songs.Add(New Song("Michael Jackson", "Billie Jean", "Open"))
lvSongs.ItemsSource = Songs
End Sub
Private Sub BtnAdd_Click(sender As Object, e As RoutedEventArgs)
Dim frm As New frmAdd
frm.mainForm = Me 'send the instance of the current form to the new form
frm.ShowDialog()
End Sub
Private Sub SaveList()
Dim sb As New StringBuilder
For Each item As Song In Songs
sb.AppendLine($"{item.Artist}|{item.Title}|{item.Status}")
Next
'call sb.ToString and write it to a .txt file
End Sub
End Class
MainWindow XAML
<Window x:Class="MainWindow"
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"
xmlns:local="clr-namespace:WPF_BindComboBox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Loaded="OnLoad"
Name="root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView Margin="10" Name="lvSongs" Grid.Row="0">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBox x:Name="txtArtist"
Text="{Binding Artist, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontWeight="Bold"/>
<TextBlock Text=", " />
<TextBlock Text="Age: " />
<TextBox Text="{Binding Title}"
FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBox Text="{Binding Status}" />
<TextBlock Text=")" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button x:Name="btnAdd" Grid.Row="1" Height="25" Width="100" Content="Add a Song" Click="BtnAdd_Click"/>
</Grid>
</Window>
The Song class
Public Class Song
Implements INotifyPropertyChanged
Public Property Artist() As String
Public Property Title() As String
Public Property Status() As String
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Public Sub New(art As String, Ttl As String, Stat As String)
Artist = art
Title = Ttl
Status = Stat
End Sub
End Class
The Add Window
Public Class frmAdd
Public mainForm As MainWindow
Private Sub BtnAdd_Click(sender As Object, e As RoutedEventArgs)
mainForm.Songs.Add(New Song(txtArtist.Text, txtTitle.Text, txtStatus.Text))
ClearForm()
End Sub
Private Sub ClearForm()
txtArtist.Clear()
txtTitle.Clear()
txtStatus.Clear()
End Sub
End Class
Add Window XAML
<Window x:Class="frmAdd"
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"
xmlns:local="clr-namespace:WPF_BindComboBox"
mc:Ignorable="d"
Title="frmAdd" Height="172.641" Width="307.547">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Artist" FontSize="16" FontWeight="Bold"/>
<TextBox x:Name="txtArtist" Grid.Column="1" Grid.Row="0" FontSize="16" Width="150" />
<TextBlock Grid.Column="0" Grid.Row="1" Text="Title" FontSize="16" FontWeight="Bold"/>
<TextBox x:Name="txtTitle" Grid.Column="1" Grid.Row="1" FontSize="16" Width="150"/>
<TextBlock Grid.Column="0" Grid.Row="2" Text="Status" FontSize="16" FontWeight="Bold"/>
<TextBox x:Name="txtStatus" Grid.Column="1" Grid.Row="2" FontSize="16" Width="150"/>
<Button x:Name="btnAdd" Grid.Column="1" Grid.Row="4" Content="Add Song" Click="BtnAdd_Click"/>
</Grid>
</Window>
EDIT
Required Imports
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Text

TextBox MouseDown Event within UserControl

I have a user control that contains a TextBox and I would like to capture the mousedown event however, I cannot seem to get things to work! My existing, non working code is below, any assistance would be greatly appreciated.
UserControl xaml:
<UserControl x:Class="LeftLabel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Width="auto" Height="auto" >
<StackPanel DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=TextBlockText}"
Name="UcTextBlock"
Width="{Binding Path=TextBlockWidth}"
FontSize="{Binding Path=TextBlockFontSize}"
HorizontalAlignment="Right"
TextAlignment="Right"
VerticalAlignment="Center"
Margin="5,0,0,0" />
<TextBox Text="{Binding Path=TextBoxText}"
Name="UcTextBox"
MouseDown="UcTextBox_MouseDown"
Width="{Binding Path=TextBoxWidth}"
Height="{Binding Path=TextBoxHeight}"
FontSize="{Binding Path=TextBoxFontSize}"
Padding="{Binding Path=TextBoxPadding}"
Margin="5,0,0,0"
BorderThickness="0" />
</StackPanel>
</StackPanel>
</UserControl>
UserControl.vb:
Public Event TextBoxMouseDown As EventHandler
Private Sub UcTextBox_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles UcTextBox.MouseDown
RaiseEvent TextBoxMouseDown(sender, e)
End Sub
For testing purposes I am adding the UserControls to my MainWindow programmatically:
Dim count As Integer = 1
While count < 10
Dim ucl As New LeftLabel
With ucl
.Margin = New Thickness(4)
.TextBlockText = "Label " & count.ToString
.TextBlockWidth = 100
.TextBlockFontSize = 12
.TextBoxFontSize = 12
.TextBoxHeight = 20
.TextBoxText = "Initial Text " & count.ToString
.TextBoxPadding = New Thickness(2)
.TextBoxWidth = 150
AddHandler .TextBoxMouseDown, AddressOf LabelLeftTextBoxMouseDown
End With
TextBoxStackPanel.Children.Add(ucl)
count += 1
End While
Private Sub LabelLeftTextBoxMouseDown(sender As Object, e As EventArgs)
Dim txt As TextBox = DirectCast(sender, TextBox)
MsgBox(txt.Text)
End Sub
This is a somewhat common problem.
It occurs with some controls due to the fact that these controls handle these events internally. The button, for instance, "swallows" the click and rather exposes its own event - the Click-event.
If you want to declare your textbox-event-handlers in XAML, I suggest you checkout the Preview*-events (i.e. PreviewMouseDown), these always occur, maybe that can solve your problem if you need to react to clicks.
<TextBox Text="{Binding Path=TextBoxText}"
Name="UcTextBox"
PreviewMouseDown="UcTextBox_PreviewMouseDown"
Width="{Binding Path=TextBoxWidth}"
Height="{Binding Path=TextBoxHeight}"
FontSize="{Binding Path=TextBoxFontSize}"
Padding="{Binding Path=TextBoxPadding}"
Margin="5,0,0,0"
BorderThickness="0" />

WPF Image only updates in run-time on last frame

I'm new to XAML. I wrote following routine to rotate an image by a given angle (0 to 360). I put in a slider control to set the angle based on slider value. Works great! However, when running the program and clicking the 'spin' button,a For/Next loop goes from 0 to 360 and the image will only display the last angle rotation (360). I did put in a Sleep command to slow, just in case I wasn't catching the previous updates. Any help with why it won't update continuously would be greatly appreciated. Thank you.
Imports System.Threading.Thread
Imports System.Windows.Media.Imaging.BitmapImage
Class MainWindow
Private Sub Slider1_ValueChanged(sender As System.Object, e As System.Windows.RoutedPropertyChangedEventArgs(Of System.Double)) Handles Slider1.ValueChanged
' ---- when I adjust manually, this works perfectly
Dim rotateTransform1 As New RotateTransform
rotateTransform1.Angle = Slider1.Value
lblAngle.Content = rotateTransform1.Angle
Image1.RenderTransform = rotateTransform1
End Sub
Private Sub btnSpin_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnSpin.Click
Dim spinAngle as Double
For SpinAngle 0 to 360
spinWheel(spinAngle)
Sleep(50)
Next spinAngle
End Sub
Private Sub spinWheel(ByVal spinAngle)
Dim rotateTransform1 As New RotateTransform
rotateTransform1.Angle = SpinAngle 'Slider1.Value
Image1.RenderTransform = rotateTransform1
lblAngle.Content = rotateTransform1.Angle
Image1.InvalidateVisual()
End Sub
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
' Image1.createOption = BitmapCreateOptions.IgnoreImageCache
End Sub
End Class
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="677" Width="910">
<Grid Background="#FF006903">
<Grid.RowDefinitions>
<RowDefinition Height="238*" />
<RowDefinition Height="178*" />
</Grid.RowDefinitions>
<Button Content="SPIN!" Height="23" HorizontalAlignment="Left" Margin="521,101,0,0" Name="btnSpin" VerticalAlignment="Top" Width="75" Grid.Row="1" FontFamily="Tahoma" FontSize="15" FontWeight="ExtraBold" />
<Image Height="615" Margin="32,7,0,0" Name="Image1" Stretch="None" VerticalAlignment="Top"
RenderTransformOrigin=" 0.5,0.5" Source="/rotatePicture;component/Images/purp_wheel_cropped.png"
Grid.RowSpan="2" HorizontalAlignment="Left" Width="619" />
<Slider Height="25" HorizontalAlignment="Left" Margin="127,188,0,0" Name="Slider1" VerticalAlignment="Top" Width="350" Maximum="360" Grid.Row="1" />
<Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="521,175,0,0" Name="lblAngle" VerticalAlignment="Top" Width="75" Grid.Row="1" FontFamily="Tahoma" FontSize="15" FontWeight="ExtraBold" />
<Image Height="29" HorizontalAlignment="Left" Margin="535,252,0,0" Name="Image2" Stretch="Fill" VerticalAlignment="Top" Width="55" Source="/rotatePicture;component/Images/wheel_pointer.png" />
</Grid>
</Window>
The problem with your approach is that you are blocking the UI thread by repeatedly calling Sleep in a Click handler. WPF provides a very elegant mechanism for what you are trying to do. It's called Animation.
A method that animates the rotation of a FrameworkElement may look like shown below in C# (sorry, but I don't speak VB).
private void RotateElement(
FrameworkElement element, double from, double to, TimeSpan duration)
{
var transform = element.RenderTransform as RotateTransform;
if (transform != null)
{
var animation = new DoubleAnimation(from, to, duration);
transform.BeginAnimation(RotateTransform.AngleProperty, animation);
}
}
Note that the RotateTransform must already be contained in the RenderTransform property of the FrameworkElement. It could for example be assigned in XAML like this:
<Image RenderTransformOrigin="0.5,0.5" ...>
<Image.RenderTransform>
<RotateTransform/>
</Image.RenderTransform>
</Image>
You would call the RotateElement method in a Button Click handler like this:
private void btnSpin_Click(object sender, RoutedEventArgs e)
{
RotateElement(Image1, 0, 360, TimeSpan.FromMilliseconds(360 * 50));
}
Note also that in your Slider1_ValueChanged it is also not necessary to create a new RotateTransform every time.
Moreover, there is rarely any need to call InvalidateVisual, as you do in spinWheel.
Clemens answer is good. Do not forget that you can pause/resume animations but only if you create them in code-behind. If I remember correctly you can't control animations if you start them in XAML.

Resources