How do I set text background color without changing cursor location? - winforms

I want to set the background color of a certain text range in a RichTextBox.
However, the only way to do that is by selecting it like that:
RichTextBox1.Select(10, 3) 'select text starting from position 10, use a length of 3
RichTextBox1.SelectionBackColor = Color.White
Using .Select puts the cursor at this location.
How do I achieve the same without changing the cursor location?
Solutions have been posted which just reset the cursor, but this does not help. I need a method would not set the cursor to a different location.

To preserve the previous caret position and selection too:
... Call to suspend drawing here
var start = richTextBox1.SelectionStart;
var len = richTextBox1.SelectionLength;
richTextBox1.Select(10, 3);
richTextBox1.SelectionBackColor = Color.White;
richTextBox1.SelectionStart = start;
richTextBox1.SelectionLength = len;
... Call to resume drawing here
To prevent flicking check the solution provided in this post: https://stackoverflow.com/a/487757/6630084
TextBoxBase.SelectionStart Property
TextBoxBase.SelectionLength Property

You can store the cursor position before setting the color, then restore the position as follows:
Public Sub New()
InitializeComponent()
richTextBox1.Text += "RichTextBox text line 1" & Environment.NewLine
richTextBox1.Text += "RichTextBox text line 2" & Environment.NewLine
richTextBox1.Text += "RichTextBox text line 3" & Environment.NewLine
Dim i As Integer = richTextBox1.SelectionStart
Dim j As Integer = richTextBox1.SelectionLength
richTextBox1.Select(10, 3)
richTextBox1.SelectionBackColor = Color.White
richTextBox1.SelectionStart = i
richTextBox1.SelectionLength = j 'use this to preserve selection length, or
richTextBox1.SelectionLength = 0 'use this to clear the selection
End Sub

Related

How to take variables entered in the userform text box to the array text in the module using VBA in PowerPoint?

I have a macro to select slides, with required text, to move to a new presentation.
I have to extract 70-80 slides from a 500+ slides presentation. But I need to enter VB/Module to change the keywords/search text in the array. Is there a way I can move the text entered in the userform to the array (text)?
Userform to enter the keywords.
How do I link the text entered with the array list in the code?
Sub selct()
Dim pres1 As PowerPoint.Presentation, pres2 As PowerPoint.Presentation,
pp As Object
Set pp = GetObject(, "PowerPoint.Application")
Set pres1 = pp.ActivePresentation
Set pres2 = pp.Presentations.Add
Dim i As Long, n As Long
Dim TargetList
'~~> Array of terms to search for
TargetList = Array("Agenda", "Review", "third", "etc")
'~~> Loop through each slide
For Each sld In pres1.Slides
'~~> Loop through each shape
For Each shp In sld.Shapes
'~~> Check if it has text
If shp.HasTextFrame Then
Set txtRng = shp.TextFrame.TextRange
For i = 0 To UBound(TargetList)
'~~> Find the text
Set rngFound = txtRng.Find(TargetList(i))
'~~~> If found
Do While Not rngFound Is Nothing
'~~> Set the marker so that the next find starts from here
n = rngFound.Start + 1
'~~> Chnage attributes
With rngFound.Font
.Bold = msoFalse
sld.Copy
pres2.Slides.Paste
'~~> Find Next instance
Set rngFound = txtRng.Find(TargetList(i), n)
End With
Loop
Next
End If
Next
Next
End Sub
The form objects are accessible even when the form is not shown, like this: Suppose you have a form with name UF1 with a textbox named TBforKeyWord, then you can access the textbox value at UF1.TBforKeyWord, so you might
Redim Preserve TargetList(Ubound(TargetList) + 1)
TargetList(Ubound(TargetList) = UF1.TBforKeyWord
The logic is the same if you let the user enter multiple keywords but then you need to work a bit more on splitting (and parsing) the keywords.
EDIT
Dim text_array() As String
text_array = Split(SearchBox.Value, " ")
Dim iDimOld As Long
Dim iDimNew As Long
Dim i As Long
iDimOld = Ubound(TargetList)
iDimNew = iDimOld + Ubound(text_array) + 1
Redim Preserve TargetList(iDimNew)
' Loop through each keyword in array
For i = 0 To Ubound(text_array)
TargetList(iDimOld + i + 1) = text_array(i)
Next

Update Textbox after each iteration VB.Net WPF

I have a For loop which runs through xlsx files in a directory, I need to append the filenames in a TextBlock after each loop and refresh the TextBlock to show the updated text.
The code I have below only displays the filenames after the loop has executed.
Dim lcFileName As String = ""
Dim fileArray() As String = Directory.GetFiles(txtDirectory.Text, "*.xlsx", SearchOption.AllDirectories)
For Each file As String In fileArray
Dim ExcelApp As Excel.Application = New Excel.Application
Dim Workbook As Excel.Workbook = ExcelApp.Workbooks.Open(file)
Dim Worksheet As Excel.Worksheet = Workbook.Sheets(1)
Dim Range As Excel.Range = Worksheet.UsedRange
Dim rowCount As Integer = Range.Rows.Count
Dim colCount As Integer = Range.Columns.Count
Dim tmpOrder(rowCount, colCount) As String
tbResults.Text = tbResults.Text + Environment.NewLine + Path.GetFileName(file) + " imported."
For i = 1 To rowCount
For j = 1 To colCount
'New line
If (i = 1 And j = 1) Then
tmpOrder(i - 1, j - 1) = Range.Cells(i, j).Value
lcFileName = tmpOrder(i - 1, j - 1).ToString()
Else
If (Not String.IsNullOrEmpty(Range.Cells(i, j).Value)) Then
tmpOrder(i - 1, j - 1) = Range.Cells(i, j).Value.ToString()
End If
End If
Next
Next
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(Worksheet)
Worksheet = Nothing
ExcelApp.ActiveWorkbook.Close(True)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(Workbook)
Workbook = Nothing
ExcelApp.Quit()
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(ExcelApp)
ExcelApp = Nothing
'
Next
Any help would be appreciated, VB.Net required.
Finally got it working.
Ok first I created my Method to update the TextBlock with the parameter passed for the filename.
Public Sub UpdateResults(ByVal lcFile As String)
tbResults.Text = tbResults.Text + Environment.NewLine + Path.GetFileName(lcFile) + " imported."
End Sub
In my For Loop i called the method with the following code
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, New ThreadStart(Sub() Me.UpdateResults(lcFile)))
Where UpdateResults(lcFile) is the method and parameter passed.
If you are not passing any parameters then call your method using this, where 'MyMethod' is the name of the method you want to run.
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, New ThreadStart(AddressOf MyMethod))
Something isn't playing nicely and is preventing the form from updating.
You can allow the app to process pending display operations by adding Application.DoEvents() inside one of the loops.
There is some overhead involved, so you probably want it in the outer loop, not the inner loop.

Excel VBA - Creating a dynamic userform with multiple combo boxes and storing the values of all combo boxes in one array and sorting through it

I am creating a userform on excel 2007 that has a 6x6 arrangement of combo boxes. the last row and last column are the 'all up' boxes that should have a value based on their respective rows/columns. the remaining 25 (5x5) combo boxes have 3 values (Red, Amber, Green), and when a user selects a value, the combo box displays the value and the background is coloured with the value selected (I did this by creating a function in a module and calling it within each combobox_change()).
I am having trouble coding the last row and column boxes. Basically, if say for Row 1, there is even a single 'Red', the last box on row 1 (1,6) should automatically turn red. If there is no red, but there is an 'amber', the last box should turn 'amber'. If there is a 'red' and an 'amber', 'red' should be given the priority. Similar logic for columns as well.
What I have tried so far:
Within the userform code:
Private Sub Txt_Score_1_1_Change() 'This is for row 1 column 1 on the matrix'
Call ScoreChange.ScoreChange("Txt_Score_1_1")
Within a module:
Public Sub ScoreChange(ctrlName As String)
If Scorecard.Controls(ctrlName).Value = "R" Then
Scorecard.Controls(ctrlName).BackColor = vbRed
ElseIf Scorecard.Controls(ctrlName).Value = "G" Then
Scorecard.Controls(ctrlName).BackColor = vbGreen
ElseIf Scorecard.Controls(ctrlName).Value = "A" Then
Scorecard.Controls(ctrlName).BackColor = vbYellow
Else
Scorecard.Controls(ctrlName).BackColor = vbWhite
End If
For i = 1 To 5
For j = 1 To 5
If Scorecard.Controls("Txt_Score_" & i & "_" & j).Value <> "" Then
If Scorecard.Controls("Txt_Score_" & i & "_" & j).Value = "R" Then
Scorecard.Controls("Txt_Score_" & i & "_6").Value = "R"
Scorecard.Controls("Txt_Score_6_" & j).Value = "R"
ElseIf Scorecard.Controls("Txt_Score_" & i & "_" & j).Value = "A" Then
Scorecard.Controls("Txt_Score_" & i & "_6").Value = "A"
Scorecard.Controls("Txt_Score_6_" & j).Value = "A"
End If
End If
Next j
Next i
End Sub
The above works to change the individual colours of the combo boxes when changed but falls apart for the 'total'/'all up' boxes.
What I think needs to be done to achieve the above is that I need to write a code that recognises when all the combo boxes for a specific row/column have been filled, and then stores those values in an array, and recognises within the array, the value for the last box.
Any help on how to achieve this will be appreciated.
Also, apologies if something similar has been posted elsewhere, but I did a lot of research and couldn't find anything.
Thanks.
I think there might be a simpler way of attacking this task, and certainly an easier way of consuming all the ComboBox_Change events.
If I understand your question correctly, you are saying that you have a matrix of 5 by 5 'child' ComboBoxes. You then have 5 'parent' controls that change based on the selection of the rows' children, and 5 'parent controls' that do the same for the columns' children.
What you could do, therefore, is create two classes. I've called them clsChild and clsParent. The child class traps the change event and then notifies the row and column parents that a change has occured. The parent class contains a list of its children and runs the colouring rules based on the children's selection.
In terms of the rules, I've created an Enum of your colours where Red is lowest and White is the highest, so you simply take the lowest 'score' of any of the children to colour the parent control.
I've kept the same naming conventions as your post for the ComboBoxes but I don't see why the 'parent' controls are Comboboxes - surely you wouldn't want a user to be able to change them? I've taken the liberty then of making them Labels with the naming convention Lbl_Score_R1 ... R5 for rows and Lbl_Score_C1 ... C5 for columns.
The beauty of this method is that you only need to tie up the relationships between children and parents once and simply pass the control objects between them. This will avoid having to do your awkward string manipulation every time a change event occurs.
So, the code...
i. Insert a new class and call it clsChild. Add the following code:
Option Explicit
Private WithEvents mCtrl As MSForms.ComboBox
Private mMum As clsParent
Private mDad As clsParent
Private mLight As Lights
Public Property Set Mum(val As clsParent)
Set mMum = val
Set mMum.ChildInLine = Me
End Property
Public Property Set Dad(val As clsParent)
Set mDad = val
Set mDad.ChildInLine = Me
End Property
Public Property Set Ctrl(val As MSForms.ComboBox)
Set mCtrl = val
With mCtrl
.List = Array("R", "A", "G", "W")
.ListIndex = 3
End With
End Property
Public Property Get Light() As Lights
Light = mLight
End Property
Private Property Let Light(val As Lights)
mLight = val
With mCtrl
Select Case mLight
Case Lights.Red: .BackColor = vbRed
Case Lights.Amber: .BackColor = vbYellow
Case Lights.Green: .BackColor = vbGreen
Case Lights.White: .BackColor = vbWhite
End Select
End With
If Not mMum Is Nothing Then mMum.ConsumeChildChanged
If Not mDad Is Nothing Then mDad.ConsumeChildChanged
End Property
Private Sub mCtrl_Change()
Select Case mCtrl.Value
Case Is = "R": Light = Red
Case Is = "A": Light = Amber
Case Is = "G": Light = Green
Case Else: Light = White
End Select
End Sub
ii. Insert another new class and call it clsParent and add the following code:
Option Explicit
Private mCtrl As MSForms.Label
Private mChildren As Collection
Private mLight As Lights
Public Property Set Ctrl(val As MSForms.Label)
Set mCtrl = val
Set mChildren = New Collection
End Property
Public Property Set ChildInLine(val As clsChild)
mChildren.Add val
End Property
Public Sub ConsumeChildChanged()
Dim lowest As Lights
Dim oChild As clsChild
lowest = White
For Each oChild In mChildren
With oChild
If .Light < lowest Then
lowest = .Light
End If
End With
Next
Light = lowest
End Sub
Private Property Get Light() As Lights
Light = mLight
End Property
Private Property Let Light(val As Lights)
mLight = val
With mCtrl
Select Case mLight
Case Lights.Red: .BackColor = vbRed
Case Lights.Amber: .BackColor = vbYellow
Case Lights.Green: .BackColor = vbGreen
Case Else: .BackColor = vbWhite
End Select
End With
End Property
iii. At the top of any Module add the following:
Public Enum Lights
Red
Amber
Green
White
End Enum
iv. And finally add the following to your UserForm code:
Option Explicit
Private mMum(1 To 5) As clsParent
Private mDad(1 To 5) As clsParent
Private mChild(1 To 5, 1 To 5) As clsChild
Private Sub UserForm_Initialize()
Dim i As Integer, j As Integer
For i = 1 To 5
Set mMum(i) = New clsParent
Set mMum(i).Ctrl = Me.Controls("Lbl_Score_R" & i)
Set mDad(i) = New clsParent
Set mDad(i).Ctrl = Me.Controls("Lbl_Score_C" & i)
Next
For i = 1 To 5
For j = 1 To 5
Set mChild(i, j) = New clsChild
With mChild(i, j)
Set .Ctrl = Me.Controls("Txt_Score_" & i & "_" & j)
Set .Mum = mMum(i)
Set .Dad = mDad(j)
End With
Next
Next
End Sub

Datagridviewcell Tooltip doesn't display after modifying image of any cell

In my winforms application, I loop through the cells of a datagridview and add a tooltip for specific cells based on values in other columns. I do this in the cellformatting event handler, and it worked perfectly. Here is the code:
Private Sub dgvResults_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles dgvResults.CellFormatting
Select Case dgvResults.Columns(e.ColumnIndex).Name
Case "TradeInValue"
DirectCast(dgvResults.Rows(e.RowIndex).Cells("TradeInValue"), DataGridViewTextBoxCell).ToolTipText = "Min = " & CDec(dgvResults.Item("BB_Min", e.RowIndex).FormattedValue).ToString("$#####.##") & ", Max = " & CDec(dgvResults.Item("BB_Max", e.RowIndex).FormattedValue).ToString("$#####.##")
If Not IsNothing(dgvResults.Item("SelectedTrimIndex", e.RowIndex).FormattedValue) AndAlso dgvResults.Item("SelectedTrimIndex", e.RowIndex).FormattedValue.ToString.Trim.Length > 0 AndAlso CInt(dgvResults.Item("SelectedTrimIndex", e.RowIndex).FormattedValue.ToString) <> -1 Then
If dgvResults.Item("ValueList", e.RowIndex).FormattedValue.ToString.Length > 0 Then
Dim ValueParts() As String = dgvResults.Item("ValueList", e.RowIndex).FormattedValue.ToString.Split("|")
'Dim selectedTrim As String = ValueParts(dgvResults.Item("SelectedTrimIndex", e.RowIndex).FormattedValue)
End If
End If
End Select
End Sub
Then, I added code in the cellpainting event handler to hide specific images, again based on values in the datagridview. Here is that code.
Private Sub dgvResults_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgvResults.CellPainting
If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
Select Case dgvResults.Columns(e.ColumnIndex).Name
Case "VIN_Pic"
If dgvResults.Rows(e.RowIndex).Cells("VIN_Value").FormattedValue = "" Then
DirectCast(dgvResults.Item(e.ColumnIndex, e.RowIndex), DataGridViewImageCell).Value = New Bitmap(1, 1)
End If
Case "EmailDisplayImage"
If dgvResults.Rows(e.RowIndex).Cells("ListingContactEmail").FormattedValue = "" Then
DirectCast(dgvResults.Item(e.ColumnIndex, e.RowIndex), DataGridViewImageCell).Value = New Bitmap(1, 1)
End If
End Select
End If
End Sub
When this code as added, the tooltips no longer display. The CellToolTipTextNeeded event fires, and it shows the correct text in the argument, but it never displays. Comment out the lines that assign a new image to the datagridviewimagecells, and the tooltips start displaying again.
I hope this explanation was sufficient. Any ideas?
With the below code, you will run into memory and GDI object leaks very easily as a new Bitmap instance is created every time the cell is painted. Also this is causing your ToolTip to be not displayed.
DirectCast(dgvResults.Item(e.ColumnIndex, e.RowIndex), DataGridViewImageCell).Value = New Bitmap(1, 1)
Define the empty bitmap in class level or add as embedded resource and use it.
Dim emptyBitmap As New Bitmap(1, 1);
DirectCast(dgvResults.Item(e.ColumnIndex, e.RowIndex), DataGridViewImageCell).Value = emptyBitmap

Array of dynamic texboxes

In the form, everytime I click te button, a new textbox has to appear.
I want to make an Array with all of these textboxes.
The problem is, that al these textboxes have a dynamic name.
How do I have to put them in the Array?
Here's my code:
Set nieuwtxtingredient = Me.Controls.Add("Forms.Textbox.1", "Ingredient", True)
With nieuwtxtingredient
.Width = Me.txtIngredient0.Width
.Height = Me.txtIngredient0.Height
.Left = Me.txtIngredient0.Left
.Top = Me.txtIngredient0.Top + 30 * aantalBoxes
.Name = "txtIngredient" + CStr(aantalBoxes)
End With
Dim naam As String
Dim ingredientArray() As String
ReDim ingredientArray(1 To aantalBoxes)
ingredientArray(aantalBoxes) = **Me.txtIngredient0.Value**
In your code, you save a reference to your new textbox in nieuwtxtingredient.
You could save this reference in an array of Textbox and later read the name and value of each one.
This is how I suggest to revise your code :
Dim aantalBoxes As Integer
Dim ingredientArray() As Control
Private Sub btnVoegToe_Click()
Dim aantalRecepten As Integer
Dim Teller As Integer
aantalRecepten = Cells(2, Columns.Count).End(xlToLeft).Column
Cells(2, aantalRecepten + 2).Value = Me.txtNaamRecept.Value
For Teller = 1 To aantalBoxes ' <-- starts at 1, formula below adjusted
Cells(2 + Teller, aantalRecepten + 2).Value = ingredientArray(aantalBoxes).Value
Next Teller
End Sub
Private Sub btnVolgendeIngredient_Click()
aantalBoxes=aantalBoxes + 1 ' <-- must calculate the total
ReDim Preserve ingredientArray(aantalBoxes) ' <-- you had this in the earlier version
Set nieuwtxtingredient = Me.Controls.Add("Forms.Textbox.1", "Ingredient", True)
With nieuwtxtingredient
.Width = Me.txtIngredient0.Width
.Height = Me.txtIngredient0.Height
.Left = Me.txtIngredient0.Left
.Top = Me.txtIngredient0.Top + 30 * aantalBoxes
.Name = "txtIngredient" + CStr(aantalBoxes)
End With
' first Textbox is numbered 1
set ingredientArray(aantalBoxes) = nieuwtxtingredient ' <-- you had this in the earlier version
End Sub
See this example, http://jsfiddle.net/7zkzttpr/2/
$(document).ready(function() {
var max_fields = 10; //maximum input boxes allowed
var wrapper = $(".input_fields_wrap"); //Fields wrapper
var add_button = $(".add_field_button"); //Add button ID
var x = 1; //initlal text box count
$(add_button).click(function(e){ //on add input button click
e.preventDefault();
if(x < max_fields){ //max input box allowed
x++; //text box increment
$(wrapper).append('<div><input type="text" name="mytext[]"/>Remove</div>'); //add input box
}
});
$(wrapper).on("click",".remove_field", function(e){ //user click on remove text
e.preventDefault(); $(this).parent('div').remove(); x--;
})
});
When ever you create a new textbox, give the same name to the textbox as an array so that you can get the values of all textboxes in an array.

Resources