The problem is importing Microsoft.Office.Interop.Word (which I need elsewhere in this class) breaks "New Frame()". The error is
'New' cannot be used on an interface
My guess is that Interop.Word redefines "frame". How do I fix this?
My code:
Imports C1.WPF
Imports Microsoft.Office.Interop.Word
End Sub
Private Sub btn_SendQuote_Click(sender As Object, e As RoutedEventArgs) Handles btn_SendQuote.Click
Dim tab_SendQuote As New C1TabItem()
Dim frame_SendQuote As New Frame()
Dim scroller_SendQuote As New ScrollViewer()
Dim str_Name As String = "Send Quote"
Dim str_NavigationLink As String = "PM_SendQuote.xaml"
createNewTab(tab_SendQuote, frame_SendQuote, scroller_SendQuote, str_Name, str_NavigationLink)
End Sub
Private Sub createNewTab(tab As C1TabItem, frame As Frame, scroller As ScrollViewer, str_TabName As String, str_NavigationLink As String)
'Function to be used for adding tabs
'Add and name new tab
tab.Header = tabcontrol.Items.Count + 1 & ". " & str_TabName
tab.CanUserClose = True
tabcontrol.Items.Add(tab)
'Add frame to the tab and include new job subform page
With frame
.NavigationService.Navigate(New Uri(str_NavigationLink, UriKind.Relative))
.HorizontalAlignment = HorizontalAlignment.Stretch
.VerticalAlignment = VerticalAlignment.Top
.Margin = New Thickness(0, 0, 0, 0)
End With
With scroller
.CanContentScroll = vbTrue
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
.Content = frame
End With
tab.Content = scroller
' Set new tab as active tab
tabcontrol.SelectedIndex = tabcontrol.Items.IndexOf(tab)
End Sub
Related
This is my first question ever, so please spare me if I did something wrong.
I have a small survey form that automatically draw questions and answers from a SQL server table, and create a question label (Label_Questionnaire(i)), a panel to nest all radiobuttons for the answer of each question (Panel_Response(i)), and 3 radiobuttons(yes, no, n/a), named RadioButton_Answers(i)_1 . All questions and answers are inside a big panel (Panel_Survey) to allow user to scroll up and down (around 50 questions).
When I run the program, I can only see questions but none of the radiobuttons are showing. What I did try are:
Use .BringToFront to bring the Panel_Response and all radiobuttons to front.
Change .Parent to Controls.Add . Instead of using .Parent, I use Panel_Survey.Controls.Add(Panel_Response) and Panel_Response.Controls.Add(RadioButton_Answers_1)
Force Panel_Response.Visible = True and all radiobuttons visible = true
(I know it might sound stupid, but I'm out of trick)
How do I make those radiobuttons show up? If not, are there any better designs for this kind of survey form? Thank you for any advice, in advance!
Below is my code:
Protected Overrides Sub OnLoad(e As EventArgs)
Dim PanelCount As Integer
Dim QuestionName As String
Dim Response1 As String
Dim Response2 As String
Dim Response3 As String
Dim InitialX As Integer = Panel_Survey.Left
Dim InitialY As Integer = Panel_Survey.Top
Dim SizeX As Integer = 1000
Dim SizeY As Integer = 25
'Load the survey
Try
'Get a list of questions and answers into array of list
Dim ListofQuestionandAnswers As New List(Of List(Of String))
Dim conn As New SqlClient.SqlConnection
conn.ConnectionString = ConnectionString
Dim CommandString As String = "SELECT [QuestionID], [QuestionName] ,[Response1],[Response2],[Response3] FROM [Question_List] ORDER BY [QuestionID]"
Dim Command As New SqlClient.SqlCommand
Command.CommandText = CommandString
Command.Connection = conn
Dim dr As SqlClient.SqlDataReader
conn.Open()
dr = Command.ExecuteReader
While dr.Read
Dim ls As New List(Of String)
ls.Add(dr.GetValue(0).ToString)
ls.Add(dr.GetValue(1).ToString)
ls.Add(dr.GetValue(2).ToString)
ls.Add(dr.GetValue(3).ToString)
ls.Add(dr.GetValue(4).ToString)
ListofQuestionandAnswers.Add(ls)
End While
conn.Close()
PanelCount = ListofQuestionandAnswers.Count
For i = 0 To ListofQuestionandAnswers.Count - 1
QuestionName = ListofQuestionandAnswers(i)(1)
Response1 = ListofQuestionandAnswers(i)(2)
Response2 = ListofQuestionandAnswers(i)(3)
Response3 = ListofQuestionandAnswers(i)(4)
Dim Label_Questionnaire As New Label
Dim Panel_Response As New Panel
Dim RadioButton_Answers_1 As New RadioButton
Dim RadioButton_Answers_2 As New RadioButton
Dim RadioButton_Answers_3 As New RadioButton
'Condition the label
With Label_Questionnaire
.Parent = Panel_Survey
.Name = "Label_Questionnaire" + i.ToString
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = QuestionName
.ForeColor = Color.Black
.Location = New Point(InitialX, InitialY)
.AutoSize = True
End With
'Condition the panel
With Panel_Response
'Panel_Survey.Controls.Add(Panel_Response)
.Parent = Panel_Survey
.Name = "Panel_Questionnaire" + i.ToString
.Location = New Point(InitialX + 880, InitialY)
.Width = 250
.Height = 25
.BringToFront()
End With
Dim j As Integer
Dim h As Integer
j = Panel_Response.Left
h = Panel_Response.Top
'Condition the radiobuttons for answers
With RadioButton_Answers_1
.Parent = Panel_Response
.Name = "RadioButton_Answers" + i.ToString + "_1"
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = Response1
.ForeColor = Color.Black
.Location = New Point(j, h)
.AutoSize = True
h += RadioButton_Answers_1.Height
End With
With RadioButton_Answers_2
.Parent = Panel_Response
.Name = "RadioButton_Answers" + i.ToString + "_2"
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = Response2
.ForeColor = Color.Black
.Location = New Point(RadioButton_Answers_1.Right, h)
.AutoSize = True
End With
With RadioButton_Answers_3
.Parent = Panel_Response
.Name = "RadioButton_Answers" + i.ToString + "_3"
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = Response3
.ForeColor = Color.Black
.Location = New Point(RadioButton_Answers_2.Right, h)
.AutoSize = True
End With
InitialY = InitialY + SizeY + 10
Next
Catch ex As Exception
MessageBox.Show(String.Format("Error: {0}", ex.Message), "Error while creating questions and answers", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Few basic problems here:
You seem to assume that when a control is placed inside a panel, it must be placed at a location relative to the form. It doesn't; it places relative to the Panel which has its own coordinate system starting at 0,0 in the top left of the panel - you initialize j and h (for locations of the radio buttons) to the Left and Top of the panel they're in, but they should be inited to 0,0 if you want the radiobuttons to start at the top left of the panel. If the Panel is placed at 300,300 on a form, and you place a radiobutton inside the panel also at 300,300 (because you copied its left and top) then the radio button will look like it's at 600,600 on the form because its at 300,300 inside a panel that is at 300,300
You only make your panel 250 wide - it's barely wide enough to show a single radio button
You increment h once, by an amount that means the next radiobutton disappears off the bottom of the panel (which is 25 pixels high)
Here:
With Panel_Response
'Panel_Survey.Controls.Add(Panel_Response)
.Parent = Panel_Survey
.Name = "Panel_Questionnaire" + i.ToString
.Location = New Point(InitialX + 880, InitialY)
.Width = 2500
.Height = 25
.BringToFront()
.BackColor = Color.Red
End With
Dim j As Integer
Dim h As Integer
j = 0
h = 0
Paste that over your app code and run it again. I made the panel BackColor red so you can more easily see where the panel is
I guess you need to decide how you want your UI to look. If the radiobuttons are laid out vertically, don't increment X when you add them to the panel (by setting the parent property). Make the panel tall enough to accommodate them (25 px not enough)
Use a FlowLayoutPaanel or TableLayoutPanel instead
In a WPF app we have the need to sometimes create a new tab that contains a Page inside a Frame..
Once the page has been opened (initialised once) it still seems to stay in navigation history and attempts to load data that may not be relevant at the time.
I have tried a myriad of methods including NavigationService.RemoveBackEntry, but it still persists :-(
This is an example of how the tab/page are opened
Private Sub CashFlow_Edit(sender As Object, e As RoutedEventArgs)
Try
Dim DGV As DGVx = ReportsCashFlow_Grid.FindName("CashFlow_DGV")
e.Handled = True
IsNewRecord = False
If DGV.SelectedItems.Count = 1 Then
Dim row As System.Data.DataRowView = DGV.SelectedItems(0)
Form_ID = row("ID")
Dim vName As String = row("Name")
Dim vTab As STC_Tabx = Application.Current.MainWindow.FindName(TabName)
Dim TabControl As STCx = Application.Current.MainWindow.FindName("AccountingReports_TabControl")
If Not vTab Is Nothing Then
vTab.Close()
End If
Dim MCFrame As New Frame
Dim MCTab As New STC_Tabx
With MCTab
.Name = TabName
.Header = " " & vName & " "
.ImageSource = ReturnImageAsString("Edit.png", 16)
.CloseButtonVisibility = DevComponents.WpfEditors.eTabCloseButtonVisibility.Visible
.TabToolTip = "View or edit the " & vName & " template"
.Content = MCFrame
End With
RemoveHandler MCTab.Closing, AddressOf TabControl_TabClosing
AddHandler MCTab.Closing, AddressOf TabControl_TabClosing
Dim vGrid As Grid = Application.Current.MainWindow.FindName("MainGrid_Accounting")
RegisterControl(vGrid, MCTab)
TabControl.Items.Add(MCTab)
Dim MCPage As New ReportCashFlow_Page
MCFrame.NavigationService.Navigate(MCPage)
LoadedTabs(TabName)
MCTab.IsSelected = True
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
To remove all the back entries do something like:
while(NavigationService.CanGoBack)
{
NavigationService.RemoveBackEntry();
}
It's not the clean bit of code I would like, but it works - create a global Boolean - when the sub that opens the tab/page is called it's set to true and the loading event will only run the loading code if this is true - it's set to false at the end.
Private Sub ReportCashFlow_Page_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Try
If IsNewTab = False Then
Exit Sub
End If
'Run all the loading code here
Catch ex As Exception
EmailError(ex)
Finally
IsNewTab = False
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() CashFlow_LoadBudget(), SendOrPostCallback), Nothing)
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() ToggleReserve(), SendOrPostCallback), Nothing)
End Try
End Sub
The user can select from a DataGrid either by double clicking on the row, or selecting the row and clicking a button.
Using the first method the new page is initialised but the loaded event is not fired.
Using the second method the new page is initialised, the old one fires the unloaded event and the new one fires the loaded event and the new tab opens.
As both the click and doubleclick events are firing the same sub I can't figure out why one works and the other doesn't - when not in debug the new tab is formed using the first method and when clicked the loaded event is then fired, but this doesn't show in debug.
Private Sub Reports_BalanceSheets_EditRecord(sender As Object, e As RoutedEventArgs)
Try
NewRecord = False
Dim DGV As CustomControl.DGVx = Reports_BalanceSheets_Grid.FindName("Reports_BalanceSheets_DGV")
If DGV.SelectedItems.Count = 1 Then
Dim row As System.Data.DataRowView = DGV.SelectedItems(0)
FormID = row("ID")
Dim vName As String = row("Name")
Dim vTab As CustomControl.STC_Tabx = Application.Current.MainWindow.FindName("Reports_BalanceSheetTab")
Dim TabControl As CustomControl.STCx = Application.Current.MainWindow.FindName("AccountingReports_TabControl")
Dim vImageSource As String = ReturnImageAsString("Profit_Loss.png", 16)
If vTab Is Nothing Then
Dim ReportsBalanceSheetFrame As New Frame
Dim Tab As New CustomControl.STC_Tabx
With Tab
.Name = "Reports_BalanceSheetTab"
.Header = " Edit " & vName & " "
.CloseButtonVisibility = DevComponents.WpfEditors.eTabCloseButtonVisibility.Visible
.TabToolTip = "Edit " & vName
.ImageSource = vImageSource
.Content = ReportsBalanceSheetFrame
End With
AddHandler Tab.Closing, AddressOf TabControl_TabClosing
Dim vGrid As Grid = Application.Current.MainWindow.FindName("MainGrid_Website")
RegisterControl(vGrid, Tab, Tab.Name.ToString)
TabControl.Items.Add(Tab)
Dim BalanceSheet As New Reports_BalanceSheet_Page
ReportsBalanceSheetFrame.NavigationService.Navigate(BalanceSheet)
TabControl.SelectedItem = Tab
Else
vTab.Close()
Dim ReportsBalanceSheetFrame As New Frame
Dim Tab As New CustomControl.STC_Tabx
With Tab
.Name = "Reports_BalanceSheetTab"
.Header = " Edit " & vName & " "
.CloseButtonVisibility = DevComponents.WpfEditors.eTabCloseButtonVisibility.Visible
.TabToolTip = "Edit " & vName
.ImageSource = vImageSource
.Content = ReportsBalanceSheetFrame
End With
AddHandler Tab.Closing, AddressOf TabControl_TabClosing
Dim vGrid As Grid = Application.Current.MainWindow.FindName("MainGrid_Website")
RegisterControl(vGrid, Tab, Tab.Name.ToString)
TabControl.Items.Add(Tab)
Dim BalanceSheet As New Reports_BalanceSheet_Page
ReportsBalanceSheetFrame.NavigationService.Navigate(BalanceSheet)
TabControl.SelectedItem = Tab
End If
ElseIf DGV.SelectedItems.Count > 1 Then
AppBoxValidation("You can only select one item at a time to edit!")
Else
AppBoxValidation("You must select an item to edit!")
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Adding e.Handled resolved the issue
From the Page_load event of Window1 I'm calling a function of a public class and passing parameter as the same Window1. After the function is called, a thread is started. The thread is called on Page_Loaded event of Window1. The code is like this:
Private Sub StartScreen_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Try
TmHeartbeat.Stop()
TmHeartbeat.Interval = 600000
TmHeartbeat.Start()
ResetVariables()
FormLoadSetting(Me)
SetButtonProperty(btnEnglish, "\Images\ButtonBgBig.png", GetFormLabel("Start Screen", "Common", "Button English"))
SetButtonProperty(btnSpanish, "\Images\ButtonBgBig.png", GetFormLabel("Start Screen", "Common", "Button Spanish"))
SetDisplayTimer(DGT, False, Me, 1800000)
MediaElement1.Source = New Uri(GetPath("Images\EnglishVideo\" & GetFormLabel("Start Screen", "Common", "Video")))
If GetFormLabel("Start Screen", "Common", "Audio") <> "" Then
PlayAudio(APlay, GetFormLabel("Start Screen", "Common", "Audio"))
End If
Dim AudioPlay As New System.Media.SoundPlayer
Dim sc As New Screenshot
sc.TakeScreenshot(Me)
Catch ex As Exception
AliLogFileEntry(TransactionType.ErrorOnForm, "Error In Function: StartScreen_Loaded: " & Me.Title & ", ErrorMessage: " & ex.Message)
End Try
End Sub
The function TakeScreenshot(Me) which is in class Screenshot is called. Along with the Screenshot class, I also have another class named GetScreenshot and a function TakeScreenshot1. The code for the class file is like this:
Imports System.IO
Imports System.Threading
Public Class Screenshot
Public Sub TakeScreenshot(ByVal formname As Window)
Dim GT As New GetScreenshot
GT.source = formname
Dim newThread As New Thread(AddressOf GT.TakeScreenshot1)
newThread.Start()
End Sub
End Class
Public Class GetScreenshot
Public source As Window
Public Function TakeScreenshot1()
Thread.Sleep(2000)
If OG.GetValue("TakeScreenshot") <> "0" Then
Try
AliLogFileEntry(TransactionType.System, "In Function: TakeScreenshot")
Dim scale As Double = OG.GetValue("Screenshot_Scale") / 100
AliLogFileEntry(TransactionType.System, "In Function: GetJpgImage")
Dim renderHeight As Double = source.RenderSize.Height * scale
Dim renderWidth As Double = source.RenderSize.Width * scale
Dim renderTarget As New RenderTargetBitmap(CInt(Math.Truncate(renderWidth)), CInt(Math.Truncate(renderHeight)), 96, 96, PixelFormats.Pbgra32)
Dim sourceBrush As New VisualBrush(source)
Dim drawingVisual As New DrawingVisual()
Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()
Using drawingContext
drawingContext.PushTransform(New ScaleTransform(scale, scale))
drawingContext.DrawRectangle(sourceBrush, Nothing, New Rect(New Point(0, 0), New Point(source.RenderSize.Width, source.RenderSize.Height)))
End Using
renderTarget.Render(drawingVisual)
Dim jpgEncoder As New JpegBitmapEncoder()
jpgEncoder.QualityLevel = 100
jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget))
Dim _imageArray As [Byte]()
Using outputStream As New MemoryStream()
jpgEncoder.Save(outputStream)
_imageArray = outputStream.ToArray()
End Using
Dim screenshot As Byte() = _imageArray
Dim dir As DirectoryInfo = New DirectoryInfo("Screenshots")
If Not dir.Exists Then dir = Directory.CreateDirectory(dir.FullName)
Dim path As String = AppDomain.CurrentDomain.BaseDirectory
Dim fileStream As New IO.FileStream("Screenshots\" & source.Title & ".jpg", FileMode.Create, FileAccess.ReadWrite)
Dim binaryWriter As New IO.BinaryWriter(fileStream)
binaryWriter.Write(screenshot)
binaryWriter.Close()
Catch ex As Exception
AliLogFileEntry(TransactionType.ErrorOnForm, "Error In Function: TakeScreenshot , ErrorMessage: " & ex.Message)
End Try
End If
End Function
End Class
When I debug this file, I get this error:
And the error is generated on line
Dim sourceBrush As New VisualBrush(source)
Pleas help
You are using source from a different thread than it was created on. In order to use a windows control on a different thread you need to call back to the original thread.
See How to: Make Thread-Safe Calls to Windows Forms Controls for more.
I am inserting lists into a RichTextBox like this - but how do I get the Caret to move to the first list item?
Private Sub TextEditor_BulletListAdd(sender As Object, e As RoutedEventArgs)
Try
Dim vEditor As RichTextBox = TextEditorGrid.FindName("Controls_TextEditorRTF")
Dim vList As New List()
vList.MarkerStyle = TextMarkerStyle.Disc
Dim vRun As New Run()
Dim vItem As New ListItem(New Paragraph(vRun))
vList.ListItems.Add(vItem)
Dim curCaret = vEditor.CaretPosition
Dim curBlock = vEditor.Document.Blocks.Where(Function(x) x.ContentStart.CompareTo(curCaret) = -1 AndAlso x.ContentEnd.CompareTo(curCaret) = 1).FirstOrDefault()
vEditor.Document.Blocks.InsertAfter(curBlock, vList)
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Private Sub TextEditor_NumberListAdd(sender As Object, e As RoutedEventArgs)
Try
Dim vEditor As RichTextBox = TextEditorGrid.FindName("Controls_TextEditorRTF")
Dim vList As New List()
vList.MarkerStyle = TextMarkerStyle.Decimal
Dim vRun As New Run()
Dim vItem As New ListItem(New Paragraph(vRun))
vList.ListItems.Add(vItem)
Dim curCaret = vEditor.CaretPosition
Dim curBlock = vEditor.Document.Blocks.Where(Function(x) x.ContentStart.CompareTo(curCaret) = -1 AndAlso x.ContentEnd.CompareTo(curCaret) = 1).FirstOrDefault()
vEditor.Document.Blocks.InsertAfter(curBlock, vList)
Catch ex As Exception
EmailError(ex)
End Try
End Sub
The easy part is setting the position of the caret... the tricky part is finding the pointer of the place that you want to set it to (unless that is simply the beginning or end of the document):
RichTextBox rtb = new RichTextBox(flowDoc);
// Get the current caret position.
TextPointer caretPos = rtb.CaretPosition;
// Set the TextPointer to the end of the current document.
caretPos = caretPos.DocumentEnd; // <<< You need to find the correct position here
// Specify the new caret position at the end of the current document.
rtb.CaretPosition = caretPos;
From RichTextBox.CaretPosition Property on MSDN.
Turns out the answer was a lot simpler that I thought :-)
Dim vMove As TextPointer = curCaret.GetNextInsertionPosition(LogicalDirection.Forward)
If Not vMove Is Nothing Then
vEditor.CaretPosition = vMove
End If
Complete
Private Sub TextEditor_BulletListAdd(sender As Object, e As RoutedEventArgs)
Try
Dim vEditor As RichTextBox = TextEditorGrid.FindName("Controls_TextEditorRTF")
Dim vList As New List()
vList.MarkerStyle = TextMarkerStyle.Disc
Dim vRun As New Run()
Dim vItem As New ListItem(New Paragraph(vRun))
vList.ListItems.Add(vItem)
Dim curCaret = vEditor.CaretPosition
Dim curBlock = vEditor.Document.Blocks.Where(Function(x) x.ContentStart.CompareTo(curCaret) = -1 AndAlso x.ContentEnd.CompareTo(curCaret) = 1).FirstOrDefault()
vEditor.Document.Blocks.InsertAfter(curBlock, vList)
Dim vMove As TextPointer = curCaret.GetNextInsertionPosition(LogicalDirection.Forward)
If Not vMove Is Nothing Then
vEditor.CaretPosition = vMove
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub