How can I force a TabItem to initialize content on load? - wpf

[disclaimer: I am new to Visual Basic.]
In a WPF, I have a TabControl containing 2 TabItems:
The first TabItem contains a bunch of URLs.
The second TabItem consists of a DockPanel that contains a cefSharp webView (chromium embedded for .net)
When I click on a url in tab1 it loads a page in the browser contained in tab2... But, it only works if I have initialized the browser first by clicking on tab2.
After doing some searching, it looks like vb.net doesn't initialize the content in a TabItem until it becomes visible. (right?)
So, my question is, how can I force a non-selected tab to initialize its content on load, in the background? (ie. so I don't have to click on the tab or switch to it first)
EDIT:
As requested, here is the relevant code:
The relevant XAML consists of a single DockPanel named "mainBox"
<DockPanel Name="mainBox" Width="Auto" Height="Auto" Background="#afe0ff" />
And here is my "code behind" vb script:
Class MainWindow : Implements ILifeSpanHandler, IRequestHandler
Shared web_view1 As CefSharp.Wpf.WebView
Shared web_view2 As CefSharp.Wpf.WebView
Public Sub init(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Loaded
'This is in a DockPanel created on the xaml named mainBox
' set up tabControl:
Dim browserTabs As New TabControl()
browserTabs.BorderThickness = Nothing
Dim tab1 As New TabItem()
tab1.Header = "My Tab"
Dim tab2 As New TabItem()
tab2.Header = "Browser"
Dim tab1Content As New DockPanel()
Dim tab2Content As New DockPanel()
tab1.Content = tab1Content
tab2.Content = tab2Content
browserTabs.Items.Add(tab1)
browserTabs.Items.Add(tab2)
mainBox.Children.Add(browserTabs)
' set up browsers:
Dim settings As New CefSharp.Settings()
settings.PackLoadingDisabled = True
If CEF.Initialize(settings) Then
web_view1 = New CefSharp.Wpf.WebView()
web_view1.Name = "myTabPage"
web_view1.Address = "http://stackoverflow.com/"
web_view2 = New CefSharp.Wpf.WebView()
web_view2.Name = "browserPage"
web_view2.Address = "https://www.google.com"
web_view2.LifeSpanHandler = Me
web_view2.RequestHandler = Me
AddHandler web_view2.PropertyChanged, AddressOf web2PropChanged
tab1Content.Children.Add(web_view1)
tab2Content.Children.Add(web_view2)
End If
End Sub
End Class
So, in its default state, tab1 is showing at start up -- the browser on tab2 (web_view2) won't initialize until I click its tab or change to its tab via script. Hope this clears it up a bit.

Your code doesn't use Windows Forms' TabPage but perhaps this might still help
As we know, Controls contained in a TabPage are not created until the
tab page is shown, and any data bindings in these controls are not
activated until the tab page is shown. This is by design and you can
call TabPage.Show() method one by one as a workaround.
via MSDN forums
Also, based on the above idea, have you tried the following.
tab2.isEnabled = True
tab1.isEnabled = True
Or:
tab2.Visibility = True
tab1.Visibility = True
Also, the BeginInit Method might help in your situation.

I had the same problem but found it initializes if I use the name of the tabpage and show (tabName1.show()) on the form load code for each page ending with the one I want displayed:
tabPage2.show()
tabPage3.show()
tabPage4.show()
tabPage5.show()
tabPage1.show()
It ripples thru and you never see the pages changing but it works. The other suggestions didn't work for me in Visual Studio 2010.

Related

Define Click event on programmatically defined WPF GridViewColumnHeader

I am programmatically creating a GridViewColumn in WPF as follows:
Dim oGVCol As GridViewColumn = New GridViewColumn
Dim oHeaderTemplate As DataTemplate
oHeaderTemplate = New DataTemplate
Dim oGridFactory As FrameworkElementFactory = New FrameworkElementFactory(GetType(Grid))
oHeaderTemplate.VisualTree = oGridFactory
oGridFactory.SetValue(Grid.BackgroundProperty, Brushes.Transparent)
oGVCol.HeaderTemplate = oHeaderTemplate
(I have removed irrelevant code to set the content of the grid)
What I can't figure out is how to add a "click" event for the GridViewColumnHeader itself. I can add events to the Grid and any other Controls I added through the Factory objects, no problem. But I'm stuck on how to add an event to the header itself.
If you have a solution in VB.NET, great, but C# is fine too.
One (failed) attempt:
AddHandler TryCast(oGVCol.Header, GridViewColumnHeader).Click, AddressOf HeaderClick
Sadly it turns out that I cannot cast oGVCol.Header to a GridViewColumnHeader.
Ok, it may not be pretty, but I found a pretty decent solution to the problem.
Firstly, when I create the Grid Factory for the root element in the header's Visual Tree, I give it a name
oGridFactory.SetValue(Grid.NameProperty, <column name here>))
(Please note that the Names must have only letters, numbers and underscores so if your data contains column names that don't have those, you'll need to deal with that both here, to convert invalid names to valid ones, and below, to revert them back to their original names if necessary.... I won't detail that functionality here)
Also, I add an event handler to the Root "Grid" in the Template for the column header:
oGridFactory.AddHandler(Grid.SizeChangedEvent,
New SizeChangedEventHandler(AddressOf ColumnHeaderSizeChanged))
The "magic" happens in the procedure ColumnHeaderSizeChanged. This procedure is called both when the grid is Rendered the first time, but also when the user is manually resizing columns.
Signature:
Private Sub ColumnHeaderSizeChanged(sender As Object, e As SizeChangedEventArgs)
I keep a List(Of GridViewColumnHeaders) which is reset to an empty list when I need to replace the Grid with a different one. In the ColumnHeaderSizeChanged event I then do the following:
The first thing we need to do is get to the Root of the controls in the Column Header. For example, your column header may contain a TextBlock to show a column name, and icons to indicate it's been sorted up or down. That sort of thing. When the user clicks on the header they may be clicking on any of those controls, so:
Dim oParent As Object
Dim oColHeader As GridViewColumnHeader = Nothing
Dim sColHeaderName As String = String.Empty
Dim oGWH As Grid = Nothing
oParent = e.OriginalSource 'This may be any of the controls in the header.
If Not oParent Is Nothing Then
Try
While Not oParent.Parent Is Nothing
'So we keep going down the Tree until we hit the Root Parent
'which will be the main Grid created in the Grid Factory
oParent = oParent.Parent
End While
Catch
End Try
End If
'But at this point, if we still have a control, it will be the main Grid
If oParent Is Nothing Then
Exit Sub
End If
If TryCast(oParent, Grid) Is Nothing Then
'what the heck is this? This SHOULD be the Grid at the root of the Visual Tree,
'so if, for whatever reason, this is NOT a Grid, get outta here.
Exit Sub
End If
By this point we're on the main Grid, but now we need to get the GridViewColumnHeader into which this Grid has been created. So now we go to the TemplatedParent
While Not oParent.TemplatedParent Is Nothing
oParent = oParent.TemplatedParent
oColHeader = TryCast(oParent, GridViewColumnHeader)
If Not oColHeader Is Nothing Then
'This procedure is called both when the Grid View is first rendered,
'and when the user is dragging the column width.
If Mouse.LeftButton = MouseButtonState.Pressed Then
'Do something appropriate to when the user is resizing the column
Else
'Do something appropriate to when the grid
'is first Rendered or re-displayed
End If
Exit While
End If
End While
At this point we have the GridViewColumnHeader we need, so we can add it to the List and add a Handler for its Click event. moColHeaders is the List(Of GridViewColumnHeaders)
If Not oColHeader Is Nothing Then
If Not moColHeaders.Contains(oColHeader) Then
oColHeader.Name = <column name here>
moColHeaders.Add(oColHeader) 'Only need to add it once!
AddHandler oColHeader.Click, AddressOf HeaderClick
End If
End If
Now you can code your HeaderClick procedure to handle the GridViewColumnHeader.Click event.
Signature:
Private Sub HeaderClick(sender As Object, e As RoutedEventArgs)

How to wait for the event inside a function?

I'm trying to write a function in VB.Net WPF application for retrieving document’s height in MS WebBrowser control. I can get this value only after some time, when page has been rendered. So, I tried:
Private Function GetHeight(ByVal htmlstring As String) As Integer
Dim wb As New WebBrowser 'Declare WebBrowser
wb.Width = 940 'set Width
wb.NavigateToString(htmlstring) 'Navigate to content
Do Until wb.IsLoaded 'Wait until page is rendered
Loop
'Get DOM Document
Dim doc As mshtml.HTMLDocument = wb.Document
'Get sought value
Dim RetVal As Integer = CInt(doc.body.getAttribute("scrollHeight").ToString)
doc = Nothing : wb.Dispose() : wb = Nothing 'Free variables
Return RetVal 'Return value
End Function
But calling such function causes application to freeze. What should I do? Do I need to implement Async and Await keywords, as well as Threading.Tasks and how to achieve this?
According to MSDN, IsLoaded is a framework element property that indicates whether or not the control has been loaded for presentation, not whether the WebBrowser control's web page has been loaded.
While I question why you would instantiate a browser and have it navigate to a page in a method called "GetHeight"... what I think you want to do here is subscribe to the LoadCompleted event: https://msdn.microsoft.com/en-us/library/system.windows.controls.webbrowser.loadcompleted(v=vs.110).aspx
Perhaps you can construct your webbrowser and have it navigate in a different method, and subscribe to the LoadCompleted event with your GetHeight method?
Edit I neglected to mention that because you declared your browser control outside of xaml, the control will never be loaded (and thus, navgiation won't work) because it's not in the visual tree of the WPF application. You'll have to either use a webbrowser already declared in xaml (which I would recommend), or add it programmatically with something like this:
<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">
<StackPanel x:Name="MainPanel">
<Button Click="Button_Click"> Clicky</Button>
</StackPanel>
Class MainWindow
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim wb As New WebBrowser 'Declare WebBrowser
wb.Width = 940 'set Width
Me.MainPanel.Children.Add(wb)
wb.NavigateToString("www.stackoverflow.com")
AddHandler wb.LoadCompleted, Sub(s, ee) DoSomething()
End Sub
Private Sub DoSomething()
MessageBox.Show("blah")
End Sub
End Class

How to write out button event without using WPF controls?

I do not know how to specify in the title, this is in WPF visual basic. I want to know how do I write a code so that when a button is clicked, the tabcontrol selection will be = 1
Here is what I have in my MainWindow, RightWindowCommands:
<Button Content="Information" Cursor="Hand" Click="InformationButton_OnClick"
ToolTip="View the information"
x:Name="InformationView"/>
However, I did not use the WPF tools' Button, as this is a GUI that I have to place the button at RightWindowCommands, I want to know how to come out with the code so that InformationButton_OnClick gives me tabControl.SelectedIndex = 1. Please guide me on writing this code out
Here is a nice example for onclick event handling a picture, you can change it to a number instead of picture.
`AddHandler pic.Click, AddressOf pic_Click'
Private Sub pic_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim pic As PictureBox = DirectCast(sender, PictureBox)
' etc...
End Sub
Source : add on click event to picturebox vb.net

I can't use Window.Show despite of creating new Window instance

here's the problem:
In my WPF application I used to load/parse my .xaml files using XamlReader.Load Method to open a window in my application.
Codefragment of my function which return the window:
Dim win As New Window()
Dim myObject As Object
Dim xml As XmlReader = XmlReader.Create("mysample.xaml")
myObject = System.Windows.Markup.XamlReader.Load(xml)
win = CType(myObject, Window)
Return win
I use this to display all my different windows the user wants to see.
I open the window with win.Show and close it, when user switch to another window with win.Close. It works well!
Now to increase the performance I plan to do all the XAMLReader.Load at Application Start and store the information into a Dictionary:
Private Shared windict As Dictionary(Of String, Object)
Public Shared Sub ConvertXAMLToWindow(ByVal formName As String)
windict = New Dictionary(Of String, Object)
Dim myObject As Object
Dim xml As XmlReader = XmlReader.Create(formName)
myObject = System.Windows.Markup.XamlReader.Load(xml)
windict.Add(formName, myObject)
End Sub
Then I want to use that information when calling windows:
If windict.ContainsKey(formName) Then
Dim win As New Window()
Dim myObject As Object
myObject = windict(formName)
win = CType(myObject, Window)
Return win
End If
Now
This works well, but when I use win.Close to close my window I get an error when trying to open it again with win.Show, although I create an new instance of Window?
System.InvalidOperationException
Cannot set Visibility or call Show, ShowDialog... after a Window has
closed.
But it works when I don't use the Dictionary Method but the XAMLReader.Load directly - any ideas whats going on ? Somehow the window I get by returning XamlReader.Load seems different than the stored information from the dict?? Am I missing somehting? Thanks in advance!
You could use Hide() instead of Close()
Hide hides the Form, so instead of disposing of the form (and its controls) you make it invisible. Show will make it visible again.
Be careful though, the form in the dictionary will still hold the state from the previous time it was used.

Cannot dynamically add new button

I'm just starting to learn VB and Visual Studio and I've run across a problem. I've spent the best part of a day trying to find the answer and I have a horrible feeling that it's going to be something very simple that I've over looked.
I'm working on a WPF in Visual Studio 2010 and am trying to dynamically create a button on the main window when a button is clicked (I know, everything I've read tells me this is pretty basic!) Here's an edited snippet of the code I've written:
Imports System.Data.OleDb
Imports System.Windows.Forms
Imports Excel = Microsoft.Office.Interop.Excel
Class MainWindow
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles edit.Click
...
Dim newButton As New Button
newButton.Text = "New Button"
newButton.Top = 200
newButton.Left = 20
Me.Controls.Add(newButton)
...
End Sub
To my eyes, this looks perfectly simple and correct, but I'm getting an error:
"'Controls' is not a member of 'myApp.MainWindow'."
Has anybody come across this before or know what the problem is? Apologies if this does turn out to be a simple fix :)
The error you are getting is telling you that Controls does not exist within MainWindow. Basically, there is not property by that name accessible from your event handler. If you are working with WPF and MainWindow inherits Window, then you need to set something within the Content property.
The best way to go about this, would be to have some form of container control as the content of the window. You can define this in XAML or in code (via code you should set the Window.Content property). Then, you can add more controls to that container. Suggested containers are Grid, Canvas and StackPanel, etc.
I would suggest something like this:
XAML
<MainWindow ...>
<StackPanel x:Name="ControlContainer">
<Button Content="Click me to create buttons!" Click="CreateButton_Click" />
</StackPanel>
</MainWindow>
Code Behind
Private Sub CreateButton_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim button As New Button()
' Initialize the button
' ...
' Add the button to the stack panel
Me.ControlContainer.Children.Add(button)
End Sub
well MainWindow is not a form. create a new project and copy the same code in a button it will work.
you must find out what is the problem with your form control.
It looks like you are mixing WinForms and WPF coding - these are two different technologies.
This link may help you to add a button at runtime in WPF

Resources