WPF Image only updates in run-time on last frame - wpf

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.

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

Calculate a ScrollViewer's scrollbar offsets so that an object will be centered, while maintaining a layout transform who's scale is variable

I have been fighting with this one for many hours now, and I just can't seem to arrive at an acceptable answer. I am hoping someone out there with much stronger geometry skills than my self can solve this riddle for me. Any help would be greatly appreciated. The nature of my problem, and description is below in the image that I provided.
And here is a sample project that I have built, which does not correctly fulfill the requirements.
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"
mc:Ignorable="d"
Title="Center and Zoom ScrollViewer Test" Height="600" Width="800" WindowStartupLocation="CenterScreen">
<Grid>
<DockPanel>
<GroupBox Header="Parameters" DockPanel.Dock="Top" Margin="10">
<StackPanel Orientation="Horizontal">
<GroupBox Header="Manually Set ScrollBar Positions" Margin="10">
<StackPanel Orientation="Horizontal">
<TextBox Name="EditHorz" Width="60" Margin="10" TextChanged="EditHorz_TextChanged" />
<Label Content="x" Margin="0 10 0 10" />
<TextBox Name="EditVert" Width="60" Margin="10" TextChanged="EditVert_TextChanged" />
</StackPanel>
</GroupBox>
<GroupBox Header="Scale" Margin="10">
<DockPanel>
<Label Content="{Binding ElementName=scaleValue, Path=Value}" DockPanel.Dock="Right" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Width="40" />
<Slider Name="scaleValue" Minimum="1" Maximum="4" SmallChange="0.05" LargeChange="0.1" Width="200" VerticalAlignment="Center" />
</DockPanel>
</GroupBox>
</StackPanel>
</GroupBox>
<GroupBox Header="Debug Output" Margin="10">
<TextBox Name="text" FontFamily="Courier New" FontSize="12" DockPanel.Dock="Left" Width="500" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" Margin="10" />
</GroupBox>
<GroupBox Header="Proof" Margin="10">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Center">
<Button Width="60" HorizontalAlignment="Left" Content="Center" Click="ButtonCenter_Click" Margin="10" />
<Button Width="60" HorizontalAlignment="Left" Content="Reset" Click="ButtonReset_Click" Margin="10" />
</StackPanel>
<ScrollViewer Name="scroll" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Background="Green" Width="100" Height="100" VerticalAlignment="Top" Margin="10">
<Canvas Width="200" Height="200" Background="Red">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=scaleValue, Path=Value}" ScaleY="{Binding ElementName=scaleValue, Path=Value}" />
</Canvas.LayoutTransform>
<Rectangle Name="rect" Width="40" Height="40" Canvas.Left="120" Canvas.Top="70" Fill="Blue" />
</Canvas>
</ScrollViewer>
</DockPanel>
</GroupBox>
</DockPanel>
</Grid>
Code Behind (VB.net)
Class MainWindow
''' Calculates the horizontal and vertical scrollbar offsets so that
''' the blue rectangle is centered within the scroll viewer.
Private Sub RecalculateCenter()
' the scale we are using
Dim scale As Double = scaleValue.Value
' get the rectangles current position within the canvas
Dim rectLeft As Double = Canvas.GetLeft(rect)
Dim rectTop As Double = Canvas.GetTop(rect)
' set our point of interest "Rect" equal to the the whole coordinates of the rectangle
Dim poi As Rect = New Rect(rectLeft, rectTop, rect.Width, rect.Height)
' get our view offset
Dim ofsViewWidth As Double = (scroll.ScrollableWidth - (((scroll.ViewportWidth / 2) - (rect.ActualWidth / 2)) * scale)) / scale
Dim ofsViewHeight As Double = (scroll.ScrollableHeight - (((scroll.ViewportHeight / 2) - (rect.ActualHeight / 2)) * scale)) / scale
' calculate our scroll bar offsets
Dim verticalOffset As Double = (poi.Top - ofsViewHeight) * scale
Dim horizontalOffset As Double = (poi.Left - ofsViewWidth) * scale
' record the output to the debug output window
Dim sb As New StringBuilder()
sb.AppendLine($"Scale : {scale}")
sb.AppendLine($"POI : {poi.ToString()}")
sb.AppendLine($"Rect : {rectLeft}x{rectTop}")
sb.AppendLine($"Extent : {scroll.ExtentWidth}x{scroll.ExtentHeight}")
sb.AppendLine($"Scrollable : {scroll.ScrollableWidth}x{scroll.ScrollableHeight}")
sb.AppendLine($"View Offset: {ofsViewWidth}x{ofsViewHeight}")
sb.AppendLine($"Horizontal : {horizontalOffset}")
sb.AppendLine($"Vertical : {verticalOffset}")
text.Text = sb.ToString()
' set the EditHorz and EditVert text box values, this will trigger the scroll
' bar offsets to fire via the TextChanged event handlers
EditHorz.Text = horizontalOffset.ToString()
EditVert.Text = verticalOffset.ToString()
End Sub
''' Try and parse the horizontal text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarHorizontalOffset()
Dim ofs As Double = 0
If Double.TryParse(EditHorz.Text, ofs) Then
scroll.ScrollToHorizontalOffset(ofs)
End If
End Sub
''' Try and parse the vertical text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarVerticalOffset()
Dim ofs As Double = 0
ofs = 0
If Double.TryParse(EditVert.Text, ofs) Then
scroll.ScrollToVerticalOffset(ofs)
End If
End Sub
''' Parse and set scrollbars positions for both Horizontal and Vertical
Private Sub SetScrollBarOffsets()
SetScrollBarHorizontalOffset()
SetScrollBarVerticalOffset()
End Sub
Private Sub ButtonCenter_Click(sender As Object, e As RoutedEventArgs)
RecalculateCenter()
End Sub
Private Sub ButtonReset_Click(sender As Object, e As RoutedEventArgs)
scroll.ScrollToVerticalOffset(0)
scroll.ScrollToHorizontalOffset(0)
End Sub
Private Sub EditHorz_TextChanged(sender As Object, e As TextChangedEventArgs)
SetScrollBarOffsets()
End Sub
Private Sub EditVert_TextChanged(sender As Object, e As TextChangedEventArgs)
SetScrollBarOffsets()
End Sub
End Class
After much more trial and error, and breaking it apart piece by piece, I was able to finally get this code to work. I hope someone else will find this useful. Solution is as follows:
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"
mc:Ignorable="d"
Title="Center and Zoom ScrollViewer Test" Height="600" Width="800" WindowStartupLocation="CenterScreen">
<Grid>
<DockPanel>
<GroupBox Header="Parameters" DockPanel.Dock="Top" Margin="10">
<StackPanel Orientation="Horizontal">
<GroupBox Header="Manually Set ScrollBar Positions" Margin="10">
<StackPanel Orientation="Horizontal">
<TextBox Name="EditHorz" Width="60" Margin="10" TextChanged="EditHorz_TextChanged" />
<Label Content="x" Margin="0 10 0 10" />
<TextBox Name="EditVert" Width="60" Margin="10" TextChanged="EditVert_TextChanged" />
</StackPanel>
</GroupBox>
<GroupBox Header="Scale" Margin="10">
<DockPanel>
<Label Content="{Binding ElementName=scaleValue, Path=Value, StringFormat={}{0:F2}}" DockPanel.Dock="Right" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Width="40" />
<Slider Name="scaleValue" Minimum="1" Maximum="4" SmallChange="0.05" LargeChange="0.1" Width="200" VerticalAlignment="Center" />
</DockPanel>
</GroupBox>
</StackPanel>
</GroupBox>
<GroupBox Header="Debug Output" Margin="10">
<TextBox Name="text" FontFamily="Courier New" FontSize="12" DockPanel.Dock="Left" Width="500" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" Margin="10" />
</GroupBox>
<GroupBox Header="Proof" Margin="10">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Center">
<Button Width="60" HorizontalAlignment="Left" Content="Center" Click="ButtonCenter_Click" Margin="10" />
<Button Width="60" HorizontalAlignment="Left" Content="Reset" Click="ButtonReset_Click" Margin="10" />
</StackPanel>
<ScrollViewer Name="scroll" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Background="Green" Width="100" Height="100" VerticalAlignment="Top" Margin="10">
<Canvas Width="200" Height="200" Background="Red">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=scaleValue, Path=Value}" ScaleY="{Binding ElementName=scaleValue, Path=Value}" />
</Canvas.LayoutTransform>
<Rectangle Name="rect" Width="40" Height="40" Canvas.Left="120" Canvas.Top="70" Fill="Blue" />
</Canvas>
</ScrollViewer>
</DockPanel>
</GroupBox>
</DockPanel>
</Grid>
Code Behind (VB.net):
Class MainWindow
' Calculates the horizontal and vertical scrollbar offsets so that
' the blue rectangle is centered within the scroll viewer.
Private Sub RecalculateCenter()
' the scale we are using
Dim scale As Double = scaleValue.Value
' get our rectangles left and top properties
Dim rectLeft As Double = Canvas.GetLeft(rect) * scale
Dim rectTop As Double = Canvas.GetTop(rect) * scale
Dim rectWidth As Double = rect.Width * scale
Dim rectHeight As Double = rect.Height * scale
' set our point of interest "Rect" equal to the the whole coordinates of the rectangle
Dim poi As Rect = New Rect(rectLeft, rectTop, rectWidth, rectHeight)
' get top and left center values
Dim horizontalCenter As Double = ((scroll.ViewportWidth / 2) - (rectWidth / 2))
Dim verticalCenter As Double = ((scroll.ViewportHeight / 2) - (rectHeight / 2))
' get our center of viewport with relation to the poi
Dim viewportCenter As New Rect(horizontalCenter, verticalCenter, rectWidth, rectHeight)
' calculate our scroll bar offsets
Dim verticalOffset As Double = (poi.Top) - (viewportCenter.Top)
Dim horizontalOffset As Double = (poi.Left) - (viewportCenter.Left)
' record the output to the debug output window
Dim sb As New StringBuilder()
sb.AppendLine($"Scale .............. {scale,0:F2}")
sb.AppendLine($"rectLeft ........... {rectLeft,0:F0}")
sb.AppendLine($"rectTop ............ {rectTop,0:F0}")
sb.AppendLine($"POI ................ {poi.Left,0:F0},{poi.Top,0:F0},{poi.Width,0:F0},{poi.Height,0:F0}")
sb.AppendLine($"Horz Center ........ {horizontalCenter,0:F0}")
sb.AppendLine($"Vert Center ........ {verticalCenter,0:F0}")
sb.AppendLine($"View Center ........ {viewportCenter.Left,0:F0},{viewportCenter.Top,0:F0},{viewportCenter.Width,0:F0},{viewportCenter.Height,0:F0}")
sb.AppendLine($"Horizontal ......... {horizontalOffset,0:F0}")
sb.AppendLine($"Vertical ........... {verticalOffset,0:F0}")
sb.AppendLine($"------------------------------------")
sb.AppendLine($"ViewPort ........... {scroll.ViewportWidth,0:F0} x {scroll.ViewportHeight,0:F0}")
sb.AppendLine($"Extent ............. {scroll.ExtentWidth,0:F0} x {scroll.ExtentHeight,0:F0}")
sb.AppendLine($"Scrollable ......... {scroll.ScrollableWidth,0:F0} x {scroll.ScrollableHeight,0:F0}")
text.Text = sb.ToString()
' set the EditHorz and EditVert text box values, this will trigger the scroll
' bar offsets to fire via the TextChanged event handlers
EditHorz.Text = $"{horizontalOffset,0:F2}"
EditVert.Text = $"{verticalOffset,0:F2}"
End Sub
' Try and parse the horizontal text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarHorizontalOffset()
Dim ofs As Double = 0
If Double.TryParse(EditHorz.Text, ofs) Then
scroll.ScrollToHorizontalOffset(ofs)
Else
scroll.ScrollToHome()
End If
End Sub
' Try and parse the vertical text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarVerticalOffset()
Dim ofs As Double = 0
ofs = 0
If Double.TryParse(EditVert.Text, ofs) Then
scroll.ScrollToVerticalOffset(ofs)
Else
scroll.ScrollToHome()
End If
End Sub
' Parse and set scrollbars positions for both Horizontal and Vertical
Private Sub SetScrollBarOffsets()
SetScrollBarHorizontalOffset()
SetScrollBarVerticalOffset()
End Sub
Private Sub ButtonCenter_Click(sender As Object, e As RoutedEventArgs)
RecalculateCenter()
End Sub
Private Sub ButtonReset_Click(sender As Object, e As RoutedEventArgs)
EditHorz.Text = String.Empty
EditVert.Text = String.Empty
End Sub
Private Sub EditHorz_TextChanged(sender As Object, e As TextChangedEventArgs)
SetScrollBarOffsets()
End Sub
Private Sub EditVert_TextChanged(sender As Object, e As TextChangedEventArgs)
SetScrollBarOffsets()
End Sub
Private Sub scaleValue_ValueChanged(sender As Object, e As RoutedPropertyChangedEventArgs(Of Double)) Handles scaleValue.ValueChanged
Dispatcher.BeginInvoke(Sub() RecalculateCenter())
End Sub
End Class

Displaying line series in WPF chart with vb.Net

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!

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" />

Binding Silverlight UserControl custom properties to its' elements

I'm trying to make a simple crossword puzzle game in Silverlight 2.0. I'm working on a UserControl-ish component that represents a square in the puzzle. I'm having trouble with binding up my UserControl's properties with its' elements. I've finally (sort of) got it working (may be helpful to some - it took me a few long hours), but wanted to make it more 'elegant'.
I've imagined it should have a compartment for the content and a label (in the upper right corner) that optionally contains its' number. The content control probably be a TextBox, while label control could be a TextBlock. So I created a UserControl with this basic structure (the values are hardcoded at this stage):
<UserControl x:Class="XWord.Square"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FontSize="30"
Width="100" Height="100">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1"
Text="7"/>
<TextBox x:Name="Content" Grid.Row="1" Grid.Column="0"
Text="A"
BorderThickness="0" />
</Grid>
</UserControl>
I've also created DependencyProperties in the Square class like this:
public static readonly DependencyProperty LabelTextProperty;
public static readonly DependencyProperty ContentCharacterProperty;
// ...(static constructor with property registration, .NET properties
// omitted for brevity)...
Now I'd like to figure out how to bind the Label and Content element to the two properties. I do it like this (in the code-behind file):
Label.SetBinding( TextBlock.TextProperty, new Binding { Source = this, Path = new PropertyPath( "LabelText" ), Mode = BindingMode.OneWay } );
Content.SetBinding( TextBox.TextProperty, new Binding { Source = this, Path = new PropertyPath( "ContentCharacter" ), Mode = BindingMode.TwoWay } );
That would be more elegant done in XAML. Does anyone know how that's done?
First, set the DataContext on the UserControl using {RelativeSource Self}:
<UserControl x:Class="XWord.Square"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FontSize="30"
Width="100" Height="100"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
Now you can bind the individual elements to the properties of the usercontrol:
<TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1"
Text="{Binding LabelText}"/>
<TextBox x:Name="Content" Grid.Row="1" Grid.Column="0"
Text="{Binding ContentCharacter}" BorderThickness="0" />
For SL 2.0, you'll need to set the DataContext on the UserControl's Loaded event handler.
private void UserControl_Loaded( object sender, RoutedEventArgs e ) {
LayoutRoot.DataContext = this;
}
As Silverlight cannot use FindAncestor technique you can use a trick similar to the one that sets the UserControl's name, but without breaking its functionality by using the name of the LayoutRoot...
<UserControl x:Class="XWord.Square"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FontSize="30"
Width="100" Height="100">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="{Binding Path=Parent.LabelText, ElementName=LayoutRoot}" Grid.Row="0" Grid.Column="1"
Text="7"/>
<TextBox x:Name="{Binding Path=Parent.ContentCharacter, ElementName=LayoutRoot}" Grid.Row="1" Grid.Column="0"
Text="A"
BorderThickness="0" />
</Grid>
</UserControl>
It worked in SL3 without having to add any additional code (I'm using it in a WP7 app), but don't know if you can use it in SL2. Well, I realize now how this question is old, hope it's still helpful, I've arrived here because the answers I got for the same problem in WP7 didn't convince me.
I think you are looking for UI Element to Element Binding which is a feature of Silverlight 3.
I may not be understanding your issue exactly. In Silverlight, you are able to bind to almost any data object. So, if you have a PuzzleSquare class that contains properties Content and Label, you may bind to these properties directly from the object.
Let's say you created a simple object PuzzleSquare:
public class PuzzleSquare
{
public string Content{ get; set; }
public string Label{ get; set; }
public void PuzzleSquare(){};
public void PuzzleSquare(string label, string content):this()
{
Content = content;
Label = label;
}
}
So, if you are building the app with the classic view/code behind model, your code behind would add this object to the DataContext property of the grid on page load:
LayoutRoot.DataContext = new PuzzleSquare("1", "A");
Your Xaml would bind to the Square property:
<TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1"
Text="{Binding Label}"/>
<TextBox x:Name="Content" Grid.Row="1" Grid.Column="0"
Text="{Binding Content}" BorderThickness="0" />
Does that make sense?
ib.
This worked in Silverlight 4.0
Put a name on the UserControl, and then refer to it in the TextBlock
<UserControl x:Class="XWord.Square"
...omitted for brevity ...
x:Name="Square">
<TextBlock x:Name="Label" ...
Text="{Binding Path=LabelText,ElementName=Square}"/>
Try this:
Public ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(ButtonEdit), New System.Windows.PropertyMetadata("", AddressOf TextPropertyChanged))
Public Property Text As String
Get
Return GetValue(TextProperty)
End Get
Set(ByVal value As String)
SetValue(TextProperty, value)
End Set
End Property
Private Sub TextPropertyChanged()
If String.IsNullOrEmpty(Text) Then
TextBox1.Text = ""
Else
TextBox1.Text = Text
End If
End Sub
Private Sub TextBox1_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles TextBox1.LostFocus
Text = TextBox1.Text
End Sub
I can bind in both XAML and code behind.

Resources