teclas revertir repetir rehacer qué para icono deshacer cómo control combinacion comando accion .net excel vba ms-word vsto

.net - revertir - ¿Puedo crear una transacción de deshacer en Word o Excel?(VSTO)



teclas para rehacer (4)

Observé que Project 2007 tiene las funciones que permiten que las operaciones que se pueden deshacer se coloquen en un solo ítem de pila, o "deshacer transacción". Por ejemplo:

Application.OpenUndoTransaction "Create 6 tasks" Dim i As Integer For i = 1 To 6 ActiveProject.Tasks.Add "UndoMe " & i Next Application.CloseUndoTransaction

Lo que esto significa es que el usuario puede deshacer todas las acciones en una sola acción de deshacer, en lugar de 6 veces.

Sería genial implementarlo en Word y / o Excel, ya que estoy haciendo algunas cosas en VSTO que hacen múltiples cambios a la vez, y será un poco molesto para el usuario si tiene que hacer clic en Deshacer varias veces si ellos cometen un error Aunque esas funciones específicas no parecen existir, ¿alguien sabe si / cómo se puede hacer de alguna manera?


Puede simular el comportamiento transaccional en Word sobrescribiendo las rutinas de comando Deshacer y Rehacer en VBA (no creo que la sobreescritura de los comandos integrados de Word sea posible usando solo VSTO). El inicio de una transacción se marca al agregar un marcador, el final se marca al eliminar el marcador.

Cuando llamemos deshacer, comprobamos si el marcador de marca de la transacción está presente y repetimos el deshacer hasta que el marcador desaparezca. Rehacer está funcionando de la misma manera. Este mecanismo admite el deshacer / rehacer transaccional de todas las modificaciones hechas al contenido del documento. Sin embargo, para permitir deshacer / rehacer modificaciones de las propiedades del documento, se debe implementar un mecanismo especial utilizando la macro SetCustomProp. Las propiedades del documento no deberían establecerse directamente sino solo a través de esta macro.

Actualización: Olvidé mencionar claramente que este enfoque solo funciona con los atajos de teclado y los comandos de menú, al hacer clic en el botón de la barra de herramientas aún se realiza un solo paso de deshacer. Por lo tanto, decidimos reemplazar los botones de la barra de herramientas con botones personalizados. El código ha estado en uso durante bastante tiempo con Word 2003 (no se ha probado con Word 2007, así que prepárese para la sorpresa;)

Option Explicit '' string constants for Undo mechanism Public Const BM_IN_MACRO As String = "_InMacro_" Public Const BM_DOC_PROP_CHANGE As String = "_DocPropChange_" Public Const BM_DOC_PROP_NAME As String = "_DocPropName_" Public Const BM_DOC_PROP_OLD_VALUE As String = "_DocPropOldValue_" Public Const BM_DOC_PROP_NEW_VALUE As String = "_DocPropNewValue_" ''----------------------------------------------------------------------------------- '' Procedure : EditUndo '' Purpose : Atomic undo of macros '' Note: This macro only catches the menu command and the keyboard shortcut, '' not the toolbar command ''----------------------------------------------------------------------------------- Public Sub EditUndo() '' Catches Ctrl-Z ''On Error Resume Next Dim bRefresh As Boolean bRefresh = Application.ScreenUpdating Application.ScreenUpdating = False Do If ActiveDocument.Bookmarks.Exists(BM_DOC_PROP_CHANGE) Then Dim strPropName As String Dim strOldValue As String strPropName = ActiveDocument.Bookmarks(BM_DOC_PROP_NAME).Range.Text strOldValue = ActiveDocument.Bookmarks(BM_DOC_PROP_OLD_VALUE).Range.Text ActiveDocument.CustomDocumentProperties(strPropName).Value = strOldValue End If Loop While (ActiveDocument.Undo = True) _ And ActiveDocument.Bookmarks.Exists(BM_IN_MACRO) Application.ScreenUpdating = bRefresh End Sub ''----------------------------------------------------------------------------------- '' Procedure : EditRedo '' Purpose : Atomic redo of macros '' Note: This macro only catches the menu command and the keyboard shortcut, '' not the toolbar command ''----------------------------------------------------------------------------------- Public Sub EditRedo() '' Catches Ctrl-Y Dim bRefresh As Boolean bRefresh = Application.ScreenUpdating Application.ScreenUpdating = False Do If ActiveDocument.Bookmarks.Exists(BM_DOC_PROP_CHANGE) Then Dim strPropName As String Dim strNewValue As String strPropName = ActiveDocument.Bookmarks(BM_DOC_PROP_NAME).Range.Text strNewValue = ActiveDocument.Bookmarks(BM_DOC_PROP_NEW_VALUE).Range.Text ActiveDocument.CustomDocumentProperties(strPropName).Value = strNewValue End If Loop While (ActiveDocument.Redo = True) _ And ActiveDocument.Bookmarks.Exists(BM_IN_MACRO) Application.ScreenUpdating = bRefresh End Sub ''----------------------------------------------------------------------------------- '' Procedure : SetCustomProp '' Purpose : Sets a custom document property ''----------------------------------------------------------------------------------- Public Function SetCustomProp(oDoc As Document, strName As String, strValue As String) Dim strOldValue As String On Error GoTo existsAlready strOldValue = "" oDoc.CustomDocumentProperties.Add _ Name:=strName, LinkToContent:=False, Value:=Trim(strValue), _ Type:=msoPropertyTypeString GoTo exitHere existsAlready: strOldValue = oDoc.CustomDocumentProperties(strName).Value oDoc.CustomDocumentProperties(strName).Value = strValue exitHere: '' support undo / redo of changes to the document properties ''On Error Resume Next Dim bCalledWithoutUndoSupport As Boolean If Not ActiveDocument.Bookmarks.Exists(BM_IN_MACRO) Then ActiveDocument.Range.Bookmarks.Add BM_IN_MACRO, ActiveDocument.Range bCalledWithoutUndoSupport = True End If Dim oRange As Range Set oRange = ActiveDocument.Range oRange.Collapse wdCollapseEnd oRange.Text = " " oRange.Bookmarks.Add "DocPropDummy_", oRange oRange.Collapse wdCollapseEnd oRange.Text = strName oRange.Bookmarks.Add BM_DOC_PROP_NAME, oRange oRange.Collapse wdCollapseEnd oRange.Text = strOldValue oRange.Bookmarks.Add BM_DOC_PROP_OLD_VALUE, oRange oRange.Collapse wdCollapseEnd oRange.Text = strValue oRange.Bookmarks.Add BM_DOC_PROP_NEW_VALUE, oRange oRange.Bookmarks.Add BM_DOC_PROP_CHANGE ActiveDocument.Bookmarks(BM_DOC_PROP_CHANGE).Delete Set oRange = ActiveDocument.Bookmarks(BM_DOC_PROP_NEW_VALUE).Range ActiveDocument.Bookmarks(BM_DOC_PROP_NEW_VALUE).Delete If Len(oRange.Text) > 0 Then oRange.Delete Set oRange = ActiveDocument.Bookmarks(BM_DOC_PROP_OLD_VALUE).Range ActiveDocument.Bookmarks(BM_DOC_PROP_OLD_VALUE).Delete If Len(oRange.Text) > 0 Then oRange.Delete Set oRange = ActiveDocument.Bookmarks(BM_DOC_PROP_NAME).Range ActiveDocument.Bookmarks(BM_DOC_PROP_NAME).Delete If Len(oRange.Text) > 0 Then oRange.Delete Set oRange = ActiveDocument.Bookmarks("DocPropDummy_").Range ActiveDocument.Bookmarks("DocPropDummy_").Delete If Len(oRange.Text) > 0 Then oRange.Delete If bCalledWithoutUndoSupport And ActiveDocument.Bookmarks.Exists(BM_IN_MACRO) Then ActiveDocument.Bookmarks(BM_IN_MACRO).Delete End If End Function ''----------------------------------------------------------------------------------- '' Procedure : SampleUsage '' Purpose : Demonstrates a transaction ''----------------------------------------------------------------------------------- Private Sub SampleUsage() On Error Resume Next '' mark begin of transaction ActiveDocument.Range.Bookmarks.Add BM_IN_MACRO Selection.Text = "Hello World" '' do other stuff '' mark end of transaction ActiveDocument.Bookmarks(BM_IN_MACRO).Delete End Sub


He estado masticando esto por un tiempo. Aquí está mi intento de usar un documento oculto, y luego tomar el archivo WordOpenXML del documento oculto y colocarlo en el documento real cuando sea necesario para deshacer cualquier cantidad de acciones de VSTO.

//Usage from ThisDocument VSTO Document level project public partial class ThisDocument { //Used to buffer writing text & formatting to document (to save undo stack) public static DocBuffer buffer; //Attached Template public static Word.Template template; private void ThisDocument_Startup(object sender, System.EventArgs e) { //Ignore changes to template (removes prompt to save changes to template) template = (Word.Template)this.Application.ActiveDocument.get_AttachedTemplate(); template.Saved = true; //Document buffer buffer = new DocBuffer(); //Start buffer ThisDocument.buffer.Start(); //This becomes one "undo" Word.Selection curSel = Globals.ThisDocument.Application.Selection; curSel.TypeText(" "); curSel.TypeBackspace(); curSel.Font.Bold = 1; curSel.TypeText("Hello, world!"); curSel.Font.Bold = 0; curSel.TypeText(" "); //end buffer, print out text ThisDocument.buffer.End(); } void Application_DocumentBeforeClose(Microsoft.Office.Interop.Word.Document Doc, ref bool Cancel) { buffer.Close(); } private void ThisDocument_Shutdown(object sender, System.EventArgs e) { buffer.Close(); } }

Aquí está la Clase DocBuffer:

public class DocBuffer { //Word API Objects Word._Document HiddenDoc; Word.Selection curSel; Word.Template template; //ref parameters object missing = System.Type.Missing; object FalseObj = false; //flip this for docbuffer troubleshooting object templateObj; //Is docbuffer running? public Boolean started{ get; private set; } //Open document on new object public DocBuffer() { //Clear out unused buffer bookmarks Word.Bookmarks bookmarks = Globals.ThisDocument.Application.ActiveDocument.Bookmarks; bookmarks.ShowHidden = true; foreach (Word.Bookmark mark in bookmarks) { if (mark.Name.Contains("_buf")) { mark.Delete(); } } //Remove trail of undo''s for clearing out the bookmarks Globals.ThisDocument.UndoClear(); //Set up template template = ThisDocument.template; templateObj = template; //Open Blank document, then attach styles *and update HiddenDoc = Globals.ThisDocument.Application.Documents.Add(ref missing, ref missing, ref missing, ref FalseObj); HiddenDoc.set_AttachedTemplate(ref templateObj); HiddenDoc.UpdateStyles(); //Tell hidden document it has been saved to remove rare prompt to save document HiddenDoc.Saved = true; //Make primary document active Globals.ThisDocument.Activate(); } ~DocBuffer() { try { HiddenDoc.Close(ref FalseObj, ref missing, ref missing); } catch { } } public void Close() { try { HiddenDoc.Close(ref FalseObj, ref missing, ref missing); } catch { } } public void Start() { try { //Make hidden document active to receive selection HiddenDoc.Activate(); //results in a slight application focus loss } catch (System.Runtime.InteropServices.COMException ex) { if (ex.Message == "Object has been deleted.") { //Open Blank document, then attach styles *and update HiddenDoc = Globals.ThisDocument.Application.Documents.Add(ref missing, ref missing, ref missing, ref FalseObj); HiddenDoc.set_AttachedTemplate(ref templateObj); HiddenDoc.UpdateStyles(); HiddenDoc.Activate(); } else throw; } //Remove Continue Bookmark, if exists Word.Bookmarks hiddenDocBookmarks = Globals.ThisDocument.Application.ActiveDocument.Bookmarks; if (hiddenDocBookmarks.Exists("Continue")) { object deleteMarkObj = "Continue"; Word.Bookmark deleteMark = hiddenDocBookmarks.get_Item(ref deleteMarkObj); deleteMark.Select(); deleteMark.Delete(); } //Tell hidden document it has been saved to remove rare prompt to save document HiddenDoc.Saved = true; //Keep track when started started = true; } //Used for non-modal dialogs to bring active document back up between text insertion public void Continue() { //Exit quietly if buffer hasn''t started if (!started) return; //Verify hidden document is active if ((HiddenDoc as Word.Document) != Globals.ThisDocument.Application.ActiveDocument) { HiddenDoc.Activate(); } //Hidden doc selection curSel = Globals.ThisDocument.Application.Selection; //Hidden doc range Word.Range bufDocRange; //Select entire doc, save range curSel.WholeStory(); bufDocRange = curSel.Range; //Find end, put a bookmark there bufDocRange.SetRange(curSel.End, curSel.End); object bookmarkObj = bufDocRange; //Generate "Continue" hidden bookmark Word.Bookmark mark = Globals.ThisDocument.Application.ActiveDocument.Bookmarks.Add("Continue", ref bookmarkObj); mark.Select(); //Tell hidden document it has been saved to remove rare prompt to save document HiddenDoc.Saved = true; //Make primary document active Globals.ThisDocument.Activate(); } public void End() { //Exit quietly if buffer hasn''t started if (!started) return; //Turn off buffer started flag started = false; //Verify hidden document is active if ((HiddenDoc as Word.Document) != Globals.ThisDocument.Application.ActiveDocument) { HiddenDoc.Activate(); } //Remove Continue Bookmark, if exists Word.Bookmarks hiddenDocBookmarks = Globals.ThisDocument.Application.ActiveDocument.Bookmarks; hiddenDocBookmarks.ShowHidden = true; if (hiddenDocBookmarks.Exists("Continue")) { object deleteMarkObj = "Continue"; Word.Bookmark deleteMark = hiddenDocBookmarks.get_Item(ref deleteMarkObj); deleteMark.Delete(); } //Hidden doc selection curSel = Globals.ThisDocument.Application.Selection; //Hidden doc range Word.Range hiddenDocRange; Word.Range bufDocRange; //Select entire doc, save range curSel.WholeStory(); bufDocRange = curSel.Range; //If cursor bookmark placed in, move there, else find end of text, put a bookmark there Boolean cursorFound = false; if (hiddenDocBookmarks.Exists("_cursor")) { object cursorBookmarkObj = "_cursor"; Word.Bookmark cursorBookmark = hiddenDocBookmarks.get_Item(ref cursorBookmarkObj); bufDocRange.SetRange(cursorBookmark.Range.End, cursorBookmark.Range.End); cursorBookmark.Delete(); cursorFound = true; } else { //The -2 is done because [range object].WordOpenXML likes to drop bookmarks at the end of the range bufDocRange.SetRange(curSel.End - 2, curSel.End - 2); } object bookmarkObj = bufDocRange; //Generate GUID for hidden bookmark System.Guid guid = System.Guid.NewGuid(); String id = "_buf" + guid.ToString().Replace("-", string.Empty); Word.Bookmark mark = Globals.ThisDocument.Application.ActiveDocument.Bookmarks.Add(id, ref bookmarkObj); //Get OpenXML Text (Text with formatting) curSel.WholeStory(); hiddenDocRange = curSel.Range; string XMLText = hiddenDocRange.WordOpenXML; //Clear out contents of buffer hiddenDocRange.Delete(ref missing, ref missing); //comment this for docbuffer troubleshooting //Tell hidden document it has been saved to remove rare prompt to save document HiddenDoc.Saved = true; //Make primary document active Globals.ThisDocument.Activate(); //Get selection from new active document curSel = Globals.ThisDocument.Application.Selection; //insert buffered formatted text into main document curSel.InsertXML(XMLText, ref missing); //Place cursor at bookmark+1 (this is done due to WordOpenXML ignoring bookmarks at the end of the selection) Word.Bookmarks bookmarks = Globals.ThisDocument.Application.ActiveDocument.Bookmarks; bookmarks.ShowHidden = true; object stringObj = id; Word.Bookmark get_mark = bookmarks.get_Item(ref stringObj); bufDocRange = get_mark.Range; if (cursorFound) //Canned language actively placed cursor bufDocRange.SetRange(get_mark.Range.End, get_mark.Range.End); else //default cursor at the end of text bufDocRange.SetRange(get_mark.Range.End + 1, get_mark.Range.End + 1); bufDocRange.Select(); }



Excel tiene un soporte incorporado (limitado) para deshacer y rehacer como parte de su arquitectura VBA.

No estoy familiarizado con vsto, así que no sé si esto te ayudará, pero puedes echarle un vistazo a esta pregunta para obtener más detalles.