My Class Property is not updated from TextBox Text Binding in WPF

c-sharp
xaml

(afree) #1

I have this small data class in C#

    public class ApiSettings
    {
        public string ApiKey { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string Path { get; set; }
    }

And I have this TextBox in my WPF as defined in xaml:

<Grid Margin="0,10,0,0" Name="pnl_api">
	<Label Style="{StaticResource SettingsTitleLabelStyle}" Content="Remote Server Credentials" Margin="20,-2,0,0" />
	<Label Content="API Key" Margin="45,36,0,0" FontSize="12.07" />
	<TextBox x:Name="tb_api_key" Style="{StaticResource RoundTextBoxStyle}"  Text="{Binding ApiKey}" 
			 Margin="106,30,0,0" Width="200" TextChanged="tb_server_TextChanged" />
	<Label Content="User" Margin="64,69,0,0" FontSize="12.07" />
	<TextBox x:Name="tb_api_user" Style="{StaticResource RoundTextBoxStyle}" Margin="106,62,0,0" 
			 Text="{Binding Username}" Width="200" TextChanged="tb_server_TextChanged" />
	<Label Content="Password" Margin="36,100,0,0" FontSize="12.07" />
	<PasswordBox x:Name="tb_api_password" Style="{StaticResource RoundPasswordBoxStyle}" Margin="106,94,0,0" Width="200" />
	<Label Content="Path" Margin="64,134,0,0" FontSize="12.07" />
	<TextBox x:Name="tb_api_path" Style="{StaticResource RoundTextBoxStyle}" 
			 Text="{Binding Path}" Margin="106,127,0,0" Width="200" />
</Grid>

The problem here is all other bound TextBox’s and PasswordBox are working except the tb_api_path

<TextBox x:Name="tb_api_path" Style="{StaticResource RoundTextBoxStyle}" Text="{Binding Path}" Margin="106,127,0,0" Width="200" />

The crazy thing is this TextBox’s are all defined the same way, i tried changing the name, the property and always the same thing, the binding for the last TextBox in the list doesn’t work at all.

Any help please?


(Yassine) #2

Here is a little hint @afree, I guess when writing into those TextBox’s you actually do it from the top most one and going down, thus the last one to get your changes is always the bottom most TextBox.

Now can you try to do it in reverse, Write something (test values) into TextBox’s from the Bottom most TextBox, and going up to the Top Most one.

What did you notice?
I believe now you found out, that not the position of the Textbox that affects whether its changes are being applied back to the source or not, but in fact whether or not the TextBox is the last one to get your Changes.

And the problem in reality is in all your TextBox’s’ binding:

<TextBox x:Name="tb_api_path" Style="{StaticResource RoundTextBoxStyle}" 
		 Text="{Binding Path}" Margin="106,127,0,0" Width="200" />

Your bindings are so simple format, only binding to the property (in the above case ‘Path’), The binding in the above case has all other properties set to default values except the one set explicitly ‘Path’.

The Binding property that you missed was Binding.UpdateSourceTrigger It’s set to Default by default.

Which means the TextBox Binding source wont be updated unless the TextBox losses input focus.

That finally explains the behavior you had.

Solution

Simply set the Binding property UpdateSourceTrigger to PropertyChanged, That will make sure whenever your TextBox Text changes the source will be updated.

Here is a XAML snippet:

<TextBox x:Name="tb_api_path" Style="{StaticResource RoundTextBoxStyle}" 
		 Text="{Binding Path, UpdateSourceTrigger=PropertyChanged}" Margin="106,127,0,0" Width="200" />

And you will have to do the same for all other Textbox’s in your code:

<Grid Margin="0,10,0,0" Name="pnl_api">
	<Label Style="{StaticResource SettingsTitleLabelStyle}" Content="Remote Server Credentials" Margin="20,-2,0,0" />
	<Label Content="API Key" Margin="45,36,0,0" FontSize="12.07" />
	<TextBox x:Name="tb_api_key" Style="{StaticResource RoundTextBoxStyle}"  Text="{Binding ApiKey, UpdateSourceTrigger=PropertyChanged}" 
			 Margin="106,30,0,0" Width="200" TextChanged="tb_server_TextChanged" />
	<Label Content="User" Margin="64,69,0,0" FontSize="12.07" />
	<TextBox x:Name="tb_api_user" Style="{StaticResource RoundTextBoxStyle}" Margin="106,62,0,0" 
			 Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" Width="200" TextChanged="tb_server_TextChanged" />
	<Label Content="Password" Margin="36,100,0,0" FontSize="12.07" />
	<PasswordBox x:Name="tb_api_password" Style="{StaticResource RoundPasswordBoxStyle}" Margin="106,94,0,0" Width="200" />
	<Label Content="Path" Margin="64,134,0,0" FontSize="12.07" />
	<TextBox x:Name="tb_api_path" Style="{StaticResource RoundTextBoxStyle}" 
			 Text="{Binding Path, UpdateSourceTrigger=PropertyChanged}" Margin="106,127,0,0" Width="200" />
</Grid>

(Kunni Can) #3

Seems like @Yassine has got everything convered in his very long Post, but if you are kind of people that are too lazy to read that;

Here is what you should do:

Replace this:

{Binding YourProperty}

By this:

{Binding YourProperty, UpdateSourceTrigger=PropertyChanged}

To force WPF to update your source property to update every time the Bound property (- in your case TextBox.Text) is updated by users. Instead of the default behavior of updating it only when focus is lost.

hope that makes sense to you