Update LineGraph Every Second - wpf

Hi I'm trying to update a line chart every second (from the wpf data visualisation toolkit). The following code works for a PieChart:
VB:
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
' DispatcherTimer setup
Dim dt As DispatcherTimer = New DispatcherTimer()
AddHandler dt.Tick, AddressOf dispatcherTimer_Tick
dt.Interval = New TimeSpan(0, 0, 1)
dt.Start()
End Sub
Public Sub dispatcherTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
UpadateChartData()
Chart1.DataContext = ""
Chart1.DataContext = ChartData
End Sub
Public Sub UpadateChartData()
Dim r As Integer = GetRandom(1, 10)
ChartData.Add(New ChartData() With {.Name = r.ToString, .Votes = r})
End Sub
XAML:
<chartingToolkit:Chart Name="Chart1" Title="Chart1" Width="400" Height="400" >
<chartingToolkit:LineSeries ItemsSource="{Binding}" IndependentValueBinding="{Binding Path=Name}" DependentValueBinding="{Binding Path=Votes}" />
</chartingToolkit:Chart>
But I get this error when I use LineSeries (or any other type of chart):
Cannot modify the logical children for this node at this time because a tree walk is in progress.
Can anyone tell me why I'm getting the error or suggest an alternative approach?
Thanks for any help!

It indeed appears to be a bug in the toolkit, as ColinE mentioned.
I would suggest to take a look at Dynamic Data Display, which really facilitates these kind of things and makes it very easy. They provide a few new objects like ObservableDataSource<T>, and on this you can call source.AppendAsync(Dispatcher, T) which will update the graph.

Related

How to create a custom Textbox in WPF?

I am new to wpf
I want to create a custom textbox with the features such as
Change background color or Change Border Darker/Lighter on GotFocus and LostFocus.
Write only in UPPER CASE
WaterMark
Rounded Corner
I found code for rounded corner in XAML and it works good.
Following is the code :
<TextBox x:Name="txtUserName" BorderThickness="1" Height="23">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="3"/>
</Style>
</TextBox.Resources>
</TextBox>
I use to code in VB.NET Winforms before and I did something like this in there.
I made a class say MainTextBoxex
Public Class MainTextboxex
Inherits TextBox
Private Sub MainTextBoxEx_Enter(sender As Object, e As EventArgs) Handles Me.Enter
Me.BackColor = Color.FromArgb(247, 180, 65)
'Me.SelectAll()
End Sub
Private Sub MainTextBoxEx_Leave(sender As Object, e As EventArgs) Handles Me.Leave
Me.BackColor = Color.White
End Sub
Public Sub New()
MyBase.New()
Me.CharacterCasing = CharacterCasing.Upper
End Sub
Private Sub MainTextboxex_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
If e.KeyChar = ChrW(39) Then e.KeyChar = ChrW(96)
End Sub
Private NotInheritable Class NativeMethods
Private Sub New()
End Sub
Private Const ECM_FIRST As UInteger = &H1500
Friend Const EM_SETCUEBANNER As UInteger = ECM_FIRST + 1
<DllImport("user32.dll", EntryPoint:="SendMessageW")>
Public Shared Function SendMessageW(hWnd As IntPtr, Msg As UInteger, wParam As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> lParam As String) As IntPtr
End Function
End Class
Private _watermark As String
Public Property Watermark() As String
Get
Return _watermark
End Get
Set(value As String)
_watermark = value
UpdateWatermark()
End Set
End Property
Private Sub UpdateWatermark()
If IsHandleCreated AndAlso _watermark IsNot Nothing Then
NativeMethods.SendMessageW(Handle, NativeMethods.EM_SETCUEBANNER, CType(1, IntPtr), _watermark)
End If
End Sub
Protected Overrides Sub OnHandleCreated(e As EventArgs)
MyBase.OnHandleCreated(e)
UpdateWatermark()
End Sub
End Class
This worked awesome in winforms as I needed. (No rounded corners in there)
I just need to know how it is done here.
I want something that I don't have to code it everytime or for every Individual TextBox for the above feature everywhere.
Just use this custom TextBox and all set.
This will help me alot.
Thank You.
Use UserControls and CustomControls depending on how much deep you want to dive in your customization.
You can add your own properties by using "Dependency Property", and about the events you can expose Action Events and pass them through your Controls Action events.

WPF in VB Text Box 'System.NullReferenceException'

so here is simple WPF application I made to practice OO concepts. It has the user input a number in feet and converts to meters by outputting a message box.
Class MainWindow
Dim lengthInMeters = txtBox1.Text '***An exception of type "System.NullReferenceException" occurred***
Private Sub button_Click(sender As Object, e As RoutedEventArgs) Handles button.Click
MessageBox.Show(COptions.GetLength(lengthInMeters), "Conversion Successful!", MessageBoxButton.OK)
End Sub
End Class
And my other Class file:
Public Class COptions
Public Shared Function GetLength(lengthInMeters) As Double
Return lengthInMeters / 3.28
End Function
End Class
Also, here is the XAML for the textbox:
<TextBox
x:Name="txtBox1"
x:FieldModifier="public"
HorizontalAlignment="Left"
Height="23"
Margin="200,140,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
enderTransformOrigin="1.29,-3.252"
Grid.Column="1"
/>
I am getting a NullReference at "Dim lengthInMeters = txtBox1.Text" and I believe I need to instantiate my object reference? But how?
Thank you for your help!
Fields are initialized before the constructor runs, and GUI components initialized in the constructor, so txtBox1 has not been initialized at the point you are using it, as you have found out. Even if it did work, it would capture the value of the text box at that point, which is before the user has had a chance to enter anything.
Instead, move the Dim lengthInMeters = txtBox1.Text line into your button click handler, so that you get the current value of the text box just before you use it. Your current code is also relying on some implicit type conversions - I would recommend turning Option Strict On to catch these, and use explicit conversions.
Private Sub button_Click(sender As Object, e As RoutedEventArgs) Handles button.Click
Dim lengthInMeters = CDbl(txtBox1.Text)
MessageBox.Show(COptions.GetLength(lengthInMeters), "Conversion Successful!", MessageBoxButton.OK)
End Sub
Public Class COptions
Public Shared Function GetLength(lengthInMeters As Double) As Double
Return lengthInMeters / 3.28
End Function
End Class

Closing just one expander within another expander in WPF/ VB.NET

First post here so sorry if I mess something up/forget something.
I'm working in Visual Studio 2010 with a WPF application and am using VB.NET.
So I have a parent expander that contains a grid with two controls: another expander and another grid. I have written some code behind to cause each expander to become invisible when it is collapsed or closed (among a few other things), and to change color/ become visible when it is expanded/opened. I also have a few buttons placed in other areas that accomplish the same tasks. My problem is that when I collapse the 2nd expander, the 1st (parent) expander also closes/ becomes invisible. However, a button that is used to collapse the 2nd expander works perfectly. Here is my relevant code (hopefully I'm formatting this right):
XAML
<Expander Name="Expander1" Visibility="Hidden" >
<Grid >
<Grid.RowDefinitions >
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Expander Name="Expander2" Visibility="Hidden" >
<Content ...>
</Expander>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Content... />
</Grid>
</Grid>
</Expander>
VB.NET
Private Sub Expander2_Expanded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Expander2.Expanded
Expander2.Background = Brushes.PaleTurquoise
Expander2.BorderBrush = Brushes.Black
End Sub
Private Sub Expander2_Collapsed(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Expander2.Collapsed
Expander2.IsExpanded = False
Expander2.Background = Brushes.Transparent
Expander2.BorderBrush = Brushes.Transparent
Expander2.Visibility = Windows.Visibility.Visible
ButtonA7.Visibility = Windows.Visibility.Visible
Expander1.IsExpanded = True
Expander1.Background = Brushes.PaleTurquoise
Expander1.BorderBrush = Brushes.Black
Expander1.Visibility = Windows.Visibility.Visible
End Sub
Private Sub Expander1_Expanded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Expander1.Expanded
Expander1.Background = Brushes.PaleTurquoise
Expander1.BorderBrush = Brushes.Black
End Sub
Private Sub Expander1_Collapsed(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Expander1.Collapsed
Expander1.Background = Brushes.Transparent
Expander1.BorderBrush = Brushes.Transparent
Expander1.Visibility = Windows.Visibility.Hidden
ButtonA7.Visibility = Windows.Visibility.Visible
End Sub
Dont worry about all the buttons in the code, the buttons all work fine. In fact, one button is suppose to do the EXACT same thing as collapsing the expander, and it works properly. I just need the same thing to happen when you click on the actual expander to collapse it. Here is the code for the button so you see it is the same:
Private Sub Button_Click_2(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Expander2.IsExpanded = False
Expander2.Background = Brushes.Transparent
Expander2.BorderBrush = Brushes.Transparent
Expander2.Visibility = Windows.Visibility.Visible
Expander1.IsExpanded = True
Expander1.Background = Brushes.PaleTurquoise
Expander1.BorderBrush = Brushes.Black
Expander1.Visibility = Windows.Visibility.Visible
ButtonA7.Visibility = Windows.Visibility.Visible
End Sub
Thank you so much for any help, I really appreciate it!
EDIT: Alternatively, if there is an easy way (I am VERY new to WPF... ~1week) to hide/ get rid of the header, that could work too. But I would prefer it the other way where I tried before, if possible. Thanks!
Expander.Collapsed Event has routing strategy Bubbling. Mark event as handled before it reaches Expander1 and all should be okay.
Private Sub Expander2_Collapsed(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Expander2.Collapsed
...
e.Handled = True
End Sub

Selected Index Change on WPF combobox?

I'm converting an app of mine from WinForms to WPF. I came across this problem where in WinForms if I CLEAR a combobox, that does not fire combobox.selectedindexchange. It only fires when the user actually changes the index.
That's the functionality I need. However in WPF I can only find combobox.SelectionChanged. This unfortunately fires on both index change and clearing a combobox (any change).
In WPF how can I only trigger an event when the user changes the index? I'm assuming there's a solution I'm missing that's like the WinForms solution. I'm trying not to play games with global variables and having to track what was previously selected....that's a mess.
Mouseleave is also a mess.
I would greatly appreciate any help!
In your case you can implement in SelectionChanged event:
Private Sub OnSelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
Dim combo = TryCast(sender, ComboBox)
If combo.SelectedItem IsNot Nothing Then
'do something
End If
End Sub
You can remove event handler, invoke clear method and add again event handler.
Example code:
<StackPanel>
<ComboBox Name="myCB"
SelectionChanged="ComboBox_SelectionChanged">
<ComboBoxItem>test1</ComboBoxItem>
<ComboBoxItem>test2</ComboBoxItem>
<ComboBoxItem>test3</ComboBoxItem>
</ComboBox>
<Button Content="Clear" Click="Button_Click" />
</StackPanel>
MainWindow class:
Class MainWindow
Private Sub ComboBox_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
Console.WriteLine("Selected index: {0}", CType(sender, ComboBox).SelectedIndex)
End Sub
Private Sub Button_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
RemoveHandler Me.myCB.SelectionChanged, AddressOf ComboBox_SelectionChanged
Me.myCB.Items.Clear()
AddHandler Me.myCB.SelectionChanged, AddressOf ComboBox_SelectionChanged
End Sub
End Class

How to get ElementHost control, given one of WPF control's content

I am trying to get a reference to ElementHost control. For example in the below code I need to initially use the “testImage” content of the WPF user control to lunch the event. The WPF control is added at run-time, so does the ElementHost control, so I can’t use the WPF control’s name or ElementHost’s name.
My logic is to get the parent WPF user control of the “testImage”, and then get the parent ElementHost of the WPF’s user control.
But I am having troubles writing it in code. Please advise. Thanks.
<UserControl x:Class="WpfTest”
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300">
<Grid>
<Label FontSize="10" Height="24" Margin="74,16,0,0" Name="testLabel" VerticalAlignment="Top" />
<Image Name="testImage" Stretch="Uniform" HorizontalAlignment="Left" Width="64" Height="81" VerticalAlignment="Top" Margin="8,0,0,0"/>
</Grid>
</UserControl>
Here is some code that might help you. The key points are:
Name the ElementHost when you create it at runtime
Make use the the help function FindVisualChildByName() to search the WPF tree to get the desired control
I Hope this helps!
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim ElementHost1 As New System.Windows.Forms.Integration.ElementHost
Dim WpfTest1 As New WindowsApplication1.WPFTest
ElementHost1.Dock = DockStyle.Fill
ElementHost1.Name = "ElementHost1"
ElementHost1.Child = WpfTest1
Me.Controls.Add(ElementHost1)
End Sub
Private Sub GetImageReference_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim ElementHost1 As System.Windows.Forms.Integration.ElementHost = Me.Controls("ElementHost1")
Dim TheGrid As System.Windows.Controls.Grid = CType(ElementHost1.Child, WPFTest).MyGrid
Dim ImageTest As System.Windows.Controls.Image = FindVisualChildByName(TheGrid, "testImage")
Stop
End Sub
Public Function FindVisualChildByName(ByVal parent As System.Windows.DependencyObject, ByVal Name As String) As System.Windows.DependencyObject
For i As Integer = 0 To System.Windows.Media.VisualTreeHelper.GetChildrenCount(parent) - 1
Dim child = System.Windows.Media.VisualTreeHelper.GetChild(parent, i)
Dim controlName As String = child.GetValue(System.Windows.Controls.Control.NameProperty)
If controlName = Name Then
Return child
Else
Dim res = FindVisualChildByName(child, Name)
If Not res Is Nothing Then
Return res
End If
End If
Next
Return Nothing
End Function

Resources