Repository: beeware/toga

Issue:
Asking how to use "file_types" in open_file_dialog
When I bookmark a list like the example below
`    def action_open_file_dialog(self, multi_var, widget=None):
            filenames = self.main_window.open_file_dialog(
                title="Open file with Toga",
                multiselect=multi_var,
                file_types=['gif', 'tiff', 'jpeg', 'bmp', 'png', 'webp']
            )`
The result is a column list, which will prevent the window from displaying all images in the correct format in the list.
How does the window show all the listed formats, instead of having to select files one by one or All files. Similar to the example in HTML.
input type="file"
       id="avatar" name="avatar"
       accept="image/png, image/jpeg"
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file


Discussion:
I'm not sure I understand the question - are you able to provide details about what platform you're on, what you're expecting to see, and what you're actually seeing?
### My code on Pycharm and Windows 10
![image](https://user-images.githubusercontent.com/69602855/96675767-d8020d00-1395-11eb-8b52-cbe535628b4f.png)

### Expected behavior
![image](https://user-images.githubusercontent.com/69602855/96675670-9c674300-1395-11eb-9df5-67f5e51b6dca.png)

### Actual behavior
![image](https://user-images.githubusercontent.com/69602855/96675706-b2750380-1395-11eb-9836-f31a6c5b460d.png)

Ah - now I understand. This looks like it's a limitation of how we've implemented the Winforms file dialog implementation. It's an easy fix though; I'll commit a fix shortly.

Resolving patch:
diff --git a/examples/dialogs/dialogs/app.py b/examples/dialogs/dialogs/app.py
--- a/examples/dialogs/dialogs/app.py
+++ b/examples/dialogs/dialogs/app.py
@@ -43,6 +43,20 @@ def action_open_file_dialog(self, widget):
         except ValueError:
             self.label.text = "Open file dialog was canceled"
 
+    def action_open_file_filtered_dialog(self, widget):
+        try:
+            fname = self.main_window.open_file_dialog(
+                title="Open file with Toga",
+                multiselect=False,
+                file_types=['doc', 'txt'],
+            )
+            if fname is not None:
+                self.label.text = "File to open:" + fname
+            else:
+                self.label.text = "No file selected!"
+        except ValueError:
+            self.label.text = "Open file dialog was canceled"
+
     def action_open_file_dialog_multi(self, widget):
         try:
             filenames = self.main_window.open_file_dialog(
@@ -104,6 +118,11 @@ def startup(self):
         btn_confirm = toga.Button('Confirm', on_press=self.action_confirm_dialog, style=btn_style)
         btn_error = toga.Button('Error', on_press=self.action_error_dialog, style=btn_style)
         btn_open = toga.Button('Open File', on_press=self.action_open_file_dialog, style=btn_style)
+        btn_open_filtered = toga.Button(
+            'Open File (Filtered)',
+            on_press=self.action_open_file_filtered_dialog,
+            style=btn_style
+        )
         btn_open_multi = toga.Button(
             'Open File (Multiple)',
             on_press=self.action_open_file_dialog_multi,
@@ -127,6 +146,7 @@ def startup(self):
                 btn_confirm,
                 btn_error,
                 btn_open,
+                btn_open_filtered,
                 btn_save,
                 btn_select,
                 btn_select_multi,
diff --git a/src/winforms/toga_winforms/window.py b/src/winforms/toga_winforms/window.py
--- a/src/winforms/toga_winforms/window.py
+++ b/src/winforms/toga_winforms/window.py
@@ -203,6 +203,18 @@ def select_folder_dialog(self, title, initial_directory, multiselect):
             raise ValueError("No folder provided in the select folder dialog")
 
     def build_filter(self, file_types):
-        file_string = "{0} files (*.{0})|*.{0}"
-        return '|'.join([file_string.format(ext) for ext in file_types]) + \
-            "|All files (*.*)|*.*"
+        filters = [
+            "{0} files (*.{0})|*.{0}".format(ext)
+            for ext in file_types
+        ] + [
+            "All files (*.*)|*.*"
+        ]
+
+        if len(file_types) > 1:
+            filters.insert(0, "All matching files ({0})|{0}".format(
+                ';'.join([
+                    '*.{0}'.format(ext)
+                    for ext in file_types
+                ])
+            ))
+        return '|'.join(filters)


Test patch:
diff --git a/src/winforms/tests/test_window.py b/src/winforms/tests/test_window.py
--- a/src/winforms/tests/test_window.py
+++ b/src/winforms/tests/test_window.py
@@ -7,6 +7,20 @@ def setUp(self):
         super().setUp()
         self.window = window.Window
 
-    def test_build_filter(self):
+    def test_build_single_filter(self):
         test_filter = self.window.build_filter(None, ["txt"])
-        self.assertEqual(test_filter, "txt files (*.txt)|*.txt|All files (*.*)|*.*")
+        self.assertEqual(
+            test_filter,
+            "txt files (*.txt)|*.txt"
+            "|All files (*.*)|*.*",
+        )
+
+    def test_build_multiple_filter(self):
+        test_filter = self.window.build_filter(None, ["txt", "doc"])
+        self.assertEqual(
+            test_filter,
+            "All matching files (*.txt;*.doc)|*.txt;*.doc"
+            "|txt files (*.txt)|*.txt"
+            "|doc files (*.doc)|*.doc"
+            "|All files (*.*)|*.*",
+        )