65.9K
CodeProject 正在变化。 阅读更多。
Home

滚动窗口内容以及控件中的多重撤销和重做

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.09/5 (2投票s)

2005年5月13日

2分钟阅读

viewsIcon

35115

downloadIcon

465

在窗口和控件中滚动内容以及多重撤销和重做。

Sample Image - MultiUndoandRedo.gif

引言

在 Pocket PC 编程中,我们总是需要在窗体上放置大量的控件。控件的数量通常非常大,以至于会超出窗体的高度,我们应该滚动窗体以便可以看到所有的信息。此外,很多时候我们需要撤销和重做之前的操作。同时,我们想要将一个字段的内容复制到另一个字段,因此 “复制、剪切、粘贴、清除、全选” 这些命令非常常用。此演示展示了一个包含上述功能的示例。

滚动窗体和输入面板阴影

我们可以使用 “VScrollBar” 和 “Panel” 来滚动窗口。 我们可以处理 “vScrollBar1_ValueChanged” 事件并编写以下代码

this.pnlDetails.Top=0-this.vScrollBar.Value;

我们必须将所有需要在窗口滚动时更改位置的控件都放在面板中,这样我们只需要更改面板的位置即可。

InputPanel 显示或隐藏时,它将引发 “inputPanel2_EnabledChanged” 事件,我们在此处更改 vScrollBar 的高度。

  private void inputPanel2_EnabledChanged(object sender, System.EventArgs e)
  {
   if (inputPanel2.Enabled == true)
   {
    this.vScrollBar.Height=inputPanel2.VisibleDesktop.Height;
   }
   else
   {
    this.vScrollBar.Height=OriginalHeight;
   }
  }
 

多重撤销/重做

我们使用两个堆栈 (stkUndostkRedo) 来处理撤销和重做操作,当用户撤销操作时,stkUndo 将推送一个包含窗体所有字段的快照,并且 stkRedo 将弹出一个元素。

快照结构体

它将充当要推送和弹出的元素。

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

撤销和重做的子程序

当用户发送 “撤销” 命令时,我们应该执行以下操作

  • SnapShot 推送到 stkUndo 并从 stkRedo 弹出。
  • 调用 getSnapshot 例程。

相反,当用户发送 “重做” 命令时,我们应该执行其他操作

  • SnapShot 推送到 stkRedo 并从 stkUndo 弹出。
  • 调用 getSnapshot 例程。

在控件失去焦点时,我们应该确保快照是否已更改,如果已更改,我们应该调用 “setSnapshot” 例程并将其推送到 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;
  }

剪贴板

我们应该实现 “剪切、复制和粘贴” 命令,因此我们应该使用剪贴板。在 .NET Compact Framework 中,我们不能直接使用剪贴板,我们应该使用 P/Invoke。

API 声明

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

注意

在 .NET Compact Framework 中,ContextMenu 中存在一个已知的错误。“Menu.Add” 应该在 “menuItem4.Text = "-";” 之后,因此在程序中我们应该更改 “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);
© . All rights reserved.