Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Scroll the contents in windows and Multi-Undo and Redo in controls

0.00/5 (No votes)
12 May 2005 1  
Scroll the contents in windows and multi-undo and redo in controls.

Sample Image - MultiUndoandRedo.gif

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
 {
  //a struct as a form's snapshot,when undo click this struct will

  //be push to redo stack pop from undo stack,and when redo click

  //this struct will be push to undo stack pop from redo stack.

  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)
  {
   //write value to the snapshot from the form's control which

   //hold the undo or redo operation

   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)
  {
   //read the value from snapshot,and update the

   //control this value.

   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)
  {
   //compare the snapshot with the form,

   //when all items are equal it return true,

   //else return false.

   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�:

   // 

   // menupUndo

   // 

   this.menupUndo.Text = "Undo";
   this.menupUndo.Click += new System.EventHandler(this.menuUndo_Click);
   // 

   // menupRedo

   // 

   this.menupRedo.Text = "Redo";
   this.menupRedo.Click += new System.EventHandler(this.menuRedo_Click);
   // 

   // menuItem1

   // 

   this.menuItem1.Text = "-";
   // 

   // menupCut

   // 

   this.menupCut.Text = "Cut";
   this.menupCut.Click += new System.EventHandler(this.menuCut_Click);
   // 

   // menupCopy

   // 

   this.menupCopy.Text = "Copy";
   this.menupCopy.Click += new System.EventHandler(this.menuCopy_Click);
   // 

   // menupPaste

   // 

   this.menupPaste.Text = "Paste";
   this.menupPaste.Click += new System.EventHandler(this.menuPaste_Click);
   // 

   // menupClear

   // 

   this.menupClear.Text = "Clear";
   this.menupClear.Click += new System.EventHandler(this.menuClear_Click);
   // 

   // menupSelectAll

   // 

   this.menupSelectAll.Text = "Select All";
   this.menupSelectAll.Click += new System.EventHandler(this.menuSelectAll_Click);
   // 

   // contextMenu1

   // 

   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);

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here