2

I'm trying to make a 'Save As' dialog with an event that would change the default path based on the type of file we choose from the filters combo box. The problem is, all the examples I've seen execute the code on result IDOK or IDCANCEL while I'd need the code to be executed while the dialog is still opened.

Also, is there any way to differentiate between what filter has been chosen if the filters have the same type? The GetFileExt() method just returns the extension but I have no way of telling if it was the first .my filter or the template .my filter.

I've seen something like LPOFNHOOKPROC but there was no example of how would I even use it and I'm not sure whether it would even solve my problem or not.

void CMyClass::OnFileOpen()
{
   CString pathNam;
   CString fileName;

   TCHAR szFilters[]= _T("MyType Files (*.my)|*.my|Template MyType (*.my)|*.my||");

   CFileDialog fileDlg(TRUE, _T("my"), _T("*.my"),
   OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters);

   if(fileDlg.DoModal() == IDOK)
   {
       pathName = fileDlg.GetPathName();
       fileName = fileDlg.GetFileTitle();
   }
}

EDIT:

I am now able to get the specific filter that's been chosen by getting the OFN and checking the nFilterIndex value. So the remaining problem is whether I can update the path based on the chosen file format?

EDIT2:

I've found the OnChangeType method and overloaded it in the subclass and it indeed executes the method and the code within, but when I try to update the file path I get an access violation:

void TFileDialogExt::OnTypeChange()
{
   LPWSTR buff = L"C:\\TEST\\template.my";

   if(m_pOFN->nFilterIndex == 2)
      m_ofn.lpstrFile = buff;    
}
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
bananeeek
  • 153
  • 1
  • 2
  • 11
  • 1
    I have never seen this functionality in any software. Your users may be confused with it. Why not make them choose the type before the file dialog is open? – Dialecticus Jul 01 '19 at 08:00
  • @Dialecticus That was the way I was doing it until now, but I had a problem with the old dialog the FolderBrowserDialog, and I opted to use the new dialog. But then the intermediary dialog where I used to choose the type, the path and the file name was redundant as I can choose everything in the CFileDialog. Is there truly no such functionality in the CFileDialog? – bananeeek Jul 01 '19 at 08:09
  • Make them choose only the type, and then in file dialog allow only that one type. If they want another type they have to close the file dialog and choose another. I do not know if what you want can be achieved. What I am saying is that I have never seen it before. – Dialecticus Jul 01 '19 at 08:23
  • @Dialecticus At least I was able to find the way to check the specific filter selected by checking the nFilterIndex value in the OFN. It's weird that this dialog has no such events... – bananeeek Jul 01 '19 at 08:32
  • Updating the path according to the chosen extension looks like a very bad idea to me. – Jabberwocky Jul 01 '19 at 09:10
  • Look at the [`CFileDialog::OnTypeChange`](https://learn.microsoft.com/en-us/cpp/mfc/reference/cfiledialog-class#ontypechange) event. But what do you mean by "change the default path"? You may be able to use a little hooking to change the filename displayed based on the type of file chosen, but you can't change the currently selected folder. Can you provide an example of what you are trying to achieve exactly? – Remy Lebeau Jul 01 '19 at 09:20
  • @RemyLebeau When I used the FolderBrowserDialog, before it was opened, I had a simple wpf dialog where I'd choose file path, name and type, and I've had different default paths for the two different types, so once I chose the specific type, I then opened the dialog and the default path was loaded. But now I want to get rid of that intermediary dialog and update the path once the type is changed while the CFileDialog is opened. I hoped there would be some sort of event that would let me change the folder if the filter is changed – bananeeek Jul 01 '19 at 12:31
  • @Jabberwocky It's just that I have two default locations for two different types of files – bananeeek Jul 01 '19 at 12:33
  • @bananeeek I undertand, it's just like a kind of unusual interaction. – Jabberwocky Jul 01 '19 at 12:39
  • 1
    @bananeeek I still don't understand the work flow you are trying to accomplish exactly. But your latest edit crashes because you are setting `m_ofn.lpstrFile` to point at read-only memory. The next time the dialog tries to copy a new path string into the `m_ofn.lpstrFile` buffer, BOOM! You can't change the buffer that `m_ofn.lpstrFile` points to, but you can copy your own strings into it. But that does not guarantee that the dialog will update to display them. You may have to update the dialog UI manually, and that is harder to do in Vista-style dialogs than in older dialogs – Remy Lebeau Jul 01 '19 at 16:28
  • I have just added an answer with part of a working solution. I think it is going in the right direction. – Andrew Truckle Jan 02 '23 at 14:12

2 Answers2

2

Basically you have to subclass CFileDialog and handle its CFileDialog::OnTypeChange method.

But, as suggested by Microsoft: you'd better use a new Common Item Dialog instead.

Jovibor
  • 759
  • 2
  • 6
  • 16
  • 4
    Current version of `CFileDialog` already uses Common Item Dialog internally. – zett42 Jul 01 '19 at 10:13
  • I've already derived from the CFileDialog class and overloaded the OnTypeChange method but when I try to update the file path I get an exception about an access violation – bananeeek Jul 01 '19 at 12:23
  • @bananeeek because the code you wrote for that is wrong. See my other comment about that – Remy Lebeau Jul 01 '19 at 16:29
  • It is not possible to provide a worked example? This would improve your answer. Overriding the event handler is easy, but working out how to change folder seems trickier. – Andrew Truckle Jan 02 '23 at 02:33
1

I did some research about this and found some useful questions:

Also, have a look at: SHCreateItemFromParsingName.


Here is a sample OnTypeChange handler:

void CMyFileDialog::OnTypeChange()
{
    {
        IFileOpenDialog* pfod = NULL;
        HRESULT hr = (static_cast<IFileDialog*>(m_pIFileDialog))->QueryInterface(IID_PPV_ARGS(&pfod));
        if (SUCCEEDED(hr))
        {
            IShellItem* psiInitialDir;

            CString strFolder = L"d:\\";
            hr = SHCreateItemFromParsingName(strFolder.GetString(), NULL, IID_PPV_ARGS(&psiInitialDir));
            if(SUCCEEDED(hr))
            {
                pfod->SetFolder(psiInitialDir);
            }
        }
    }

    CFileDialog::OnTypeChange();
}

My code uses a hard coded path for testing purposes, but you should now be able to complete your code:

  1. Determine which path you want to use based on the currently selected filter index.
  2. Use similar logic as here to navigate to that folder.
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164