WPF KeyBindings not working - wpf

Already gone through a dozen or more SO questions and articles on this topic. Nothing seems to work. Here's my simple system. A main Window that has a Frame to host Page objects in it. One of my Page object has the following stuff in it:
<Page.Resources>
<RoutedUICommand x:Key="PreviousRecordCommand" Text="Previous Record" />
<RoutedUICommand x:Key="NextRecordCommand" Text="Previous Record" />
<CollectionViewSource x:Key="cvsAllOrders" Source="{Binding AllSalesOrders}" />
</Page.Resources>
<Page.CommandBindings>
<CommandBinding Command="{StaticResource PreviousRecordCommand}" Executed="PreviousRecordCommand_Executed" CanExecute="PreviousRecordCommandBinding_CanExecute" />
<CommandBinding Command="{StaticResource NextRecordCommand}" Executed="NextRecordCommand_Executed" CanExecute="NextRecordCommandBinding_CanExecute" />
</Page.CommandBindings>
<Page.InputBindings>
<KeyBinding Command="{StaticResource PreviousRecordCommand}" Gesture="CTRL+LEFT" />
<KeyBinding Command="{StaticResource NextRecordCommand}" Gesture="CTRL+RIGHT" />
</Page.InputBindings>
And the code-behind has:
Private Sub PreviousRecordCommand_Executed(sender As System.Object, e As System.Windows.Input.ExecutedRoutedEventArgs)
Dim CurView = DirectCast(Me.Resources("cvsAllOrders"), CollectionViewSource).View
CurView.MoveCurrentToPrevious()
If CurView.IsCurrentBeforeFirst Then CurView.MoveCurrentToFirst()
End Sub
Private Sub NextRecordCommand_Executed(sender As System.Object, e As System.Windows.Input.ExecutedRoutedEventArgs)
Dim CurView = DirectCast(Me.Resources("cvsAllOrders"), CollectionViewSource).View
CurView.MoveCurrentToNext()
If CurView.IsCurrentAfterLast Then CurView.MoveCurrentToLast()
End Sub
Private Sub PreviousRecordCommandBinding_CanExecute(sender As System.Object, e As System.Windows.Input.CanExecuteRoutedEventArgs)
Dim MyView = DirectCast(Me.Resources("cvsAllOrders"), CollectionViewSource).View
e.CanExecute = (MyView IsNot Nothing) AndAlso (DirectCast(Me.Resources("cvsAllOrders"), CollectionViewSource).View.CurrentPosition > 0)
End Sub
Private Sub NextRecordCommandBinding_CanExecute(sender As System.Object, e As System.Windows.Input.CanExecuteRoutedEventArgs)
Dim MyView = DirectCast(Me.Resources("cvsAllOrders"), CollectionViewSource).View
e.CanExecute = (MyView IsNot Nothing) AndAlso (DirectCast(Me.Resources("cvsAllOrders"), CollectionViewSource).View.CurrentPosition < DirectCast(Me.Resources("cvsAllOrders"), CollectionViewSource).View.Cast(Of SalesOrderEntryModel.SalesOrder).Count() - 1)
End Sub
I can't get this to work. I have tried declaring the commands in my code-behind too. Doesn't make any difference. For reference, I have dozens of textboxes and other controls on my Page and need these keys to work wherever the current focus be.
Any help will greatly be appreciated.

Related

How do I change the value of a bound WPF class variable?

I am new to WPF/XAML.
I am making a scrolling marquee using a demo I found online.
It is WinForms using a HostedElement WPF custom User Control.
I was able to setup a class called MyModelView so that I can bind the text value of a textblock to a variable I control (MyTextProperty).
But I can't figure out how to change the variable. My goal is to have a textbox you can enter text into and it will bind to the textblock value and change it while the program is running.
I am not able to do this, which I am trying to do (Change bound variable to TextBox1 value - it is not a valid reference to the variable... what is?):
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
MyViewModel.MyTextProperty = TextBox1.text
End Sub
How do I reference that class variable to change the text? I am missing something.
MyViewModel.vb
Imports System.ComponentModel
Public Class MyViewModel
Implements INotifyPropertyChanged
Public Sub New()
Me.myTextValue = "WINNING!!!"
End Sub
Private myTextValue As String = String.Empty
Public Property MyTextProperty() As String
Get
Return Me.myTextValue
End Get
Set(ByVal value As String)
Me.myTextValue = value
NotifyPropertyChanged("MyTextProperty")
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Scrolling Marque.xaml
<UserControl x:Class="ScrollingMarquee"
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:interopdemo="clr-namespace:InteropDemo"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="300">
<UserControl.Resources>
<Storyboard x:Key="MarqueeScroll">
<DoubleAnimation RepeatBehavior="Forever"
Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)"
Storyboard.TargetName="spMarquee"
From="1500" To="-500"
Duration="0:0:0:7" />
</Storyboard>
</UserControl.Resources>
<Grid>
<Grid.DataContext>
<interopdemo:MyViewModel/>
</Grid.DataContext>
<StackPanel x:Name="spMarquee" Orientation="Horizontal" Width="Auto">
<TextBlock Text="{Binding MyTextProperty}" FontSize="28" VerticalAlignment="Center" Margin="30,0,60,0"/>
<TextBlock Text="Hello Scrolling Text!" Foreground="Firebrick" FontSize="28" VerticalAlignment="Center" Margin="40,0,60,0"/>
<TextBlock Text="Ticker 2000!" Foreground="Red" FontSize="28" FontStyle="Italic" FontWeight="Bold" VerticalAlignment="Center" Margin="50,0,80,0"/>
<StackPanel.RenderTransform>
<TranslateTransform/>
</StackPanel.RenderTransform>
</StackPanel>
</Grid>
</UserControl>
Form1.vb
Imports System.Windows.Media.Animation
Public Class Form1
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
Me.WindowState = FormWindowState.Maximized
ElementHost1.Visible = True
Dim storyRunMarquee As Storyboard = ScrollingMarquee1.FindResource("MarqueeScroll")
storyRunMarquee.Begin()
End Sub
End Class
I've tried referencing the variable but can't make it work. The scrolling animation displays as "Winning! Hello Scrolling Text! Ticker 2000!" But I'm trying to change the bound "Winning!" text.
Remove
<Grid.DataContext>
<interopdemo:MyViewModel/>
</Grid.DataContext>
Declare a member variable in the form to hold reference to the view model object:
Private scrollerViewModel As MyViewModel
Instantiate an object of class MyViewModel in Form1_Load and set it as a DataContext for user control.
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
Me.WindowState = FormWindowState.Maximized
ElementHost1.Visible = True
scrollerViewModel = New MyViewModel
ScrollingMarquee1.DataContext = scrollerViewModel
Dim storyRunMarquee As Storyboard = ScrollingMarquee1.FindResource("MarqueeScroll")
storyRunMarquee.Begin()
End Sub
Modify property:
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
scrollerViewModel.MyTextProperty = TextBox1.text
End Sub
I think you just need to cast the datacontext of ScrollingMarquee1 as MyViewModel
CType(ScrollingMarquee1.DataContext, MyViewModel).MyTextProperty = TextBox1.text

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

AddHandler from Page1 to Show Message MainWIndow using WPF and VB.NET

I'm stuck. I am trying to AddHandler for a button on Page1 such that when the button is clicked it will AddHandler to the MainWindow_MouseUp event, so I can click anywhere on the MainWindow to show a message.
How can I fix this problem?
Notes:
I have removed the Handler for MainWindow_MouseUp on MaindWindow_load event, so the message will no show on load of MainWindow.
I have a frame showing Page1.
When a button in Page1 is clicked, it's supposed to AddHandler, so MainWindow_MouseUp will show a message when MainWindow executes the MainWindow_MouseUp event.
Page1.xaml
<Page x:Class="Page1"
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"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1" Background="Red">
<Grid>
<Button Content="AddHandler for MainWindow MouseUp message"
Height="42"
HorizontalAlignment="Left"
Margin="0,126,0,0"
Name="Button1"
VerticalAlignment="Top"
Width="300" />
<TextBlock Height="84"
HorizontalAlignment="Left"
Margin="80,184,0,0"
Name="TextBlock1"
Text="The button will add a handler in order to show a message on the MainWindow MouseUp event"
VerticalAlignment="Top"
Width="151"
TextWrapping="Wrap" />
<TextBlock Height="23"
HorizontalAlignment="Left"
Margin="12,56,0,0"
Name="TextBlock2"
Text="Page1"
VerticalAlignment="Top" />
</Grid>
</Page>
Page1.vb
Class Page1
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles Button1.Click
Dim _MainWindow As New MainWindow
AddHandler _MainWindow.MouseUp, AddressOf Button1_Click
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"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Frame Source="Page1.xaml"
Height="300"
HorizontalAlignment="Left"
Margin="110,-1,0,0"
Name="Frame1"
VerticalAlignment="Top"
Width="300" />
<TextBlock Height="144"
HorizontalAlignment="Left"
Margin="0,48,0,0"
Name="TextBlock2"
Text="Click here to show a message if handler MainWindow MouseUp is added from page1"
VerticalAlignment="Top"
TextWrapping="Wrap"
Width="104"
Foreground="#FF003AFF" />
<TextBlock Height="153"
HorizontalAlignment="Left"
Margin="416,159,0,0"
Name="TextBlock3"
Text="Click here to show a message if handler MainWindow MouseUp is added from page1"
VerticalAlignment="Top"
TextWrapping="Wrap"
Foreground="#FFEF00FF" />
</Grid>
</Window>
MainWindow.vb
Public Class MainWindow
Private Sub Window_Loaded(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles MyBase.Loaded
RemoveHandler Me.MouseUp, AddressOf MainWindow_MouseUp
End Sub
Private Sub MainWindow_MouseUp(ByVal sender As Object, _
ByVal e As MouseEventArgs) _
Handles Me.MouseUp
Test1()
End Sub
Public Sub Test1()
MessageBox.Show("AddHandler added from page 1 was successful!")
End Sub
End Class
This is just all a bit confused. Let's look at what you are doing :
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _
System.Windows.RoutedEventArgs) Handles Button1.Click
Dim _MainWindow As New MainWindow
AddHandler _MainWindow.MouseUp, AddressOf Button1_Click
End Sub
This is making a new MainWindow and is linking its MouseUp event to the method Button1_Click - you're telling _MainWindow to execute Page1's Button1_Click method when it fires its MouseUp event. I suspect this is not what you want to do.
Now this :
RemoveHandler Me.MouseUp, AddressOf MainWindow_MouseUp
tells me that you don't want MainWindow_MouseUp to be hooked up right away. You can get rid of this line simply by removing the Handles clause like :
Private Sub MainWindow_MouseUp(ByVal sender As Object, _
ByVal e As MouseEventArgs) 'Handles Me.MouseUp
Test1()
End Sub
Now there's nothing to remove.
So... we're back to the problem of notifying MainWindow that Page1's button has been clicked. To do this, add an event to Page1 :
Class Page1
Shared Event PageButtonClick(sender As Object, e As RoutedEventArgs)
Private Sub Button1_Click(sender As System.Object, e As _
System.Windows.RoutedEventArgs) Handles Button1.Click
RaiseEvent PageButtonClick(sender, e)
End Sub
End Class
I've made the event shared here - this allows us to link it up without creating an instance of the page in MainWindow (ie: you can simply have a frame using Page1 as source). What we're doing here is raising a PageButtonClick event in Page1 when Button1 has been clicked.
In your main window, then :
Class MainWindow
Private Sub Window_Loaded(sender As System.Object, e As _
System.Windows.RoutedEventArgs) Handles MyBase.Loaded
'Here we listen for the Page1Click event and execute the method
'Page1_Clicked when we hear it
AddHandler Page1.PageButtonClick, AddressOf Page1_Clicked
End Sub
Private Sub Window_MouseUp(sender As System.Object, e As _
System.Windows.Input.MouseButtonEventArgs)
MessageBox.Show("Hello")
End Sub
Private Sub Page1_Clicked(sender As Object, e As RoutedEventArgs)
'When we get a click from Page1, add a handler to this window's
'MouseUp event and have it execute the Sub Window_Mouseup
AddHandler Me.MouseUp, AddressOf Window_MouseUp
End Sub
End Class
Now, the above is quite dangerous because you'll always be adding handlers any time the button is clicked. You should always be very careful about adding handlers - whenever you add one you MUST make sure to remove it before freeing up whatever has hooked it up (otherwise you end up with memory leaks since handlers can root an object!). I would always do something like this (in MainWindow) :
Private _mouseUpConnected As Boolean = False
Private Sub Page1_Clicked()
If Not _mouseUpConnected Then
AddHandler Me.MouseUp, AddressOf Window_MouseUp
_mouseUpConnected = True
End If
End Sub
This makes sure you only add the handler once. Now to get rid of it later :
Private Sub Window_Closed(sender As System.Object, e As System.EventArgs) _
Handles MyBase.Closed
RemoveHandler Page1.PageButtonClick, AddressOf Page1_Clicked
if _mouseupConnected then RemoveHandler Me.MouseUp, AddressOf Window_MouseUp
End Sub
This takes care of the PageButtonClick handler - it is added when the window is loaded and removed when it is closed. It also takes care of the MouseUp handler - if it has been connected, we disconnect it before closing. This ensures that MainWindow is properly garbage collected.

Update LineGraph Every Second

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.

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