wpf richtextbox in winforms application - wpf

I would like to use a WPF RichTextBox in a WinForms project written with VB
I have created the WinForms project with one form and a button
I then added a new project WPF User Control Library placed a WPF RichTextBox on the WPF form
I added ElementHost interoperability to the WinForm with these Imports
Imports System
Imports System.Windows.Forms
Imports System.Windows.Forms.Integration
From here I am lost some of the SO question are 10 to 7 years old the MS tutorial is not much help
Code from WPF Form
Public Class UserControl1
ReadOnly rtbWPF As New UserControl
ElementHost
wpfwindow.Show
End Class
I did not post the XAML code NOT sure how to do that
So the question what to do next to link the WPF form with the RTB to the WinForms form?
I would like to load data from a SQLite DB into the WPF RichTextBox and save the text entered in the RTB into the DB

This answer is meant to expand on #KyleWang wonderful answer
One BIG issue with Vectors choice of the WPF RichTextBox is There is no Text property in the WPF RichTextBox control. Here is one way to get all of the text out. That said I would IMHO would suggest using the WPF Plain TextBox control
Vector also commented on how to hide the HotReload in the title bar
Tools > Options > Debugging > General > un-check Enable UI Debugging Tools for XAML
OK Code Below hope this is helpful if you decide to use a WPF control in WinForms for spell checking
Public Class frmStart
Dim rtb As Windows.Controls.RichTextBox = New Windows.Controls.RichTextBox()
Dim tb As Windows.Controls.TextBox = New Windows.Controls.TextBox()
Dim str As String = " "
Private Sub frmStart_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ElementHost1.Child = rtb
rtb.SpellCheck.IsEnabled = True
ElementHost2.Child = tb
tb.SpellCheck.IsEnabled = True
If str.Length < 100 Then
rtb.VerticalScrollBarVisibility = Windows.Controls.ScrollBarVisibility.Visible
End If
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
str = "Plain WPF TxtBox"
tb.Text = str
rtb.AppendText("Heree is som mispelled txt se if the dictioary wrks more nonsense to see the scroll bar's will this word wrapp or is that rapp")
End Sub
Private Sub btnGet_Click(sender As Object, e As EventArgs) Handles btnGet.Click
Dim elementHost = ElementHost1
Dim wpfRichText = CType(elementHost.Child, Windows.Controls.RichTextBox)
Dim range As Windows.Documents.TextRange = New Windows.Documents.TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd)
Dim allText As String = range.Text
tbMsg.Text = allText.ToString
End Sub
Private Sub btnGTB_Click(sender As Object, e As EventArgs) Handles btnGTB.Click
Dim elementHost = ElementHost2
Dim text = tb.Text
tbMsg.Text = text.ToString
End Sub

To host a WPF control in Winforms, you can refer to the following two ways.
First, both of them need to add a ElementHost control into form.
Solution A:
Directly declare the wpf controls (using Windows.Controls)
Dim rtb As Windows.Controls.RichTextBox = New Windows.Controls.RichTextBox()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
rtb.SpellCheck.IsEnabled = True
ElementHost1.Child = rtb
End Sub
Solution B:
Create a new User Control(WPF) and edit the content in "UserControl1.xaml" as follows.
<Grid>
<RichTextBox x:Name="richtextbox" Foreground="Black" FontSize="24" Margin="0"></RichTextBox>
<RichTextBox SpellCheck.IsEnabled="True" />
</Grid>
Then modify the code in 'form1.vb'
Private uc As UserControl1 = New UserControl1()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ElementHost1.Child = uc
End Sub

Related

How to properly expose Windows form object to other classes

I have a basic question. I'm creating a VSTO Word Addin. I have a Ribbon button which opens a WPF dialog box called TableSelector. It is hosted in a Windows form.
Public Class RibbonControl
Private f As Form
Private Sub btnSelectTable_Click(sender As Object, e As RibbonControlEventArgs) Handles btnSelectTable.Click
Dim h As New System.Windows.Forms.Integration.ElementHost()
Dim tableselector1 As New TableSelector
f = New Form()
f.MaximumSize = New Size(500, 380)
f.MinimumSize = New Size(500, 380)
f.MaximizeBox = False
f.Name = "HostForm"
h.Dock = DockStyle.Fill
h.Child = tableselector1
f.Controls.Add(h)
f.Show()
f.TopMost = True
End Sub
Public ReadOnly Property hostForm() As Form
Get
Return f
End Get
End Property
End Class
Then I have a close button on that WPF control to close the dialog box. When I click it I get runtime error "Object reference not set to an instance of an object." on line 3 of the below code.
Private Sub btnClose_Click(sender As Object, e As RoutedEventArgs) Handles btnClose.Click
Dim ribbon As New RibbonControl()
ribbon.hostForm.Close()
End Sub
What am I missing?
You can use following code for get the specific window for close
Window win = Application.Current.Windows.OfType<Window>().SingleOrDefault(w => w.Name == "Window Name");
win.Close();

Error with adding WPF user control to ElementHost in Windows Form

I am trying to add a WPF user control to windows form. The WPF user control currently does not have anything in it, but I will be adding buttons. In form load, I do this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Application.EnableVisualStyles()
Dim elemHost As New ElementHost
Dim wuc As WPFUC = New WPFUC
elemHost.Controls.Add(wuc) <<-- I get error here
'elemHost.child = wuc <<-- and here
AddSolid()
'AddPanel()
End Sub
Error is "Value of type WindowsApplication1.WPFUC cannot be converted to System.Windows.Forms.Control. What else should I do?
I just calculated the screen points using PointToScreen method and specify that as location of WPF window wherever I needed.

WPF Running a command on a windows form

I have a WPF Project to which i've added a Windows Form.
The windows form has a method runme(). But I'd like to call it from the WPF form.
Currently I've used
Dim wpfForm as new wpfForm
wpfForm.Show()
to display the the WPF form from a Windows form.
I'm not sure how to send a command back though.
I've tried:
Me.parent.runme()
But this gives me errors
The only way that I can think of to do what you are wanting is to create a Custom Constructor for your Winform and pass the reference to the Wpf Window to it. There appears to be no easy way to assign a WPF Window as an Owner/Parent of a Winforms Window.
Winforms Form
Public Class Form1
Dim myParent As System.Windows.Window
Public Sub New()
InitializeComponent()
End Sub
Public Sub New(parent As System.Windows.Window)
myParent = parent
InitializeComponent()
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
CType(myParent, MainWindow).runMe()
End Sub
End Class
Wpf Form
Class MainWindow
Dim winForm As Form1
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
winForm = New Form1(Me)
winForm.Show()
End Sub
Public Sub runMe()
Me.Background = Brushes.Red
End Sub
End Class
Eventually what worked was to create an interface as suggested by Cyborgx37
Interface ILogOut
Sub DoIt()
End Interface
Class WinFormWindow : Implements ILogOut
Public Sub DoIt() Implements ILogOut.DoIt
MsgBox("Message Received!")
End Sub
End Class
You can then use the ILogOut interface to send a message to the Windows forms window from a WPF window

Cannot update a treeview inside a usercontrol

I created a usercontrol with a treeview inside it. The treeview will be populated if I add nodes in the onload handler of the usercontrol. But after that(for example, I click a button in its parent form), the treeview will not refresh. I can see the nodes was updated in memory, but it just cannot display on the screen. I called refresh/update after adding nodes. Any suggestion is appreciated.
I put a quick test together based on your description and it seems to paint just fine.
UserControl1
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class UserControl1
Inherits System.Windows.Forms.UserControl
'UserControl overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.TreeView1 = New System.Windows.Forms.TreeView
Me.SuspendLayout()
'
'TreeView1
'
Me.TreeView1.Dock = System.Windows.Forms.DockStyle.Fill
Me.TreeView1.Location = New System.Drawing.Point(0, 0)
Me.TreeView1.Name = "TreeView1"
Me.TreeView1.Size = New System.Drawing.Size(150, 150)
Me.TreeView1.TabIndex = 0
'
'UserControl1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.Controls.Add(Me.TreeView1)
Me.Name = "UserControl1"
Me.ResumeLayout(False)
End Sub
Friend WithEvents TreeView1 As System.Windows.Forms.TreeView
End Class
Public Class UserControl1
Public Sub AddNewNode(ByVal text As System.String)
TreeView1.Nodes.Add(text)
End Sub
End Class
Put the usercontrol on a form with a button
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
UserControl11.AddNewNode(Now.ToString)
End Sub
End Class
If you are seeing proper painting as well then look at any graphics handling in the parent form then the usercontrol then the controls within the usercontrol. We really need more info.
Thank you, Dave. I figured it out. I put the usercontrol twice to my form by mistake(I cannot remember how I did it). And the one I operate is underneath the other one. That's why I cannot see it. Sorry for wasting your time.

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