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

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

Related

My Child window not closed in WPF

I’m working in WPF and I’m facing an issue regarding NDI container
In my MainWindow Xaml I have the MainMdiContainer
<mdi:MdiContainer Theme="Aero" mdi:MdiChild.Closed="MdiChild_Closed" Margin="0,0,0,80" Name="MainMdiContainer" Grid.Row="1" Background="{x:Null}">
</mdi:MdiContainer>
Of course I have another xaml page named “Registration” and I also have a button in mainWindow to open the mdiChild
Public Sub btnAddUser_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles btnAddUser.Click
MainMdiContainer.Children.Clear()
MainMdiContainer.Children.Add(New MdiChild() With { _
.Title = "Πρόγραμμα Εσωτερικής Διαχείρισης Διορισμός Επιτροπής και Χρηστών", _
.Height = (System.Windows.SystemParameters.PrimaryScreenHeight - 500), _
.Width = (System.Windows.SystemParameters.PrimaryScreenWidth - 430), _
.Style = Nothing, _
.Content = New Registration() _
})
End Sub
Public Sub MdiChild_Closed(sender As System.Object, e As System.Windows.RoutedEventArgs)
For Each child As MdiChild In MainMdiContainer.Children.ToList()
If (child.Name.ToString().Contains("mdiChildRegistration")) Then
MainMdiContainer.Children.Remove(child)
End If
Next
End Sub
Until this stage everything goes fine and the mdiChild opens
Now I want to close the child window by pushing a button
Private Sub btnClose_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles btnClose.Click
Dim mainWin As New MainWindow
Dim mdiCont As MdiContainer = mainWin.MainMdiContainer
mainWin.MdiChild_Closed(sender, e)
End Sub
But the mdiChild it not close… in fact the Childern in mdiCont is empty.
When I add a Childern
<mdi:MdiChild IsTabStop="False" />
In MainWindow then the Childern in mdiCont is not empty it has one Child which is closed when I'm try to open my child whindow by pressing the button.
I suspect that something in the add procedure has to do with but I don’t know what.
Is there someone to assist me?
Finally I've made it to clese the child window
And all comes from the child code behind
Private Sub btnClose_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles btnClose.Click
Dim mainWin As MainWindow = Economy.Application.Current.MainWindow
mainWin.MainMdiContainer.Children.Clear()
In the declaration of mainWin it has to be refered as
'Object.Application.Current.MainWindow'
in order to transfer the child window attributes and have the ability to clear it.
Not Removed because the Remove is not clear the child window.
Thank you very much for #dymanoid.

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.

WPF KeyBindings not working

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.

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