locked
VS Extension: When adding multiple files to a project, IVsTrackProjectDocumentsEvents2.OnQueryAddFiles is fired for each file separately RRS feed

  • Question

  • Hello,

    I'm working on an Extension for Visual Studio 2010, (The version I use is 10.0.30128.0 RC1Rel, installed on Windows XP professional, Version 2002, SP3), the Extension is written in C#.

    The Add-In must do some pre-processing and validation of files, which are added to a project. One of the steps include to check if the file which is added already exists in a specified location, ask the user whether he wants to replace the file and either stop the execution of the event or forward it.

    I have registered for the IVsTrackProjectDocumentsEvents2.OnQueryAddFiles() event and it works fine if one file is added to the project (for example by right-clicking on the project and choosing "Add Existing Item"). In case the user has selected multiple files to be added though, the same event is fired for each separate file, e.g, you right-click on the project, select "Add existing item" then select a couple of files from the file-select dialog and click "OK".

    I expected that the OnQueryAddFiles-event will be fired once; it has a list with files as a parameter( string[] rgpszMkDocuments) and I expected it to be filled with the names of all of the selected files. But it turns out that a separate event is fired for each file, (the parameter " string[] rgpszMkDocuments" contains only one file).

    This behavior  prevents me from adding an "Yes to all" and "No to all" options to my file-replace dialog.

    So my question is, is this the right behavior of this event, and will this be changed for the release version of Visual Studio 2010. Or is there any other event I can register for, which is fired once for multiple file selection (Because I really need to enable the "Yes to all" and "No to all"-options)

    Here is a link to the onQueryAddFiles event's documentation:

    http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.ivstrackprojectdocuments2.onqueryaddfiles%28VS.100%29.aspx

    Thanks in advance for the response,

    Regards,

    Diana

     

    Wednesday, March 24, 2010 10:05 AM

Answers

  • Hello,

    I use Visual studio 2010 ultimate, version is 10.0.30128.1 RC1Rel.

    I need to add an "Yes to all" and "No to all"-options in the file-replace dialog, which means that in case the user has selected many files, and wants to replace them all, I should ask only once, for all the files. How can I implement it if a separate event is triggered for each file? 

    You know “Yes to all” is easy to implement, please see My implementation of IVsTrackProjectDocumentsEvents2, which has implemented the “Yes to all”.

        class DocEvents : IVsTrackProjectDocumentsEvents2

        {

            #region IVsTrackProjectDocumentsEvents2 Members

     

            public int OnAfterAddDirectoriesEx(int cProjects, int cDirectories, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDDIRECTORYFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterAddFilesEx");

                return 1;

            }

     

            public int OnAfterRemoveDirectories(int cProjects, int cDirectories, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSREMOVEDIRECTORYFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterRemoveFiles(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSREMOVEFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterRenameDirectories(int cProjects, int cDirs, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgszMkOldNames, string[] rgszMkNewNames, VSRENAMEDIRECTORYFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterRenameFiles(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgszMkOldNames, string[] rgszMkNewNames, VSRENAMEFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterSccStatusChanged(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, uint[] rgdwSccStatus)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnQueryAddDirectories(IVsProject pProject, int cDirectories, string[] rgpszMkDocuments, VSQUERYADDDIRECTORYFLAGS[] rgFlags, VSQUERYADDDIRECTORYRESULTS[] pSummaryResult, VSQUERYADDDIRECTORYRESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryAddDirectories");

                return 1;

            }

    //Set a boolean to see whether you have clicked "Yes to all".

            Boolean YesToAll = false;

            public int OnQueryAddFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYADDFILEFLAGS[] rgFlags, VSQUERYADDFILERESULTS[] pSummaryResult, VSQUERYADDFILERESULTS[] rgResults)

            {

                //NotifyForm is the mode form that notify the user.

              

                NotifyForm nForm = new NotifyForm();

                if (!YesToAll)

                {

                    if (DialogResult.Yes == nForm.ShowDialog())

                    {

                        YesToAll = true;

                    }

                    else if (DialogResult.OK == nForm.ShowDialog())

                    {

                        // The file will be added to the project.

                        pSummaryResult[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddOK;

                        rgResults[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddOK;

                    }

                    else

                    {

                        //The file will not be added.

                        pSummaryResult[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK;

                        rgResults[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK;

                    }

                }

                return 1;

               

            }

     

            public int OnQueryRemoveDirectories(IVsProject pProject, int cDirectories, string[] rgpszMkDocuments, VSQUERYREMOVEDIRECTORYFLAGS[] rgFlags, VSQUERYREMOVEDIRECTORYRESULTS[] pSummaryResult, VSQUERYREMOVEDIRECTORYRESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRemoveDirectories");

                return 1;

            }

     

            public int OnQueryRemoveFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYREMOVEFILEFLAGS[] rgFlags, VSQUERYREMOVEFILERESULTS[] pSummaryResult, VSQUERYREMOVEFILERESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRemoveFiles");

                return 1;

            }

     

            public int OnQueryRenameDirectories(IVsProject pProject, int cDirs, string[] rgszMkOldNames, string[] rgszMkNewNames, VSQUERYRENAMEDIRECTORYFLAGS[] rgFlags, VSQUERYRENAMEDIRECTORYRESULTS[] pSummaryResult, VSQUERYRENAMEDIRECTORYRESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRenameDirectories");

                return 1;

            }

     

            public int OnQueryRenameFiles(IVsProject pProject, int cFiles, string[] rgszMkOldNames, string[] rgszMkNewNames, VSQUERYRENAMEFILEFLAGS[] rgFlags, VSQUERYRENAMEFILERESULTS[] pSummaryResult, VSQUERYRENAMEFILERESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRenameFiles");

                return 1;

            }

     

            #endregion

        }

    This is the dialog form class

        public partial class NotifyForm : Form

        {

            public NotifyForm()

            {

                InitializeComponent();

            }

            private void button1_Click(object sender, EventArgs e)

            {

                //Yes

                this.DialogResult = DialogResult.OK;

            }

     

            private void button2_Click(object sender, EventArgs e)

            {

                //No

                this.DialogResult = DialogResult.Cancel;

            }

     

            private void button3_Click(object sender, EventArgs e)

            {

                //Yes to all

                this.DialogResult = DialogResult.Yes;

            }

        }

    This is the event registration code snippet.

            private void MenuItemCallback(object sender, EventArgs e)

            {

                IVsTrackProjectDocuments2 trackDoc = GetService(typeof(SVsTrackProjectDocuments)) as IVsTrackProjectDocuments2;

                IVsSolution2 sln = GetService(typeof(SVsSolution)) as IVsSolution2;

                DTE dte = GetService(typeof(SDTE)) as DTE;

                string projUniqueName = dte.Solution.Projects.Item(1).FullName;

     

                IVsHierarchy hierarchy;

                sln.GetProjectOfUniqueName(projUniqueName, out hierarchy);

                IVsProject proj = (IVsProject)hierarchy;

                DocEvents de = new DocEvents();

     

                uint pdwCookie;

                trackDoc.AdviseTrackProjectDocumentsEvents(de, out pdwCookie);

     

            }

    Please look at the code, and is there any difference between your code and mine?

    Thanks

    Chao

    • Marked as answer by Chao Kuo Friday, April 2, 2010 2:42 AM
    Tuesday, March 30, 2010 2:44 AM
  • Hello, Again

    I still don’t know the reason for your OnAfterAddFilesEx was triggered multiple times.

    Because my OnAfterAddFilesEx was triggered only once, and the cFileso is the number of files that were added, so I could reset the YesToAll to false inside this method.

    Just like

    public int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterAddFilesEx");

                this.YesToAll = false;

                return 1;

            }

    Have you found the reason for the wrong behavior of your OnAfterAddFilesEx?

    Thanks

    Chao

    • Marked as answer by Chao Kuo Friday, April 2, 2010 2:42 AM
    Wednesday, March 31, 2010 1:58 AM

All replies

  • Hello, Diana

    One of the steps include to check if the file which is added already exists in a specified location, ask the user whether he wants to replace the file and either stop the execution of the event or forward it.

    Maybe you have misunderstood IVsTrackProjectDocuments2.OnQueryAddFiles method. This method enables us to determine whether the file can be added to the project.

    We could determine whether the project to be added by assigning the output parameter VSQUERYADDFILERESULTS[] pSummaryResult, if we assign VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddOK, the projectItem will be added, and it will prevent the file to be added if we set VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK.

    So we could ask a dialog to notify the user whether he wants to replace the file.

    Please reference the following implementation of OnQueryAddFiles.

            public int OnQueryAddFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYADDFILEFLAGS[] rgFlags, VSQUERYADDFILERESULTS[] pSummaryResult, VSQUERYADDFILERESULTS[] rgResults)

            {

                //NotifyForm is the mode form that notify the user.

                NotifyForm nForm = new NotifyForm();

     

                if (DialogResult.OK == nForm.ShowDialog())

                {

                    // The file will be added to the project.

                    pSummaryResult[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddOK;

                    rgResults[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddOK;

                }

                else

                {

                    //The file will not be added.

                    pSummaryResult[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK;

                    rgResults[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK;

                }

                return 1;

               

            }

    I expected that the OnQueryAddFiles-event will be fired once;

    I also don’t know why it add a single file each time instead of adding them once in a array.

    But you could use OnAfterAddFilesEx method instead of OnQueryAddFiles, this will only triggered once if you add many files together.

            public int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterAddFilesEx");

                return 1;

            }

     

    Thanks

    Chao

    Friday, March 26, 2010 9:42 AM
  • Hello,

    thanks for the response!

    In VS2010 RC1, the OnAfterAddFilesEx is triggered again for each file, if you add many files together, so no difference between the two events, regarding this.

    In the example you provided, I need to include a "Yes to all", and "No to all" buttons, so that the user does not have to click yes, or no each time, but this is not possible with the current behavior.

     

    Thanks,

    Diana

    Friday, March 26, 2010 2:05 PM
  • Hi,

    In the example you provided, I need to include a "Yes to all", and "No to all" buttons, so that the user does not have to click yes, or no each time, but this is not possible with the current behavior.

    Why this is not possible? You could check the whether the single file is existed, and let the user determine whether to add or not.

    In VS2010 RC1, the OnAfterAddFilesEx is triggered again for each file, if you add many files together, so no difference between the two events, regarding this.

    I also use VS2010 RC1, but my OnAfterAddFilesEx is triggered only once, and the OnQueryAddFiles was triggered for each file. How did you add many files together, do you select many files first and click “Add” to add them?

    Thanks

    Chao

    Monday, March 29, 2010 2:37 AM
  • In the example you provided, I need to include a "Yes to all", and "No to all" buttons, so that the user does not have to click yes, or no each time, but this is not possible with the current behavior .

    Why this is not possible? You could check the whether the single file is existed, and let the user determine whether to add or not.

     - I need to add an "Yes to all" and "No to all"-options in the file-replace dialog, which means that in case the user has selected many files, and wants to replace them all, I should ask only once, for all the files. How can I implement it if a separate event is triggered for each file?               

    I also use VS2010 RC1, but my OnAfterAddFilesEx   is triggered only once, and the OnQueryAddFiles was triggered for each file.

    - Which version of VS2010/SDK are you using? Because in my version the OnAfterAddFilesEx is fired for each file?

    How did you add many files together, do you select many files first and click “Add” to add them?

    - Yes, I right-click on the project, select "Add existing item" then select a couple of files from the file-select dialog and click "Add".

     

    Thanks,

    Diana

    Monday, March 29, 2010 7:31 AM
  • Hello,

    I use Visual studio 2010 ultimate, version is 10.0.30128.1 RC1Rel.

    I need to add an "Yes to all" and "No to all"-options in the file-replace dialog, which means that in case the user has selected many files, and wants to replace them all, I should ask only once, for all the files. How can I implement it if a separate event is triggered for each file? 

    You know “Yes to all” is easy to implement, please see My implementation of IVsTrackProjectDocumentsEvents2, which has implemented the “Yes to all”.

        class DocEvents : IVsTrackProjectDocumentsEvents2

        {

            #region IVsTrackProjectDocumentsEvents2 Members

     

            public int OnAfterAddDirectoriesEx(int cProjects, int cDirectories, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDDIRECTORYFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterAddFilesEx");

                return 1;

            }

     

            public int OnAfterRemoveDirectories(int cProjects, int cDirectories, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSREMOVEDIRECTORYFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterRemoveFiles(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSREMOVEFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterRenameDirectories(int cProjects, int cDirs, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgszMkOldNames, string[] rgszMkNewNames, VSRENAMEDIRECTORYFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterRenameFiles(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgszMkOldNames, string[] rgszMkNewNames, VSRENAMEFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnAfterSccStatusChanged(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, uint[] rgdwSccStatus)

            {

                MessageBox.Show("OnAfterSccStatusChanged");

                return 1;

            }

     

            public int OnQueryAddDirectories(IVsProject pProject, int cDirectories, string[] rgpszMkDocuments, VSQUERYADDDIRECTORYFLAGS[] rgFlags, VSQUERYADDDIRECTORYRESULTS[] pSummaryResult, VSQUERYADDDIRECTORYRESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryAddDirectories");

                return 1;

            }

    //Set a boolean to see whether you have clicked "Yes to all".

            Boolean YesToAll = false;

            public int OnQueryAddFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYADDFILEFLAGS[] rgFlags, VSQUERYADDFILERESULTS[] pSummaryResult, VSQUERYADDFILERESULTS[] rgResults)

            {

                //NotifyForm is the mode form that notify the user.

              

                NotifyForm nForm = new NotifyForm();

                if (!YesToAll)

                {

                    if (DialogResult.Yes == nForm.ShowDialog())

                    {

                        YesToAll = true;

                    }

                    else if (DialogResult.OK == nForm.ShowDialog())

                    {

                        // The file will be added to the project.

                        pSummaryResult[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddOK;

                        rgResults[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddOK;

                    }

                    else

                    {

                        //The file will not be added.

                        pSummaryResult[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK;

                        rgResults[0] = VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK;

                    }

                }

                return 1;

               

            }

     

            public int OnQueryRemoveDirectories(IVsProject pProject, int cDirectories, string[] rgpszMkDocuments, VSQUERYREMOVEDIRECTORYFLAGS[] rgFlags, VSQUERYREMOVEDIRECTORYRESULTS[] pSummaryResult, VSQUERYREMOVEDIRECTORYRESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRemoveDirectories");

                return 1;

            }

     

            public int OnQueryRemoveFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYREMOVEFILEFLAGS[] rgFlags, VSQUERYREMOVEFILERESULTS[] pSummaryResult, VSQUERYREMOVEFILERESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRemoveFiles");

                return 1;

            }

     

            public int OnQueryRenameDirectories(IVsProject pProject, int cDirs, string[] rgszMkOldNames, string[] rgszMkNewNames, VSQUERYRENAMEDIRECTORYFLAGS[] rgFlags, VSQUERYRENAMEDIRECTORYRESULTS[] pSummaryResult, VSQUERYRENAMEDIRECTORYRESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRenameDirectories");

                return 1;

            }

     

            public int OnQueryRenameFiles(IVsProject pProject, int cFiles, string[] rgszMkOldNames, string[] rgszMkNewNames, VSQUERYRENAMEFILEFLAGS[] rgFlags, VSQUERYRENAMEFILERESULTS[] pSummaryResult, VSQUERYRENAMEFILERESULTS[] rgResults)

            {

                MessageBox.Show("OnQueryRenameFiles");

                return 1;

            }

     

            #endregion

        }

    This is the dialog form class

        public partial class NotifyForm : Form

        {

            public NotifyForm()

            {

                InitializeComponent();

            }

            private void button1_Click(object sender, EventArgs e)

            {

                //Yes

                this.DialogResult = DialogResult.OK;

            }

     

            private void button2_Click(object sender, EventArgs e)

            {

                //No

                this.DialogResult = DialogResult.Cancel;

            }

     

            private void button3_Click(object sender, EventArgs e)

            {

                //Yes to all

                this.DialogResult = DialogResult.Yes;

            }

        }

    This is the event registration code snippet.

            private void MenuItemCallback(object sender, EventArgs e)

            {

                IVsTrackProjectDocuments2 trackDoc = GetService(typeof(SVsTrackProjectDocuments)) as IVsTrackProjectDocuments2;

                IVsSolution2 sln = GetService(typeof(SVsSolution)) as IVsSolution2;

                DTE dte = GetService(typeof(SDTE)) as DTE;

                string projUniqueName = dte.Solution.Projects.Item(1).FullName;

     

                IVsHierarchy hierarchy;

                sln.GetProjectOfUniqueName(projUniqueName, out hierarchy);

                IVsProject proj = (IVsProject)hierarchy;

                DocEvents de = new DocEvents();

     

                uint pdwCookie;

                trackDoc.AdviseTrackProjectDocumentsEvents(de, out pdwCookie);

     

            }

    Please look at the code, and is there any difference between your code and mine?

    Thanks

    Chao

    • Marked as answer by Chao Kuo Friday, April 2, 2010 2:42 AM
    Tuesday, March 30, 2010 2:44 AM
  • Thanks again for the response!

    My code looks pretty much like yours, but imagine the following situation:

    the user adds many files to the project, the file-replace-dialog is shown and the user wants to replace them all. So far so gut,  the user closes the dialog. Then after a while the user wants again to add a couple of files to the project. As the Boolean  parameter 'YesToAll ' is already set to true from the previous actions, the file-replace dialog will never appear. This is not the behavior the user expects.

     

    Regards,

    Diana

    Tuesday, March 30, 2010 7:43 AM
  • Hello, Again

    I still don’t know the reason for your OnAfterAddFilesEx was triggered multiple times.

    Because my OnAfterAddFilesEx was triggered only once, and the cFileso is the number of files that were added, so I could reset the YesToAll to false inside this method.

    Just like

    public int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags)

            {

                MessageBox.Show("OnAfterAddFilesEx");

                this.YesToAll = false;

                return 1;

            }

    Have you found the reason for the wrong behavior of your OnAfterAddFilesEx?

    Thanks

    Chao

    • Marked as answer by Chao Kuo Friday, April 2, 2010 2:42 AM
    Wednesday, March 31, 2010 1:58 AM
  • Hello, Diana

    I marked my reply, because the OnQueryAddDirectories and OnAfterAddFilesEx are really behaved like what I have said. And my last reply could resolve the condition you have mentioned.

    If you don’t think it is ok, feel free to unmark it.

    Thanks

    Chao

    Friday, April 2, 2010 2:41 AM