Closing a form from user control using dependency object - wpf

I have a navigation control that is displayed in multiple pages. So, I have no way to identify the exact class name during design time. Now, when user navigates to different page, I want to hide the current page. Basically, a typical menubar behaviour. I am able to get the outermost element as a dependency object using the code below.
Private Function GetTopLevelControl(ByVal control As DependencyObject) As DependencyObject
Dim tmp As New DependencyObject
tmp = control
Dim parent As New DependencyObject
parent = Nothing
While Not VisualTreeHelper.GetParent(tmp) Is Nothing
parent = VisualTreeHelper.GetParent(tmp)
End While
Return parent
End Function
Now, on the mouse down event, I am trying to write code to hide this parent object.
Private Sub Menu_Nomination_MouseDown(sender As Object, e As MouseButtonEventArgs)
Dim surveySearchPage As New SurveySearch
surveySearchPage.Show()
Dim parentControl As DependencyObject
parentControl = GetTopLevelControl(Me)
parentControl
End Sub
Problem is parentControl object has no hide or close property at all. So, I am currently stuck in trying to close the page.

Related

How do I change the style of FindTextBox of FlowDocumentReader to match the theme of my app?

I have a WPF app with a FlowDocumentReader control and when I click the search button to search the text, the text box doesn't match the theme of the app. The text does, but the background of the text box doesn't. So if I am using a dark theme, the text I type into the text box is white (which is correct), but the text box background is also white (incorrect, making text not legible).
Does anyone know how to fix this? I tried applying a style but I don't know which component to target.
Wow, Microsoft really does not make this easy.
My Process
I tried the easy trick of adding a Style TargeType="TextBox" to FlowDocumentReader.Resources, but that doesn't work.
I tried doing things the "right" way and overriding FlowDocumentReader's ControlTemplate, but the TextBox in question isn't even part of the ControlTemaplte! Instead, there's a Border named PART_FindToolBarHost. The TextBox we want is added as a child to PART_FindToolBarHost in code- but only after the user has clicked the "find" button (the one with the magnifying glass icon). You can see this for yourself by looking at the control's source code.
With no more XAML-only ideas, I had to resort to using code. We need to somehow get a reference to the TextBox being created, and I can't think of any better way than to manually search the visual tree for it. This is complicated by the fact that the TextBox only exists once the find command has been executed.
Specifically, the find button in FlowDocumentReader binds to ApplicationCommands.Find. I tried adding a CommandBinding for that command to FlowDocumentReader so I could use it as a trigger to retrieve the TextBox. Unfortunately, adding such a CommandBinding somehow breaks the built-in functionality and prevents the TextBox from being generated at all. This breaks even if you set e.Handled = False.
Luckily, though, FlowDocumentReader exposes an OnFindCommand- except, of course, it's a Protected method. So I finally gave in and decided to inherit FlowDocumentReader. OnFindCommand works as a reliable trigger, but it turns out the TextBox isn't created until after the sub finishes. I was forced to use Dispatcher.BeginInvoke in order to schedule a method to run after the TextBox was actually added.
Using OnFindCommand as a trigger, I was finally able to reliably get a reference to the "find" TextBox, which is actually named FindTextBox.
Now that I can get a reference, we can apply our own Style. Except: FindTextBox already has a Style, so unless we want to override it, we're going to have to merge the two Styles. There's no publicly-accessible method for this (even though WPF does this internally in places), but luckily I already had some code for this.
The Working Code
First, a Module with the helper methods I used:
FindVisualChild is used to loop through the visual tree and get a reference to FindTextBox.
MergeStyles is used to combine the existing Style with the Style we supply, once we have that reference.
Module OtherMethods
<Extension()>
Public Function FindVisualChild(obj As DependencyObject, Name As String) As FrameworkElement
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
Dim ChildObj As DependencyObject = VisualTreeHelper.GetChild(obj, i)
If TypeOf ChildObj Is FrameworkElement AndAlso DirectCast(ChildObj, FrameworkElement).Name = Name Then Return ChildObj
ChildObj = FindVisualChild(ChildObj, Name)
If ChildObj IsNot Nothing Then Return ChildObj
Next
Return Nothing
End Function
Public Function MergeStyles(ByVal style1 As Style, ByVal style2 As Style) As Style
Dim R As New Style
If style1 Is Nothing Then Throw New ArgumentNullException("style1")
If style2 Is Nothing Then Throw New ArgumentNullException("style2")
If style2.BasedOn IsNot Nothing Then style1 = MergeStyles(style1, style2.BasedOn)
For Each currentSetter As SetterBase In style1.Setters
R.Setters.Add(currentSetter)
Next
For Each currentTrigger As TriggerBase In style1.Triggers
R.Triggers.Add(currentTrigger)
Next
For Each key As Object In style1.Resources.Keys
R.Resources(key) = style1.Resources(key)
Next
For Each currentSetter As SetterBase In style2.Setters
R.Setters.Add(currentSetter)
Next
For Each currentTrigger As TriggerBase In style2.Triggers
R.Triggers.Add(currentTrigger)
Next
For Each key As Object In style2.Resources.Keys
R.Resources(key) = style2.Resources(key)
Next
Return R
End Function
End Module
Then, there's StyleableFlowDocumentReader, which is what I named my extended control that inherits FlowDocumentReader:
Public Class StyleableFlowDocumentReader
Inherits FlowDocumentReader
Protected Overrides Sub OnFindCommand()
MyBase.OnFindCommand()
Dispatcher.BeginInvoke(Sub() GetFindTextBox(), DispatcherPriority.Render)
End Sub
Private Sub GetFindTextBox()
findTextBox = Me.FindVisualChild("FindTextBox")
ApplyFindTextBoxStyle()
End Sub
Private Sub ApplyFindTextBoxStyle()
If findTextBox IsNot Nothing Then
If findTextBox.Style IsNot Nothing AndAlso FindTextBoxStyle IsNot Nothing Then
findTextBox.Style = MergeStyles(findTextBox.Style, FindTextBoxStyle)
Else
findTextBox.Style = If(FindTextBoxStyle, findTextBox.Style)
End If
End If
End Sub
Private findTextBox As TextBox
Public Property FindTextBoxStyle As Style
Get
Return GetValue(FindTextBoxStyleProperty)
End Get
Set(ByVal value As Style)
SetValue(FindTextBoxStyleProperty, value)
End Set
End Property
Public Shared ReadOnly FindTextBoxStyleProperty As DependencyProperty =
DependencyProperty.Register("FindTextBoxStyle",
GetType(Style), GetType(StyleableFlowDocumentReader),
New PropertyMetadata(Nothing, Sub(d, e) DirectCast(d, StyleableFlowDocumentReader).ApplyFindTextBoxStyle()))
End Class
And then, finally, a usage example:
<local:StyleableFlowDocumentReader x:Name="Reader">
<local:StyleableFlowDocumentReader.FindTextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</local:StyleableFlowDocumentReader.FindTextBoxStyle>
<FlowDocument/>
</local:StyleableFlowDocumentReader>

Double click listview item and pass the variable to another page to run a search

I have 2 forms; MainWindow and OwnerShares. There is a search on MainWindow that looks up what items are being shared. OwnerShares has a search function that looks up the owner details and what they are sharing. On OwnerShares the options are displayed in a ListView. What i want to do is allow the user to double click on a row in ListView and be directed to the MainWindow which runs the SEARCH based on the value that was selected from the ListView. The variable ACEName will be the one that i want to pass to the MainWindow form search.
Private Sub listSearchOwner_MouseDoubleClick(sender As Object, e As MouseButtonEventArgs) Handles listSearchOwner.MouseDoubleClick
Dim ACEName As String
ACEName = listSearchOwner.SelectedItems(0).xShareName
OwnerShares.Close()
End Sub
The name of the search in MainWindow is (cmdSearch_Click). I am currently getting the value that i click on with the above code. I just dont know how to open the MainWindow form and run the search command automatically.
If I read your question correctly you could just call show your form and run your other routine
Private Sub listSearchOwner_MouseDoubleClick(sender As Object, e As MouseButtonEventArgs) Handles listSearchOwner.MouseDoubleClick
Dim ACEName As String
ACEName = listSearchOwner.SelectedItems(0).xShareName
OwnerShares.Close()
MainWindow.show()
' YourMethod or Button here.
End Sub
Approach it this way:
Add a module to your project, if you don't have one.
Create a public variable in the module, e.g.
Public VariableName As DataType
Everytime that you double-click the listview control, assign a value to your variable.
After getting the value stored in the public variable, delete the value to create a room to pass another value again.
That should do the trick.

OnVisualChildrenChanged is called before dependency properties are set

I have created a custom control (CartesianCanvas) that stores some of the properties of any child element when it is added. To do this I have created a new collection property(ItemsInfo) and overrode OnVisualChildrenChanged so that when a child is added or removed the corresponding properties are added or removed from the collection property.
However, when I add children to the control through XAML, OnVisualChildrenChanged seemes to be called before the properties have been set as all the properties have their default values. This is not the case when a child is added after the window has been loaded. How can I ensure that the child's properties have been set when OnVisualChildrenChanged is called?
Here is my code:
Public ReadOnly Property ItemsInfo As Collection(Of CartesianInfo)
Get
Return DirectCast(GetValue(ItemsInfoProperty), Collection(Of CartesianInfo))
End Get
End Property
Friend Shared ReadOnly ItemsInfoKey As System.Windows.DependencyPropertyKey = System.Windows.DependencyProperty.RegisterReadOnly("ItemsInfo", GetType(Collection(Of CartesianInfo)), GetType(CartesianCanvas), New System.Windows.PropertyMetadata(New Collection(Of CartesianInfo)))
Public Shared ReadOnly ItemsInfoProperty As DependencyProperty = ItemsInfoKey.DependencyProperty
Protected Overrides Sub OnVisualChildrenChanged(visualAdded As DependencyObject, visualRemoved As DependencyObject)
Try
If Not visualAdded Is Nothing Then
If Not GetType(FrameworkElement).IsAssignableFrom(visualAdded.GetType) Then
Me.Children.Remove(visualAdded)
Throw New Exception("The object added:" & visualAdded.ToString & " was not of type or decended from: FrameworkElement and so was removed from CartesianCanvas")
Else
AddItemInfo(visualAdded)
End If
End If
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
If Not visualRemoved Is Nothing Then
RemoveItemInfo(visualRemoved)
End If
MyBase.OnVisualChildrenChanged(visualAdded, visualRemoved)
End Sub
Private Sub AddItemInfo(ByRef item As FrameworkElement)
Dim itemsInfoCollection As New Collection(Of CartesianInfo)
itemsInfoCollection = ItemsInfo
Dim ItemXCoordinate As Double
Dim ItemYCoordinate As Double
Dim ItemCalculateFromVerticalCenter As Boolean
Dim ItemCalculateFromHorizontalCenter As Boolean
If Double.IsNaN(Canvas.GetLeft(item)) Then
Canvas.SetLeft(item, 0)
End If
If Double.IsNaN(Canvas.GetTop(item)) Then
Canvas.SetTop(item, 0)
End If
ItemXCoordinate = Canvas.GetLeft(item)
ItemYCoordinate = Canvas.GetTop(item)
If item.VerticalAlignment = Windows.VerticalAlignment.Center Then
ItemCalculateFromVerticalCenter = True
End If
If item.HorizontalAlignment = Windows.HorizontalAlignment.Center Then
ItemCalculateFromHorizontalCenter = True
End If
ItemsInfo.Add(New CartesianInfo(item, ItemXCoordinate, ItemYCoordinate, ItemCalculateFromVerticalCenter, ItemCalculateFromHorizontalCenter))
SetValue(ItemsInfoKey, ItemsInfo)
PositionChild(item)
End Sub
Here is my XAML
<local:CartesianCanvas x:Name="MainCanvas">
<Button Width="50" Height="100" Canvas.Top="100" Canvas.Left="20"
Content="test" Click="Button_Click"/>
</local:CartesianCanvas>
WPF is pretty complicated especially when writing custom controls therefore my first suggestion would be - leave it. Take an already existing control or composite few already existing controls into UserControl instead of writing everything from scratch.
Controls in WPF may contain any other control and the control's children are loaded only when needed. That is why you run into your problem.
The solution is when OnVisualChildrenChanged event fires, you should run through all children and subscribe to their Initialized or Loaded event. Once the child is loaded the event will fire and the handler will be called. Furthermore inside the handler you shouldn't forget to unsubscribe to the event.

close wpf user control in VB.NET

I'm stuck on this issue, please help.
I created an user control named "CreateNewCase_uc" within which I created a button Close named "btnClose"
In my MainWindow, I created a Grid named "grid1" and a button Open named "btnCreateNewCase" with this code
Private Sub btnCreateNewCase_Click(sender As Object, e As RoutedEventArgs)
Dim cnc As CreateNewCase_uc = New CreateNewCase_uc
grid1.Children.Clear()
grid1.Children.Add(cnc)
End Sub
My question : which Code I need to Write for my bntClose button which is inside the user control to close or make disappeared the user control in VB.NET
Thanks in advance for your help.
You need to get the Parent control then remove it.
' from inside your close button on UC.
Dim parent = TryCast(Me.Parent, Grid))
If Not parent Is Nothing Then
parent.Children.Remove(Me)
End If
You may simply set the UserControl's Visibility to Collapsed:
Private Sub btnCreateNewCase_Click(sender As Object, e As RoutedEventArgs)
Me.Visibility = System.Windows.Visibility.Collapsed
End Sub

Handling events from user control containing a WPF textbox

In order to take advantage of the spell checking ability of WPF textboxes, I have added one to a user control (with the use of elementhost). This user control is used in various window forms. My current problem is trying to handle keyup events from this textbox but the windows form is unable to "get" any event from the control. I can access the properties of the textbox just fine (i.e. text, length, etc.) but keyboard events don't seem to work.
I have found, however, that the following will bring back events from the WPF textbox:
Public Class MyUserControl
Private _elementHost As New ElementHost
Private _wpfTextbox As New System.Windows.Controls.Textbox
Private Sub MyUserControl_Load(...) Handles Me.Load
Me.Controls.Add(_elementHost)
_elementHost.Dock = DockStyle.Fill
_elementHost.Child = _wpfTextbox
Dim MyEventInfo As EventInfo
Dim MyMethodInfo As MethodInfo
MyMethodInfo = Me.GetType().GetMethod("WPFTextbox_KeyUp")
MyEventInfo = _wpfTextBox.GetType().GetEvent("PreviewKeyUp")
Dim dlg As [Delegate] = [Delegate].CreateDelegate(MyEventInfo.EventHandlerType, Me, MyMethodInfo)
MyEventInfo.AddEventHandler(_wpfTextBox, dlg)
End Sub
Public Sub WPFTextbox_KeyUp(ByVal sender As Object, ByVal e As RoutedEventArgs)
' something goes here
End Sub
End Class
The user control is now able to do something after the PreviewKeyUp event is fired in the WPF textbox. Now, I'm not completely sure how to have the window form containing this user control to work with this.
Im a C# person not VB so please bear with me.. Basically you could assign the event from your Window rather than within your UserControl.. So in the constructor of your Window assign the PreviewKeyUp:
this.myUserContorl.PreviewKeyUp += new System.Windows.Input.KeyEventHandler(WPFTextbox_KeyUp);
then place the event handler in your Window:
private void WPFTextbox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
}
Incidentally you needn't go through the hassle of capturing the event in your UserControl as you can still access your TextBox within you UserControl directly from your Window (if you make it public), again from your constructor in your Window:
this.myUserContorl.wpfTextbox.PreviewKeyUp += new System.Windows.Input.KeyEventHandler(WPFTextbox_KeyUp);
I imagine it would look like this in VB (at a guess):
AddHandler myUserContorl.wpfTextbox.PreviewKeyUp, AddressOf WPFTextbox_KeyUp
ElementHost has a static method called EnableModelessKeyboardInterop(). Try calling it?
ElementHost.EnableModelessKeyboardInterop();
Read more here
Seems basic, but did you set the KeyPreview to TRUE on your form?

Resources