Displaying line series in WPF chart with vb.Net - wpf

I'm having a rather frustrating time porting some VB.Net winforms code to WPF, and could do with a quick bit of assistance:
In short, I have data that is generated dynamically that I need to plot on a lineseries. Regardless of what I try, the chart is stubbornly refusing to display my data! I've messed about with just about every combination of .DataContext / .ItemsSource / Bindings / etc. I can find and have had a serious google about, but good VB.Net examples seem to be thin on the ground. I've clearly missed something "simple"... any suggestions will be welcomed.
Cut-down code is as follows:
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" x:Class="MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<chartingToolkit:Chart x:Name="MyChart" HorizontalAlignment="Left" Margin="10,10,0,0" Title="Chart Title" VerticalAlignment="Top" Height="300" Width="497">
<chartingToolkit:LineSeries x:Name="MyLineSeries" DependentValueBinding="{Binding Path=Intensity}" IndependentValueBinding="{Binding Path=PxNum}" IsSelectionEnabled="True" ItemsSource="{Binding}" >
<!-- Vertical axis for Intensity values -->
<chartingToolkit:LineSeries.DependentRangeAxis>
<chartingToolkit:LinearAxis
Orientation="Y"
Title="Intensity"
Minimum="0"
Maximum="65535"
Interval="8000"
ShowGridLines="True"
/>
</chartingToolkit:LineSeries.DependentRangeAxis>
</chartingToolkit:LineSeries>
<chartingToolkit:Chart.Axes>
<!-- Shared horizontal axis -->
<chartingToolkit:LinearAxis
Orientation="X"
Title="Detector px"
Interval="64"
Minimum="0"
Maximum="256"
ShowGridLines="True"/>
</chartingToolkit:Chart.Axes>
</chartingToolkit:Chart>
</Grid>
</Window>
VB:
Imports System.Collections.ObjectModel
Class MainWindow
Dim Series_Saturation As New ObservableCollection(Of GraphPoint)
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Series_Saturation.Add(New GraphPoint() With {.PxNum = 0, .Intensity = 54000}) ' New KeyValuePair(Of Int32, Int32)(0, 54000))
Series_Saturation.Add(New GraphPoint() With {.PxNum = 200, .Intensity = 54000}) ' New KeyValuePair(Of Int32, Int32)(nPX, 54000))
MyLineSeries.DataContext = Series_Saturation
End Sub
End Class
Public Class GraphPoint
Public Property PxNum As Long
Public Property Intensity As Long
End Class

Sorted it :-)
The vital nugget of information was in this link: how to plot values in line series graph using two textboxes in wpf
There seemed to be two subtleties going on - neither seemed to fix things in isolation, but the combination of the two did:
Basically, when you just drag-and-drop a WPF Toolkit chart on the a VB WPF window it add the xmlns entry:
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" x:Class="MainWindow"
Title="MainWindow" Height="350" Width="525">
and your chart is defined as
<chartingToolkit:Chart x:Name="MyChart" ...>
</chartingToolkit:Chart>
It all seems to compile up OK, but with my VB code would not display the points. Note the different xmlns in the following code, which appears to work just fine. This may be due to my development PC having multiple development environments on it?
Also, note that in this example, the datacontext is set for the chart, not the line series. My next challenge will be to see what happens when I try to display multiple lines!
I should add also, that the change in the way the axes are defined was not the thing that made it behave, that was just one of many avenues explored. The text boxes & button are just to demonstrate adding points - I make no apologies for a lack of finesse in the layout, I just wanted the thing to work!!
xaml:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Toolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
x:Class="MainWindow"
Title="MainWindow" Height="352.357" Width="803.114">
<Grid>
<Toolkit:Chart Name="MyChart"
Title="Line Series Demo"
VerticalAlignment="Top"
Height="320"
Width="500" Margin="32,0,263,0">
<Toolkit:Chart.Axes>
<!-- Shared horizontal axis -->
<Toolkit:LinearAxis
Orientation="X"
Title="Detector px"
Interval="64"
Minimum="0"
Maximum="256"
ShowGridLines="True"/>
<Toolkit:LinearAxis
Orientation="Y"
Title="Intensity"
Minimum="0"
Maximum="65535"
Interval="8000"
ShowGridLines="True"/>
</Toolkit:Chart.Axes>
<Toolkit:LineSeries DependentValuePath="Intensity"
IndependentValuePath="PxNum"
ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}"
IsSelectionEnabled="True"/>
</Toolkit:Chart>
<TextBox x:Name="txtX" HorizontalAlignment="Left" Height="25" Margin="550,10,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="78"/>
<TextBox x:Name="txtY" HorizontalAlignment="Left" Height="25" Margin="648,10,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="78"/>
<Button x:Name="butAdd" Content="Add Point" HorizontalAlignment="Left" Height="29" Margin="648,40,0,0" VerticalAlignment="Top" Width="78"/>
</Grid>
</Window>
VB:
Imports System.Collections.ObjectModel
Class MainWindow
Dim Series_Saturation As New ObservableCollection(Of GraphPoint)
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Series_Saturation.Add(New GraphPoint() With {.PxNum = 0, .Intensity = 54000}) ' New KeyValuePair(Of Int32, Int32)(0, 54000))
Series_Saturation.Add(New GraphPoint() With {.PxNum = 200, .Intensity = 4000}) ' New KeyValuePair(Of Int32, Int32)(nPX, 54000))
MyChart.DataContext = Series_Saturation
End Sub
Private Sub butAdd_Click(sender As Object, e As RoutedEventArgs) Handles butAdd.Click
Dim X As Int32 = CInt(txtX.Text)
Dim Y As Int32 = CInt(txtY.Text)
Series_Saturation.Add(New GraphPoint() With {.PxNum = X, .Intensity = Y})
End Sub
End Class
Public Class GraphPoint
Public Property PxNum As Long
Public Property Intensity As Long
End Class
I sincerely hopes this help someone else!

Related

Binding an ObservableCollection Dependency Property in a User Control

I have a UserControl that hosts a TreeView that has a custom collection which inherits from ObservableCollection of a custom class. I am trying to bind to a property using a dependency property from a ViewModel, whilst I have proven the process works for another property it does not work here.
I'm sure it is something silly I have missed but I've been going round in circles, can anyone spot my mistake?
The UserControl XAML:
<UserControl
x:Class="FileExplorer.TreeViewUserControl"
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:fe="clr-namespace:WpfControlLibrary.FileExplorer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="FileExplorer"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<TreeView
BorderThickness="0"
ItemsSource="{Binding FileSystem, RelativeSource={RelativeSource AncestorType=UserControl}}"
TreeViewItem.Collapsed="TreeView_Collapsed"
TreeViewItem.Expanded="TreeView_Expanded"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type fe:FileSystemObject}" ItemsSource="{Binding Items}">
<StackPanel Margin="0,2" Orientation="Horizontal">
<Image
Width="14"
Margin="2"
Source="{Binding Image}"
Stretch="Fill" />
<TextBlock
VerticalAlignment="Center"
FontSize="{StaticResource FontSizeSmall}"
Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The code behind for the UserControl:
Namespace FileExplorer
Public Class TreeViewUserControl
Inherits UserControl
#Region "Constructors"
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.DataContext = Me
End Sub
#End Region
#Region "Properties"
Public Shared ReadOnly FileSystemProperty As DependencyProperty = DependencyProperty.Register("FileSystem", GetType(FileSystemObjectCollection), GetType(TreeViewUserControl))
Public Property FileSystem As FileSystemObjectCollection
Get
Return CType(GetValue(FileSystemProperty), FileSystemObjectCollection)
End Get
Set(ByVal value As FileSystemObjectCollection)
SetValue(FileSystemProperty, value)
FileExplorer.UpdateLayout()
End Set
End Property
#End Region
Private Function GetFileSystemInfo(ByVal root As String) As FileSystemObjectCollection
Dim items As New FileSystemObjectCollection
' Parse all the directories at the path
For Each dir As String In Directory.GetDirectories(root)
items.Add(New FileSystemObject(dir, root))
Next
' Parse all the file at the path
For Each file As String In Directory.GetFiles(root)
items.Add(New FileSystemObject(file, root))
Next
Return items
End Function
Private Sub TreeView_Collapsed(sender As Object, e As RoutedEventArgs)
Dim node As TreeViewItem = CType(e.OriginalSource, TreeViewItem)
Dim fs As FileSystemObject = CType(node.DataContext, FileSystemObject)
fs.Clear()
End Sub
Private Sub TreeView_Expanded(sender As Object, e As RoutedEventArgs)
Dim node As TreeViewItem = CType(e.OriginalSource, TreeViewItem)
Dim fs As FileSystemObject = CType(node.DataContext, FileSystemObject)
If Not fs.HasChildren Then Exit Sub
fs.Items = GetFileSystemInfo(fs.FullName)
End Sub
End Class
End Namespace
The 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:fe="clr-namespace:WpfControlLibrary.FileExplorer;assembly=WpfControlLibrary"
xmlns:local="clr-namespace:ModStudio.Client"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ModStudio.Client.ViewModels"
Title="MainWindow"
d:DesignHeight="600"
d:DesignWidth="800"
WindowStartupLocation="CenterOwner"
WindowState="Maximized"
mc:Ignorable="d">
<Window.Resources>
<vm:MainWindowViewModel x:Key="MainWindowViewModel" />
</Window.Resources>
<Grid>
<fe:TreeViewUserControl DataContext="{StaticResource MainWindowViewModel}" FileSystem="{Binding ApplicationExplorer}" />
</Grid>
</Window>
The MainWindow has its DataContext set to a new instance of the ViewModel in code behind. The MainWindowViewModel has a property called ApplicationExplorer which is an instance of FileSystemObjectCollection. As mentioned FileSystemObjectCollection inherits from ObservableCollection(Of FileSystemObject). A FileSystemObject implements INotifyPropertyChanged. If I change ApplicationExplorer property, the control remains blank.
I have intentionally omitted some code here, but can add them if necessary.
Don't set the DataContext in the UserControl, i.e. remove this line:
Me.DataContext = Me
When you explcitly set the DataContext like this, you break the inheritance chain which means that the binding to the ApplicationExplorer property in your window no longer works:
<fe:TreeViewUserControl DataContext="{StaticResource MainWindowViewModel}"
FileSystem="{Binding ApplicationExplorer}" />
I made 3 changes and got this working!
Added OnPropertyChanged to ApplicationExplorer:
Public Property ApplicationExplorer As FileSystemObjectCollection
Get
Return _applicationExplorer
End Get
Set(value As FileSystemObjectCollection)
_applicationExplorer = value
OnPropertyChanged(NameOf(ApplicationExplorer))
End Set
End Property
Updated the binding in the UserControl:
<UserControl
x:Class="FileExplorer.TreeViewUserControl"
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:fe="clr-namespace:WpfControlLibrary.FileExplorer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="FileExplorer"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<TreeView
BorderThickness="0"
ItemsSource="{Binding FileSystem, RelativeSource={RelativeSource AncestorType=fe:TreeViewUserControl}}"
TreeViewItem.Collapsed="TreeView_Collapsed"
TreeViewItem.Expanded="TreeView_Expanded"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type fe:FileSystemObject}" ItemsSource="{Binding Items}">
<StackPanel Margin="0,2" Orientation="Horizontal">
<Image
Width="14"
Margin="2"
Source="{Binding Image}"
Stretch="Fill" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</UserControl>
Removed the Me.DataContext = Me from the UserControl code behind as you suggested

WPF User Control Data Binding Not Working

I'm creating a simple User Control combining a popup with a text view, nothing crazy. When I set it up in a window at first to style it all out, it worked perfectly, but when I moved it into a User Control to actually finish it up, it won't work correctly any more.
I pass a min and max value into the control and then it automatically creates a list of numbers to pick from in that range. In the User Control, the list of numbers doesn't get bound correctly, who knows why. Maybe someone can take a look at my code.
I've read a bunch of other questions about this, but don't really know what's happening. I'm not getting any errors in my output window, so no clues there. Anyway, here's the code -
UserControl.xaml
<UserControl x:Class="UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Me"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<TextBox Name="myTextbox"
Height="30"
Margin="0"
FontSize="14"
IsReadOnly="True"
Padding="5,2"
Text="{Binding Value}" />
<Popup x:Name="myPopup"
PlacementTarget="{Binding ElementName=myTextbox}"
StaysOpen="True">
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myTextbox, Path=IsFocused}" Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<StackPanel>
<ListView Name="myListView"
Height="100"
MaxHeight="300"
ItemsSource="{Binding List,
UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="ListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Label Width="100"
Height="30"
Margin="0"
Content="{Binding}"
FontFamily="Segoe UI"
FontSize="14"
Padding="5,2" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Width="200" Height="30" />
</StackPanel>
</Popup>
</StackPanel>
UserControl.xaml.vb
Imports System.ComponentModel
Imports System.Linq.Expressions
Imports System.Collections.ObjectModel
Class UserControl1
' Dependency Properties
Public Shared ReadOnly ListProperty As DependencyProperty = DependencyProperty.Register("List", GetType(ObservableCollection(Of Integer)), GetType(MainWindow))
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(MainWindow))
Public Shared ReadOnly MaxValueProperty As DependencyProperty = DependencyProperty.Register("MaxValue", GetType(Integer), GetType(MainWindow))
Public Shared ReadOnly MinValueProperty As DependencyProperty = DependencyProperty.Register("MinValue", GetType(Integer), GetType(MainWindow))
' Properties
Public Property List As ObservableCollection(Of Integer)
Get
Return DirectCast(GetValue(ListProperty), ObservableCollection(Of Integer))
End Get
Set(value As ObservableCollection(Of Integer))
SetValue(ListProperty, value)
End Set
End Property
Public Property Value As Integer
Get
Return DirectCast(GetValue(ValueProperty), Integer)
End Get
Set(value As Integer)
SetValue(ValueProperty, value)
End Set
End Property
Public Property MaxValue As Integer
Get
Return DirectCast(GetValue(MaxValueProperty), Integer)
End Get
Set(value As Integer)
SetValue(MaxValueProperty, value)
End Set
End Property
Public Property MinValue As Integer
Get
Return DirectCast(GetValue(MinValueProperty), Integer)
End Get
Set(value As Integer)
SetValue(MinValueProperty, value)
End Set
End Property
Private Sub ListView_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
Value = List(myListView.SelectedIndex)
End Sub
Private Sub UserControl1_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
List = New ObservableCollection(Of Integer)
' Add all available numbers into the list
For iCounter As Integer = MinValue To MaxValue
List.Add(iCounter)
Next
' Set the selected index on the list for the value
myListView.SelectedIndex = Value - MinValue
End Sub
End Class
Just for reference, when I tested this out, I set the Min and Max values myself in both the window and usercontrol setup.
I think that you have made the same mistake that I used to when I first started learning WPF. I can't guarantee that this is the cause of your problem, but as it's the only one that I can see, I'll address it. Unfortunately, there are so many poor tutorials and quick examples that show the connecting of a UserControl.DataContext to itself:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Or:
DataContext = this;
Now this is perfectly acceptable if you don't want to Bind to the UserControl externally because it's a quick and easy way to connect with properties defined in the code behind. However, when you want to Bind to the properties from outside the control, you'll find problems. In these instances (if not on all occasions), you should use a RelativeSource Binding to Bind to your code behind properties:
<TextBox Name="myTextbox" Height="30" Margin="0" FontSize="14" IsReadOnly="True"
Padding="5,2" Text="{Binding Value, RelativeSource={RelativeSource AncestorType={
x:Type UserControl1}}}" />
In this Binding, UserControl1 is the name of the UserControl that declared the properties. This should be done on all of the internal Bindings. This way, the DataContext is not set to the UserControl, but the Bindings still find the properties.
You can find out more about RelativeSource from the RelativeSource MarkupExtension page on MSDN.
As I cant provide a comment to Sheridan good answer I have to provide a new answer, sorry for this.
While I love this solution
DataContext="{Binding RelativeSource={RelativeSource Self}}"
it fails (as Sheridan pointed out already) fast.
What you can do is just set the DataContext of the content of your User Control
<UserControl x:Class="Example.View.Controls.MyUserControl"
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:controls="clr-namespace:Example.View.Controls"
mc:Ignorable="d">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MyUserControl}}}">
</Grid>
In this way all following Bindings have less Boilerplate code as you can just bind directly to your DP from the code-behind like:
<Label Content="{Binding MyLabel}"/>
As for Windows Apps (Windows 8 and Windows 10 UWP) the way to go is to give your control a name and reference it within your XAML file using Path and ElementName:
<UserControl
x:Class="MyControl"
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"
x:Name="Control"
mc:Ignorable="d" >
<Grid Height="240" VerticalAlignment="Top">
<Rectangle Fill="{Binding ElementName=Control, Path=Background}" />
</Grid>
</UserControl>
``

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.

How do I bind a WPF control to a VB.net property? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Data Binding WPF Property to Variable
How would I bind my module1 property to my WPF TextBox1?
WPF code:
<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>
<TextBox Height="23" HorizontalAlignment="Left" Margin="210,146,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>
VB.net code:
Module Module1
ReadOnly Property tbBinding As String
Get
Return "Success!"
End Get
End Property
End Module
Below is code that I have been working on based on the feed back I have been getting and the reading I have been doing.
/#######Current code in progres (trying with a class instead of a module)#######/
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 DataContext="Class1">
<TextBox Height="23" HorizontalAlignment="Left" Margin="210,146,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=tbBinding2}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="192,74,0,0" Name="Button1" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
Class1:
Imports System.ComponentModel
Public Class Class1
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
Dim varField As String = String.Empty
Public Property tbBinding2 As String
Get
Return varField
End Get
Set(value As String)
varField = value
NotifyPropertyChanged("tbBinding2")
End Set
End Property
End Class
MainWindow:
Class MainWindow
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim myClass1 As New Class1
myClass1.tbBinding2 = "Success!"
End Sub
End Class
You are not setting the DataContext anywhere
WPF has two layers: the data layer and the UI layer. The data layer is null by default, and you can set it by setting the DataContext property of any UI objects. Bindings are used to pull data from the data layer into the UI layer.
So, if you say MainWindow.DataContext = new Class1(), then you are setting the data layer beind MainWindow to a new instance of your Class1 object.
Writing <TextBox Text="{Binding tbProperty}" /> in the XAML is telling WPF to look in the data layer for a property called tbProperty and use it for the Text value of the TextBox.
If you change the tbProperty in your Class1 object being used as the DataContext, that change will also be reflected in TextBox.Text (providing you implemented INotifyPropertyChanged). And if the binding mode is set to TwoWay (default for TextBox.Text), then changes to TextBox.Text will also update the tbProperty in the data layer.
I actually recently posted an overview of the DataContext on my blog if you're interested.
You need to implement the INotifyPropertyChanged interface. Refer to this article for an example.
Edit:
Here is an example of binding to class Class1 (a.k.a. the "view model") from XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels:"clr-namespace:MyApplication.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<viewModels:Class1 />
</Window.DataContext>
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="210,146,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=tbBinding2}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="192,74,0,0" Name="Button1" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
Note that this XAML assumes that the Class1 class is contained in the same assembly under the MyApplication.ViewModels namespace.

Resources