Repository: beeware/toga

Issue:
toga-winforms v0.3.0.dev38 gives Error on Windows app
Toga-winforms v0.3.0.dev38 gives AttributeError: 'TextInput' object has not attribute '_on_change'

**To Reproduce**
1. Create an app that uses toga.TextInput(),
2. Set toga-winforms==0.3.0.dev38 under Windows requirements in pyproject.toml file.
3. Update dependencies: `update -d`
4. Build and run app on Windows

**Screenshots**
![attribute_error](https://user-images.githubusercontent.com/6691022/194653140-a06a8ef9-cab7-41a5-907e-b5126a2e8507.PNG)

**Environment:**
 - Operating System: Windows 10
 - Python version: 3.8.10
 - Software versions:
   - Briefcase: 0.3.9
   - Toga: 0.3.0.dev38



Discussion:
Thanks for the report. 

As a clarification: the error only manifests if the widget is constructed with a `value` argument. If the input is constructed empty, this error doesn't occur. As a result, the `textinput` example works as written; but if you add a `value=` argument to one of the inputs, you get this error on startup.



Resolving patch:
diff --git a/examples/textinput/textinput/app.py b/examples/textinput/textinput/app.py
--- a/examples/textinput/textinput/app.py
+++ b/examples/textinput/textinput/app.py
@@ -13,12 +13,14 @@ class TextInputApp(toga.App):
     def do_extract_values(self, widget, **kwargs):
         # Disable all the text inputs
         self.text_input.enabled = False
+        self.text_input_placeholder.enabled = False
         self.password_input.enabled = False
         self.number_input.enabled = False
 
         # Update the labels with the extracted values
-        self.text_label.text = "Text content: {}".format(
-            self.text_input.value
+        self.text_label.text = "Text content: {}; {}".format(
+            self.text_input.value,
+            self.text_input_placeholder.value,
         )
 
         self.password_label.text = "Your password is {}: {}".format(
@@ -40,6 +42,7 @@ def do_extract_values(self, widget, **kwargs):
 
         # Renable the inputs again.
         self.text_input.enabled = True
+        self.text_input_placeholder.enabled = True
         self.password_input.enabled = True
         self.number_input.enabled = True
 
@@ -60,6 +63,10 @@ def startup(self):
 
         # Text inputs and a button
         self.text_input = toga.TextInput(
+            value='Initial value',
+            placeholder='Type something...', style=Pack(padding=10)
+        )
+        self.text_input_placeholder = toga.TextInput(
             placeholder='Type something...', style=Pack(padding=10)
         )
         self.password_input = toga.PasswordInput(
@@ -91,6 +98,7 @@ def startup(self):
             children=[
                 self.label,
                 self.text_input,
+                self.text_input_placeholder,
                 self.password_input,
                 self.password_content_label,
                 self.email_input,
diff --git a/src/core/toga/widgets/textinput.py b/src/core/toga/widgets/textinput.py
--- a/src/core/toga/widgets/textinput.py
+++ b/src/core/toga/widgets/textinput.py
@@ -70,7 +70,10 @@ def __init__(
         self.readonly = readonly
 
         # Set the actual value before on_change, because we do not want on_change triggered by it
+        # However, we need to prime the handler property in case it is accessed.
+        self._on_change = None
         self.value = value
+
         self.on_change = on_change
         self.validators = validators
         self.on_lose_focus = on_lose_focus


Test patch: