Introduction
In Pocket PC programming, we always need to put a lot of controls on a form. Always the number of controls is so large that it will overload the form�s height, we should scroll the form so that we can see all the information. Also, many times we need to undo and redo the previous operation. Also we want to copy a field�s content to another, so the commands �Copy, Cut, Paste, Clear, Select all� are very commonly used. This demo shows an example which contain the functions referenced above.
Scroll the form and Inputpanel shadow
We can use a �VScrollBar
� and a �Panel
� to scroll the windows. We can process the �vScrollBar1_ValueChanged
� event and write this code:
this.pnlDetails.Top=0-this.vScrollBar.Value;
We must put all controls whose position need to be changed when the window is scrolling in the panel, so that we only need change the panel�s position.
When the InputPanel
shows or hides, it will raise the �inputPanel2_EnabledChanged
� event, we change the vScrollBar
�s height here.
private void inputPanel2_EnabledChanged(object sender, System.EventArgs e)
{
if (inputPanel2.Enabled == true)
{
this.vScrollBar.Height=inputPanel2.VisibleDesktop.Height;
}
else
{
this.vScrollBar.Height=OriginalHeight;
}
}
Multi-Undo/Redo
We used two stacks (stkUndo
and stkRedo
) to process the undo an redo operation, when user undo an operation the stkUndo
will be push a snapshot which contains all fields of the form and the stkRedo
will pop an element.
The SnapShot struct
It will act as an element to be pushed and popped.
struct Snapshot
{
public string Field1;
public string Field2;
public string Field3;
public string Field4;
public string Field5;
public string Field6;
public string Field7;
public string Field8;
public string Field9;
public string Field10;
public string Field11;
public string Field12;
public int ScrollValue;
public TextBox tbfocused;
}
Subroutine for undo and redo
When the user sends �Undo� command, we should do the following things:
- Push the
SnapShot
to stkUndo
and pop from the stkRedo
.
- Call the
getSnapshot
routine.
In the opposite, when the user sends �Redo� command we should do the other things:
- Push
SnapShot
to stkRedo
and pop from the stkUndo
.
- Call the
getSnapshot
routine.
At the time which a control lost focus, we shoud make sure whether the snapshot has been changed, if changed we should call the �setSnapshot
� routine and push it to stkUndo
.
private void setSnapshot(ref Snapshot sp)
{
sp.Field1=this.textBox1.Text;
sp.Field2=this.textBox2.Text;
sp.Field3=this.textBox3.Text;
sp.Field4=this.textBox4.Text;
sp.Field5=this.textBox5.Text;
sp.Field6=this.textBox6.Text;
sp.Field7=this.textBox7.Text;
sp.Field8=this.textBox8.Text;
sp.Field9=this.textBox9.Text;
sp.Field10=this.textBox10.Text;
sp.Field11=this.textBox11.Text;
sp.Field12=this.textBox12.Text;
}
private void getSnapshot(Snapshot sp)
{
this.textBox1.Text=sp.Field1;
this.textBox2.Text=sp.Field2;
this.textBox3.Text=sp.Field3;
this.textBox4.Text=sp.Field4;
this.textBox5.Text=sp.Field5;
this.textBox6.Text=sp.Field6;
this.textBox7.Text=sp.Field7;
this.textBox8.Text=sp.Field8;
this.textBox9.Text=sp.Field9;
this.textBox10.Text=sp.Field10;
this.textBox11.Text=sp.Field11;
this.textBox12.Text=sp.Field12;
}
private bool CompareSnapshot(Snapshot sp)
{
if (this.textBox1.Text!=sp.Field1)
return false;
if (this.textBox2.Text!=sp.Field2)
return false;
if (this.textBox3.Text!=sp.Field3)
return false;
if (this.textBox4.Text!=sp.Field4)
return false;
if (this.textBox5.Text!=sp.Field5)
return false;
if (this.textBox6.Text!=sp.Field6)
return false;
if (this.textBox7.Text!=sp.Field7)
return false;
if (this.textBox8.Text!=sp.Field8)
return false;
if (this.textBox9.Text!=sp.Field9)
return false;
if (this.textBox10.Text!=sp.Field10)
return false;
if (this.textBox11.Text!=sp.Field11)
return false;
if (this.textBox12.Text!=sp.Field12)
return false;
return true;
}
Clipboard
We should implement commands of �Cut, Copy and Paste�, so we should use the Clipboard. And in .NET Compact Framework, we can�t use the Clipboard directly, we should use the P/Invoke.
API declare:
[DllImport("Coredll.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("Coredll.dll")]
private static extern bool CloseClipboard();
[DllImport("Coredll.dll")]
private static extern bool EmptyClipboard();
[DllImport("Coredll.dll")]
private static extern bool IsClipboardFormatAvailable(uint uFormat);
[DllImport("Coredll.dll")]
private static extern IntPtr GetClipboardData(uint uFormat);
[DllImport("Coredll.dll")]
private static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
[DllImport("coredll", EntryPoint="LocalAlloc", SetLastError=true)]
private static extern IntPtr LocalAllocCE(int uFlags, int uBytes);
private static IntPtr LocalAlloc(MemoryAllocFlags uFlags, int uBytes)
{
IntPtr ptr = IntPtr.Zero;
ptr = LocalAllocCE((int)uFlags,uBytes);
return ptr;
}
private static IntPtr StringToPointer(string val)
{
if(val == null)
return IntPtr.Zero;
IntPtr retVal = LocalAlloc(MemoryAllocFlags.LPtr, (val.Length + 1) * 2);
if(retVal == IntPtr.Zero)
throw new OutOfMemoryException();
Marshal.Copy(val.ToCharArray(), 0, retVal, val.Length);
return retVal;
}
public static void SetClipboardText(string text)
{
IntPtr hClipboard = IntPtr.Zero;
IntPtr hInstance=IntPtr.Zero;
Clipboard.OpenClipboard(hInstance);
hClipboard =
Clipboard.LocalAlloc(MemoryAllocFlags.LPtr,
(int)((text.Length + 1) * Marshal.SystemDefaultCharSize));
hClipboard = Clipboard.StringToPointer(text);
Clipboard.EmptyClipboard();
Clipboard.SetClipboardData((uint)ClipboardFormats.UnicodeText, hClipboard) ;
Clipboard.CloseClipboard();
}
public static string GetClipboardText()
{
IntPtr hInstance=IntPtr.Zero;
Clipboard.OpenClipboard(hInstance);
IntPtr buffer = Clipboard.GetClipboardData((uint)ClipboardFormats.UnicodeText);
string text = System.Runtime.InteropServices.Marshal.PtrToStringUni(buffer);
Clipboard.CloseClipboard();
return text;
}
public static bool IsClipboardTextAvailable()
{
return IsClipboardFormatAvailable((uint)ClipboardFormats.UnicodeText);
}
Pay attention to
In .NET Compact Framework, there is a known bug in ContextMenu
. The �Menu.Add
� should be after �menuItem4.Text = "-";
�, so in the program we should change the �InitializeComponent
�:
this.menupUndo.Text = "Undo";
this.menupUndo.Click += new System.EventHandler(this.menuUndo_Click);
this.menupRedo.Text = "Redo";
this.menupRedo.Click += new System.EventHandler(this.menuRedo_Click);
this.menuItem1.Text = "-";
this.menupCut.Text = "Cut";
this.menupCut.Click += new System.EventHandler(this.menuCut_Click);
this.menupCopy.Text = "Copy";
this.menupCopy.Click += new System.EventHandler(this.menuCopy_Click);
this.menupPaste.Text = "Paste";
this.menupPaste.Click += new System.EventHandler(this.menuPaste_Click);
this.menupClear.Text = "Clear";
this.menupClear.Click += new System.EventHandler(this.menuClear_Click);
this.menupSelectAll.Text = "Select All";
this.menupSelectAll.Click += new System.EventHandler(this.menuSelectAll_Click);
this.contextMenu1.MenuItems.Add(this.menupUndo);
this.contextMenu1.MenuItems.Add(this.menupRedo);
this.contextMenu1.MenuItems.Add(this.menuItem1);
this.contextMenu1.MenuItems.Add(this.menupCut);
this.contextMenu1.MenuItems.Add(this.menupCopy);
this.contextMenu1.MenuItems.Add(this.menupPaste);
this.contextMenu1.MenuItems.Add(this.menupClear);
this.contextMenu1.MenuItems.Add(this.menupSelectAll);
this.contextMenu1.Popup += new System.EventHandler(this.contextMenu1_Popup);