Displaying ToolTip to MouseHover Text - c#

Related to this question:
Displaying tooltip on mouse hover of a text
How can I do the same thing on a keyword in the RichTextBox but not a link?
In RichTextBox I type:
Hello World, How are You?
I set hovering the word "World" for tooltip and when it hovers ToolTip will appear saying "this is world"!
But if it hovers out of the RichTextBox or in the word "World" itself, tooltip will disappear.

It's a little simple. You have to use the GetCharIndexFromPosition method of your RichTextBox to get the Char index under the Mouse Pointer, make some simple loops to find the whole word and then show it in a Tooltip popup normally. Here is the code:
string punctuations = " ,.;!?'\")]}\n";
//This saves your words with their corresponding definitions/details
Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
ToolTip tt = new ToolTip();
int k;
int lineBreakIndex = 60;
int textHeight;
//MouseMove event handler for your richTextBox1
private void richTextBox1_MouseMove(object sender, MouseEventArgs e){
if (richTextBox1.TextLength == 0) return;
Point lastCharPoint = richTextBox1.GetPositionFromCharIndex(richTextBox1.TextLength - 1);
if (e.Y > textHeight || (e.Y >= lastCharPoint.Y && e.X > lastCharPoint.X + textHeight - lastCharPoint.Y))
{
tt.Hide(richTextBox1);
k = -1;
return;
}
int i = richTextBox1.GetCharIndexFromPosition(e.Location);
int m = i, n = i;
while (m>-1&&!punctuations.Contains(richTextBox1.Text[m])) m--;
m++;
while (n<richTextBox1.TextLength&&!punctuations.Contains(richTextBox1.Text[n])) n++;
if (n > m){
string word = richTextBox1.Text.Substring(m, n - m);
if (dict.ContainsKey(word)){
if (k != m){
tt.ToolTipTitle = word;
tt.Show(dict[word], richTextBox1, e.X, e.Y + 10);
k = m;
}
}
else{
tt.Hide(richTextBox1);
k = -1;
}
}
}
//This will get the entry text with lines broken.
private string GetEntryText(string key){
string s = dict[key];
int lastLineEnd = lineBreakIndex;
for (int i = lastLineEnd; i < s.Length; i += lineBreakIndex)
{
while (s[i] != ' '){
if (--i < 0) break;
}
i++;
s = s.Insert(i, "\n");
lastLineEnd = i+1;
}
return s;
}
//MouseLeave event handler for your richTextBox1
private void richTextBox1_MouseLeave(object sender, EventArgs e){
tt.Hide(richTextBox1);
k = -1;
}
//ContentsResized event handler for your richTextBox1
private void richTextBox1_ContentsResized(object sender, ContentsResizedEventArgs e)
{
textHeight = e.NewRectangle.Height;
}
//Here are some sample words with definitions:
dict.Add("world", "- World is a common name for the whole of human civilization, specifically human experience, history, or the human condition in general, worldwide, i.e. anywhere on Earth.");
dict.Add("geek", "- A person who is single-minded or accomplished in scientific or technical pursuits but is felt to be socially inept");

Related

how to determine the mouse pointer is on tablelayoutpanel cell border c#

I have TableLayoutPanel on windows form. I want mouse pointer cursor style is cross when the pointer on/near the cell border.
Edit I tried with mouse move event. I get the cell positions where the mouse point is moving.But I couldn't use this information and I was stuck. How can achieve that?
Edit: I fixed the problem. It is about size type. The code is working. I'm sharing it for those who have similar demands. Thanx.
bool calcCells = false;
List<float> XCoordinates = new List<float>();
List<float> YCoordinates = new List<float>();
public Form3()
{
InitializeComponent();
// Set the DoubleBuffered property via reflection (if needed)
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
tlp1.GetType().GetProperty("DoubleBuffered", flags).SetValue(tlp1, true);
tlp1.Layout += tlp1_Layout;
tlp1.CellPaint += tlp1_CellPaint;
tlp1.MouseMove += tlp1_MouseMove;
}
// Added the x coordinates of cell borders in a List
private void CreateXCoordinateList()
{
XCoordinates.Clear();
// first and last column sizetype is SizeType.Absoulute.
float tlpWidth = tlp1.Width- tlp1.ColumnStyles[0].Width - tlp1.ColumnStyles[tlp1.ColumnCount-1].Width;
float x = 0;
for (int i = 0; i < tlp1.ColumnCount; i++)
{
if(tlp1.ColumnStyles[i].SizeType==SizeType.Absolute)
x += tlp1.ColumnStyles[i].Width;
else if(tlp1.ColumnStyles[i].SizeType == SizeType.Percent)
{
double k = tlpWidth * tlp1.ColumnStyles[i].Width * 0.01;
x += Convert.ToSingle(k);
}
XCoordinates.Add(x);
}
}
// Added the y coordinates of cell borders in a List
private void CreateYCoordinateList()
{
YCoordinates.Clear();
// first and last row sizetype is SizeType.Absoulute.
float tlpHeight = tlp1.Height - tlp1.RowStyles[0].Height - tlp1.RowStyles[tlp1.RowCount - 1].Height;
float y = 0;
for (int i = 0; i < tlp1.RowCount; i++)
{
if (tlp1.RowStyles[i].SizeType == SizeType.Absolute)
y += tlp1.RowStyles[i].Height;
else if (tlp1.RowStyles[i].SizeType == SizeType.Percent)
{
double k = tlpHeight * tlp1.RowStyles[i].Height*0.01;
y += Convert.ToSingle(k);
}
YCoordinates.Add(y);
}
}
private void tlp1_Layout(object sender, LayoutEventArgs e) => calcCells = true;
private void tlp1_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
{
if (calcCells)
{
CreateXCoordinateList();
CreateYCoordinateList();
if (e.Column == tlp1.ColumnCount - 1 &&
e.Row == tlp1.RowCount - 1)
calcCells = false;
}
}
private void tlp1_MouseMove(object sender, MouseEventArgs e)
{
//Comparing the mouse pointer position with the cellborder coordinates,
//if the difference is less than and equal to 4, change the cursor style.
float x = e.Location.X;
float y = e.Location.Y;
if (MouseNearCellBorderXAxis(e) || MouseNearCellBorderYAxis(e))
tlp1.Cursor = Cursors.Cross;
else
tlp1.Cursor = Cursors.Default;
}
private bool MouseNearCellBorderXAxis(MouseEventArgs e)
{
float x = e.Location.X;
for (int i = 0; i < XCoordinates.Count; i++)
{
float Border = XCoordinates[i];
double difference = Math.Abs(x - Border);
if (difference <= 4)
return true;
}
return false;
}
private bool MouseNearCellBorderYAxis(MouseEventArgs e)
{
float y = e.Location.Y;
for (int i = 0; i < YCoordinates.Count; i++)
{
float Border = YCoordinates[i];
double difference = Math.Abs(y - Border);
if (difference <= 4)
return true;
}
return false;
}
If I get what you're asking, provided you have controls in the cells of the TableLayoutPanel all one would have to do is set the different cursors one time for:
Main form (Arrow)
Table layout panel (Cross)
The controls therein contained (e.g. Hand)
Everything else should happen on its own.
public MainForm()
{
InitializeComponent();
// MainForm has ARROW
this.Cursor = Cursors.Arrow;
// TableLayoutPanel has CROSS
tableLayoutPanel.Cursor = Cursors.Cross;
int key = 0; string text;
for (int column = 0; column < tableLayoutPanel.ColumnCount; column++)
for (int row = 0; row < tableLayoutPanel.RowCount; row++)
{
switch (++key)
{
case 10: text = "*"; break;
case 11: text = "0"; break;
case 12: text = "#"; break;
default: text = $"{key}"; break;
}
tableLayoutPanel.Controls.Add(new Label
{
BackColor = Color.LightGreen,
Anchor = (AnchorStyles)0xF,
Margin = new Padding(10),
Text = text,
TextAlign = ContentAlignment.MiddleCenter,
// Controls in the table have HAND
Cursor = Cursors.Hand,
});
}
}

RichTextBox text color

I created RichTextBox and I add this code:
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
//( )
int selectionStart = richTextBox1.SelectionStart;
string helpText = richTextBox1.Text;
int closerPos;
for (int i = 0; i < helpText.Length; i++)
{
if (helpText[i] == '(')
{
selectionStart = richTextBox1.SelectionStart;
closerPos = helpText.Substring(i).IndexOf(')') + i;
helpText = helpText.Substring(i + 1, closerPos - i - 1);
richTextBox1.Text = richTextBox1.Text.Remove(i + 1, closerPos - i - 1);
richTextBox1.Select(i + 1, 0);
richTextBox1.SelectionColor = Color.Red;
richTextBox1.SelectedText = helpText;
richTextBox1.SelectionColor = Color.Black;
helpText = richTextBox1.Text;
richTextBox1.SelectionStart = selectionStart;
}
}
}
This code should color the text between the ( ).
For example:
"Hi (need to be colored) text (sdadsasd) "
the text between the ( ) need to be colored in red. but only the last text is colored. How can I fix it?
You're only getting the first paranthesis with that if condition.
Try splitting the text like helpText.Split('(');
Then iterate over it and do your logic.
I think you can accomplish this without moving byte by byte. You can try to do it using the IndexOf method.
This is something I was thinking of:
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
string rbText = richTextBox1.Text;
int position = 0;
int startBrace = rbText.IndexOf('(', position) + 1;
while (startBrace != -1)
{
position = rbText.IndexOf(')', startBrace);
if (position != -1)
{
richTextBox1.Select(startBrace, position - startBrace);
richTextBox1.SelectionColor = Color.Red;
startBrace = rbText.IndexOf('(', position) + 1;
}
else
break;
}
}
Keep in mind that I haven't fully test this code.
I already see a potential issue, helpText is being used as an array when it is only a single-variable string. Try breaking the entirety of helpText down into a char array, then iterating through that to find your brackets.

checking a color of a button and changing it using a 2D array

I'm creating a small game just like the game Reversi/Othello I have managed to created a 2x3 board with buttons.
The buttons change colour ones you click on them but I'm having trouble to detect if there is a white colour in between 2 black colours and if so change that white colour into black.. I hope this make sense. the buttons are in a 2D array. Any suggestions that could help me do this would be much appreciated.
The image:
Here is my code:
![namespace reversitest
{
public partial class Form1 : Form
{
private Button\[,\] squares;
public Form1()
{
InitializeComponent();
squares = new Button\[3, 2\];
squares = new Button\[,\] {{button1, button2, button3},
{button4, button5, button6,}};
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (Button sqrr in squares)
{
sqrr.Click += new System.EventHandler(this.DrawCharacter);
}
}
int _turn = 0;
private void DrawCharacter(object sender, EventArgs e)
{
Button sqrr = (Button)sender;
int col = 0;
if (sqrr.BackColor.Equals(Color.Black) || sqrr.BackColor.Equals(Color.White))
{
MessageBox.Show("Move Not Allowed!");
}
else
{
for ( int i = 0; i < squares.GetLongLength(1); ++i)
{
// check othere squares and change color
if (i < 2)
{
for (int f = 0; f < 3; ++f)
{
var ss = squares\[i, f\];
if (ss.BackColor.Equals(Color.Black))
{
MessageBox.Show("we have a black");
//ss = squares\[i, f+1\];
ss.BackColor = Color.Black;
}
else
{
MessageBox.Show("no black");
}
}
}
if (_turn == 0)
{
_turn = 1;
sqrr.BackColor = Color.Black;
}
else
{
_turn = 0;
sqrr.BackColor = Color.White;
}
}
}
}
}
}
First name your buttons with the array index. It will help you to find the button.
For example according to you picture button1 name would be btn_1_1.
Then inside your button click event first get the button name and then identify the button positioned.
Button b = sender as Button;
string[] btnData = b.Name.Split('_');
int x = int.Parse(btnData[1]);
int y = int.Parse(btnData[2]);
//check for possible combinations
int top = y - 2;
int botton = y + 2;
int left = x - 2;
int right = x + 2;
if (top >= 0 && squares[top, y].Background == Color.Black)
{
squares[top+1, y].Background = Color.Black;
}
...
...
Continue like that. If you need more detail please free to ask.
Final Answer
//check for possible combinations
int top = x - 2;
int botton = x + 2;
int left = y - 2;
int right = y + 2;
if (top >= 0 && squares[top, y].BackColor == Color.Black)
{
squares[top + 1, y].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
Will be extended for a 8x8 board later on
Do you need it to be elegant? A kind of brute force method: You could check for pieces in the 8 different directions it is possible for them to be aligned. So for example, you start with a black piece. Check the next piece over in one direction. If it's white, keep going and take a note of the position that was white so you can change it to black later. When you finally hit a black piece, change all the stored positions to black and move on to the next direction and repeat the process until you've done all 8 directions.

2 in a row game [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
c# using 2d arrays for buttons
I'm working on a game using a 2x2 board that will be extended to a 7x6.
I'm doing the winning detection at the moment, but I think I'm doing it the long way. There must be a much shorter way.
The winn
Horizontally
Vertically
Diagonally
Here's a pic of game board:
This is how I'm currently detecting winner
if (btns[0, col].BackColor.Equals(Color.Red) && btns[1, col].BackColor.Equals(Color.Red))
{
MessageBox.Show("Red Win");
}
if (btns[0, col].BackColor.Equals(Color.Blue) && btns[1, col].BackColor.Equals(Color.Blue))
{
MessageBox.Show("Blue Win");
}
This way seems like I have to list all combinations, and it would not be very ideal when I extend to 7x6.
Here is the whole code of the program
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Button[,] btns;
public Form1()
{
InitializeComponent();
btns = new Button[,] { { button2 , button1 },
{ button4 , button3 }};
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (var btn in btns)
{
btn.Enabled = false;
}
}
int cc = 0;
private void button5_Click(object sender, EventArgs e)
{
// Button[] row1 = new Button[] {button2, button1};
for (int col = 0; col < btns.GetLength(1); ++col)
{
var btn = btns[0, col];
if (!btn.Enabled)
{
btn.Enabled = true;
if (cc == 0)
{
cc = 1;
btn.BackColor = Color.Red;
}
else
{
cc = 0;
btn.BackColor = Color.Blue;
}
if (btns[0, col].BackColor.Equals(Color.Red) && btns[1, col].BackColor.Equals(Color.Red))
{
MessageBox.Show("Red Win");
}
if (btns[0, col].BackColor.Equals(Color.Blue) && btns[1, col].BackColor.Equals(Color.Blue))
{
MessageBox.Show("Blue Win");
}
return;
}
}
}
private void button6_Click(object sender, EventArgs e)
{
// Button[] row2 = new Button[] { button4, button3 };
for (int col = 0; col < btns.GetLength(1); ++col)
{
var btn = btns[1, col];
if (!btn.Enabled)
{
btn.Enabled = true;
if (cc == 0)
{
cc = 1;
btn.BackColor = Color.Red;
}
else
{
cc = 0;
btn.BackColor = Color.Blue;
}
if (btns[1, col].BackColor.Equals(Color.Red) && btns[0, col].BackColor.Equals(Color.Red))
{
MessageBox.Show("Red Win");
}
if (btns[1, col].BackColor.Equals(Color.Blue) && btns[0, col].BackColor.Equals(Color.Blue))
{
MessageBox.Show("Blue Win");
}
return;
}
}
}
}
}
I have tried a lot of other ways but I can't seem to get it working.
Maybe this answer is complicated and I'll get many downvotes but I couldn't resist solving this as optimal as possible. Try to inspect this code to the detail:
int n; //dimension of the matrix
Button [,] btns;
public Form1()
{
InitializeComponent();
n = 2;/*You should set here the dimension of your matix. I considered it nxn because of diagonals. If you want nxm matrix than the code is a little bit complicated but not too much*/
btns = new Button[n, n];
for(int i = 0;i<n;i++)
for(int j = 0; j<n; j++)
{
Button btn = new Button();
btn.Location = new Point(i*20,j*40);
btn.Size = new Size(18,38);
btns[i,j] = btn;
this.Controls.Add(btn);
}
}
private void button1_Click(object sender, EventArgs e)
{
int mainDiag = 0;
int secDiag = 0;
int i = 0;
int j = 0;
int [] cols = new int[n];
int winner = 0; //no winner
while(winner == 0 && i<n)
{
int row = 0;
j = 0;
while(j<n)
{
if (btns[i, j].BackColor == Color.Blue)
{
if (i == j)
mainDiag++;//inrement main diagonal
if(i + j == n-1)
secDiag++;//increment second diagonal
row++; //increment row
cols[i]++; //increment column
}
else if (btns[i, j].BackColor == Color.Red)
{
if (i == j)
mainDiag--;
if(i + j == n-1)
secDiag--;
row--;
cols[i]++;
}
j++;
}
if(row == n) //if row value == n whole row is blue and blue player wins
winner = 1;
else if(row == -n)
winner = -1; //if row value == -n whole row is red and red player wins
i++;
}
if(winner == 0)
{
if(mainDiag == n)
winner = 1; //similar for the diagonal
else if(mainDiag == -n)
winner = -1;
else if(secDiag == n)
winner = 1;//similar for the second diagonal
else if(secDiag == -n)
winner = -1;
else
{
i = 0;
while (winner == 0 && i < n)
{
if (cols[i] == n)
winner = 1; //i-th column is whole blue and blue player wins
else if (cols[i] == -n)
winner = -1; //i-th column is whole red and red player wins
}
}
}
if (winner == 1)
MessageBox.Show("Blue wins");
else if(winner == -1)
MessageBox.Show("Red wins");
}
As people have said in the comments, you'll probably want to use loops in order to do this.
I'm not going to go into a lot of detail here, but I'll describe some algorithms to accomplish your win condition detection.
Algorithm 1:
For Each cell, detect whether that's part of a horizontal, vertical, or diagonal row. The time complexity for this will be O(n^(3/2)). You can account for duplicate checks if you want to but you don't have to.
Algorithm 2:
Check All vertical rows, all horizontal rows, and all diagonal rows, and see if any win conditions lie upon those rows. The time complexity for this method should be roughly O(n). You could probably save a little bit of time by not checking diagonal rows that aren't large enough to hold a win condition, but you don't have to.
If you wanted a really basic answer I would consider the following
Add each row into an array
Add each column into an array
For Each item in the array if they all equal a single colour then you have won
You may want to use a multi-dim array when you go to larger numbers (ie 6x6 grid)

How to get the character index in a Silverlight TextBox in a click handler?

Specifically, what I'd like is to have a user right-click in a TextBox, figure out and save the index position within the text where the right-click occurred, and then later insert some text at that position once the user makes a choice from the context menu that pops up because of the right click.
The tricky part is getting the index position based on the coordinates of the right-click.
This is in Silverlight 4.
private int _insertPoint;
private void userNotification_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
// Move and open the context menu relative to its container.
contextMenu.HorizontalOffset = e.GetPosition(container).X;
contextMenu.VerticalOffset = e.GetPosition(container).Y;
contextMenu.IsOpen = true;
// Get the click coordinates relative to the TextBox.
int clickX = e.GetPosition(textBox).X;
int clickY = e.GetPosition(textBox).Y;
_insertPoint = ?; // Here's the problem.
}
private void SelectFieldToInsert(object sender, MouseButtonEventArgs e)
{
// Close the context menu.
contextMenu.IsOpen = false;
var item = sender as ListBoxItem;
textBox.Text = textBox.Text.Insert(_insertPoint, "<" + item.Content + ">");
}
The TextBox.SelectionStart property on a textbox should help you. From the link:
If there is no selection, the SelectionStart value gets or sets the location of the cursor.
How about:
// Get the click coordinates relative to the TextBox.
int clickX = (int)e.GetPosition(textBox).X;
int clickY = (int)e.GetPosition(textBox).Y;
int startPosition = 0;
double currentHeight = 0;
double calculatedHeight = 0;
int charIndex;
TextBlock tb = new TextBlock();
tb.Width = this.textBox.Width;
tb.FontFamily = this.textBox.FontFamily;
tb.FontSize = this.textBox.FontSize;
for (charIndex = 0; charIndex < this.textBox.Text.Length; charIndex++)
{
tb.Text = this.textBox.Text.Substring(startPosition, charIndex - startPosition + 1);
if (tb.ActualHeight > currentHeight)
{
if (currentHeight == 0)
{
currentHeight = tb.ActualHeight;
calculatedHeight = currentHeight;
}
else
{
startPosition = charIndex + 1;
charIndex++;
tb.Text = this.textBox.Text.Substring(startPosition, charIndex - startPosition + 1);
currentHeight = tb.ActualHeight;
calculatedHeight += currentHeight;
}
}
if (tb.ActualWidth > clickX && calculatedHeight > clickY) break;
}
_insertPoint = charIndex;
I won't lie and say it's elegant, but it appears to work reasonably well.

Categories