WoW Chat Project Part 2
So let's jump right in. We've got the program starting and running fine, and setting the proper channel. Now we have to handle the event that fires when the user presses "Enter". We want to write the text of the entry window to the chat window, clear the entry window and set the channel back.
To get us started, we want to add an event handler to the Form1 constructor:
public Form1()
{
InitializeComponent();
rChatEntry.KeyDown += new KeyEventHandler(chatEnterPressed);
}
I covered event handlers in the SimConnect tutorials, but the whole handler/delegate concept is pretty simple. When something happens in specified control (in this case, that's the chat entry window, rChatEntry) it fires off an event. Since we've registered an event handler, the event has a delegate, which is simply a function that runs when the event gets fired.
So now we need chatEnterPressed.
private void chatEnterPressed(object sender, KeyEventArgs e)
{
// check to see if Enter was pressed
if (e.KeyValue == 13)
{
if (rChatEntry.Text.Length != lineStart)
{
}
e.Handled = true;
}
}
This is essentially just the frame, we'll get to the guts of the function in a second.
An event handler function like this has parameters for the sender which is simply whatever control or object sent the event. The variable e represents whatever arguments got sent along with the event, in this case, it's info about the keypress. For example, we are testing here for a specific key value, 13, which is the Enter button.
Then we throw in a little validation to make sure the length of our total string (channel name + user input) is not the same as our lineStart value. You may recall, lineStart is our "real" starting position, meaning how far along the chat entry text string where user input starts. So if the values are the same, there's been no user input. We don't want to send empty lines to the chat, so we have that check in there.
The last little bit is e.Handled = true. When we get an event like a keypress, the event handler's delegate gets called, but there is still a default handling of the keypress. In the case of the textbox like we are dealing with it's just a new line, but we don't want that. We want to go back to the start of the first line. So we say that the event is "Handled" so that any other event handlers don't touch it.
Now for the guts:
if (lineStart != rChatEntry.Text.Length)
{
// add the line to the chat window
rChatWindow.SelectedRtf = rChatEntry.Rtf;
// clear the text entry
rChatEntry.Select(0, rChatEntry.Text.Length);
rChatEntry.SelectionProtected = false;
rChatEntry.Text = System.String.Empty;
// Set the channel back
chatSetChannel(selectedChannel, selectedColor);
// Scroll to the proper point
rChatWindow.ScrollToCaret();
}
rChatWindow.SelectedRtf is the spot in the chat window where our cursor/caret/pointer whatever you want to call it is. We've used the Text property to get the contents of textboxs, but with Rich Text, we can copy the formatting over as well.
Then we select everything in the chat window, set it to unprotected, and empty the string out. Now we should have a blank chat entry field, and the line of chat is in the chat window.
Back to our handy helper function to set the Channel name in the entry box. The last little bit is a textbox function that scrolls the window (if necessary) to the position of the caret. In this case, what it means is that if the text in the window gets long enough to cause a vertical scroll bar to appear, the window scrolls down to show whatever new line is added.
Looks like that is working fine.
Now for the real point of this all, the channel switching.
The real simple way to assign an event handler for the TextChanged property is just going into the designer view and double clicking on our chat entry bar.
It should create the function for you. I renamed the generated function to chatTextChanged to fit with everything else. There are some good tools in here for renaming or refactoring. By clicking on the little red underline that appears or by pressing Shift-Alt-F10, you get the rename dialog, and can select to rename the function. This is important because there's an event handler auto-generated that assigns the new function as a delegate. If we change the name of the function without updating the event handler through the rename dialog, we end up with errors and general not-working-ness.
This event will fire whenever there's a change to the text in the entry window. We'll then check to see if the text is two characters longer than our lineStart value, for the "/g" or whatever we need. If so, the next step is to select just those two characters, set it to lower case for matching purposes and match it to a list of possible solutions. If it doesn't match any of them, just deselect it all.
The rest is simple, because we've already got a helper function to set the channel.
So here's our code to do it.
private void chatTextChanged(object sender, EventArgs e)
{
// test if the first two characters are /something. If there are more than two characters, skip it
if (rChatEntry.Text.Length == lineStart + 2)
{
// use a switch to test for our cases
rChatEntry.Select(rChatEntry.Text.Length - 2, rChatEntry.Text.Length);
switch (rChatEntry.SelectedText.ToLower())
{
case "/g":
// Switch to guild chat
chatSetChannel("Guild", Color.Green);
break;
case "/1":
// Switch to general chat
chatSetChannel("General", Color.Black);
break;
case "/2":
// Switch to trade channel
chatSetChannel("Trade", Color.Orange);
break;
case "/s":
// switch back to local "saying"
chatSetChannel("Say", Color.Red);
break;
default:
// deselect all
rChatEntry.Select(rChatEntry.Text.Length, rChatEntry.Text.Length);
break;
}
}
}
Walking through it, we have the test to check if we only have two user-inputted characters, selecting those two characters, and then a switch statement to test for the various slash commands we are looking for. Notice in the switch line that we use the ToLower method so that "/G" comes out the same as "/g".
Then for each case, we set the channel using our helper program, and if it doesn't match anything, we deselect our selection.
Looks like it works fine. I added in functions for the context menu items and a little mouse click handler that keeps the user from changing our position in the chat window. Otherwise, it could be possible to insert chat lines in the middle of other lines.
Here's the code as I have it:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace AutoChatChannels
{
public partial class Form1 : Form
{
// The currently selected channel
private string selectedChannel = "General";
private Color selectedColor = Color.Black;
// Since we are appending a channel name to the beginning of the line, we need to keep our own count
// where the "real" line (i.e. the user input) starts
private int lineStart = 0;
public Form1()
{
InitializeComponent();
rChatEntry.KeyDown += new KeyEventHandler(chatEnterPressed);
rChatWindow.MouseDown += new MouseEventHandler(rChatWindow_MouseClick);
}
void rChatWindow_MouseClick(object sender, MouseEventArgs e)
{
rChatWindow.Select(rChatWindow.Text.Length, rChatWindow.Text.Length);
}
private void Form1_Load(object sender, EventArgs e)
{
// Get datetime of entry into the channel
string currentDate = DateTime.Now.ToString();
// Write out the join message
string entryText = "You have joined the General Chat Channel at "+currentDate+System.Environment.NewLine;
// Add the text to the chat window
rChatWindow.Text += entryText;
// Now we have to select the text to make formatting changes to it.
rChatWindow.Select(lineStart, entryText.Length);
// make the text blue and bold
rChatWindow.SelectionColor = Color.Blue;
// Now make sure nothing is selected
rChatWindow.Select(rChatWindow.Text.Length, rChatWindow.Text.Length);
// Enter that in our text entry box
chatSetChannel(selectedChannel, selectedColor);
}
private void chatTextChanged(object sender, EventArgs e)
{
// test if the first two characters are /something. If there are more than two characters, skip it
if (rChatEntry.Text.Length == lineStart + 2)
{
// use a switch to test for our cases
rChatEntry.Select(rChatEntry.Text.Length - 2, rChatEntry.Text.Length);
switch (rChatEntry.SelectedText.ToLower())
{
case "/g":
// Switch to guild chat
chatSetChannel("Guild", Color.Green);
break;
case "/1":
// Switch to general chat
chatSetChannel("General", Color.Black);
break;
case "/2":
// Switch to trade channel
chatSetChannel("Trade", Color.Orange);
break;
case "/s":
// switch back to local "saying"
chatSetChannel("Say", Color.Red);
break;
default:
// deselect all
rChatEntry.Select(rChatEntry.Text.Length, rChatEntry.Text.Length);
break;
}
}
}
private void chatSetChannel(string channelName, Color channelColor)
{
// Select everything
rChatEntry.Select(0, rChatEntry.Text.Length);
// Unprotect everything first
rChatEntry.SelectionProtected = false;
// Set channel name
rChatEntry.Text = "<" + channelName + "> ";
// Select the text again
rChatEntry.Select(0, rChatEntry.Text.Length);
// Set the color
rChatEntry.SelectionColor = channelColor;
// Set the current text as protected
rChatEntry.SelectionProtected = true;
// Clear the selection to the end of the channel name
rChatEntry.Select(rChatEntry.Text.Length, rChatEntry.Text.Length);
// set our "real" start of line position
lineStart = rChatEntry.Text.Length;
// set our color
rChatEntry.ForeColor = channelColor;
// Selected Channel/Color
selectedChannel = channelName;
selectedColor = channelColor;
}
private void chatEnterPressed(object sender, KeyEventArgs e)
{
// check to see if Enter was pressed
if (e.KeyValue == 13)
{
if (lineStart != rChatEntry.Text.Length)
{
// add the line to the chat window
rChatWindow.SelectedRtf = rChatEntry.Rtf;
// clear the text entry
rChatEntry.Select(0, rChatEntry.Text.Length);
rChatEntry.SelectionProtected = false;
rChatEntry.Text = System.String.Empty;
// Set the channel back
chatSetChannel(selectedChannel, selectedColor);
// Scroll to the proper point
rChatWindow.ScrollToCaret();
}
e.Handled = true;
}
}
private void menuitemGeneralChannel_Click(object sender, EventArgs e)
{
chatSetChannel("General", Color.Black);
}
private void menuitemTradeChannel_Click(object sender, EventArgs e)
{
chatSetChannel("Trade", Color.Orange);
}
private void menuitemGuildChannel_Click(object sender, EventArgs e)
{
chatSetChannel("Guild", Color.Green);
}
}
}
Pretty simple little process all in all, I'd like to build off of it down the road, maybe incorporate it into something.
Please feel free to drop any questions in there for me.
Labels: Programming, Visual Studio 2008, WoW
0 Comments:
Post a Comment
<< Home