-1

EDIT: THIS CODE IS AN UPDATED VERSION USING THE RECOMMENDATIONS BELOW BUT STILL RESULTING IN THE SAME PROBLEM. LINK TO SAMPLE IMAGE OF PROBLEM, [EXAMPLE IMAGE].

This code is meant to update the "AddUserNewUsername" label on a tabitem for creating a new user account for my little program. It use to update the label once the user left the "First Name" and "Last Name" fields. However, after some testing, it looks like the "AddUserNewUsername" string IS being populated, but not updating from blank on the GUI.

I'm really confused. I'm new to binding but I don't really understand how this broke. The only thing I changed was the variable from "NewUsername" to "AddUserNewUsername" and updated everything along with it. Below is a snippet of the relevant code.

MainWindow XAML:

 <Label Name="add_user_uname" Grid.Column="5" Grid.ColumnSpan="6" Grid.Row="7" Content="{Binding Path=AddUserNewUsername, Mode=OneWay}" Style="{DynamicResource add_user_label_uname}"/> <TextBox Name="add_user_finame" Grid.Column="5" Grid.ColumnSpan="6" Grid.Row="2" Text="{Binding Path=AddUserFirstName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource add_user_text_box_std}"/> <TextBox Name="add_user_lname" Grid.Column="5" Grid.ColumnSpan="6" Grid.Row="3" Text="{Binding Path=AddUserLastName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource add_user_text_box_std}"/> 

MainWindow Code Behind:

 public MainWindow() { InitializeComponent(); this.DataContext = id; } public Instance_Data id = new Instance_Data(); 

User_Management Class (Just to show where the message box comes from.):

public static void AttemptUserCreation(MainWindow parent, string finame, string lname, string email, string pword, string verify_pword, string uname, string current_user) { MessageBox.Show(parent.id.AddUserNewUsername); return; 

Instance_Data Class:

public class Instance_Data : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private string _addUserFirstName; private string _addUserLastName; public string AddUserFirstName { get { return _addUserFirstName; } set { _addUserFirstName = value; OnPropertyChanged("AddUserFirstName"); OnPropertyChanged("AddUserNewUsername"); } } public string AddUserLastName { get { return _addUserLastName; } set { _addUserLastName = value; OnPropertyChanged("AddUserLastName"); OnPropertyChanged("AddUserNewUsername"); } } public string AddUserNewUsername { get { if (!String.IsNullOrEmpty(AddUserFirstName)) { return AddUserFirstName[0] + AddUserLastName; } else { return AddUserFirstName; } } } 

add_user_label_uname Style as requested:

 <Style x:Key="add_user_label_uname" TargetType="{x:Type Label}"> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="FontFamily" Value="Tahoma"/> <Setter Property="FontSize" Value="10"/> <Setter Property="Foreground" Value="#B45A00"/> </Style> 

I spent the end of my work day yesterday trying to figure this out and cannot seem to find my hiccup. Hopefully I provided enough data but should you need more just ask! Thank you!

7
  • Where are you setting AddUserLastName and/or AddUserFirstName? Commented Sep 14, 2017 at 14:38
  • Excellent question, sorry about that! I've updated the post to show where those two strings are populated. Commented Sep 14, 2017 at 14:42
  • 1
    The bindings are set to default update mode which is LostFocus meaning they will only write back once you leave the TextBox. If you set UpdateSourceTrigger=PropertyChanged then they will update on typing. Also if AddUserFirstName is null the getter of AddUserNewName will cause an exception. Commented Sep 14, 2017 at 14:44
  • Hmm... Okay that makes sense. Thank you for the pointer for the real time updating! Do you think it's not updating due to some sort of exception with the code under the "AddUserNewUsername" GET code? Commented Sep 14, 2017 at 14:46
  • 1
    @JonnySmall One bug is this: if (AddUserFirstName != "") { return AddUserFirstName[0] + AddUserLastName; -- if AddUserFirstName is null, it isn't an empty string, but you get a null reference exception if you then try to index it. You should check if (!String.IsNullOrEmpty(AddUserFirstName))... Commented Sep 14, 2017 at 14:48

2 Answers 2

3

After much probing, we discovered that a seemingly innocuous line in your C# code was clearing the Binding on your Label:

parent.add_user_uname.Content = ""; 

When you set a dependency property using its set accessor, you set a local value. That value will clear any OneWay bindings that had been applied to the same property.

As a rule, if you are going to be binding against some sort of view model or data model, you should stick to manipulating the model rather than the view that binds to it. Following that approach would have saved you hours of frustrated debugging. On the upside, you won't make that mistake again :).

Dependency properties have many subtleties. Not only will setting a local value fully replace the previous value (including a one-way, source-to-target binding), but it will override values being supplied by setters and triggers too. These subtleties invariably cause some confusion for new WPF developers. If you want to succeed at WPF development, you should dive into the detailed MSDN documentation. I also recommend Adam Nathan's excellent book WPF 4.5 Unleashed. Chapter 3, the most relevant to this subject, used to be available for free online, and it still might be.

As @Iqon points out, you also had a potential NullReferenceException in your AddUserNewUsername accessor

Sign up to request clarification or add additional context in comments.

2 Comments

"That value will clear any bindings" should actually read "... will clear any OneWay bindings". A TwoWay binding would remain in place, and its source property would be set.
@Clemens Good catch. Hadn't thought about that in years, which goes to show that if you follow MVVM or similar practices, you hardly ever have to think about these things :).
0

The only possible way of breaking I see is in the getter of AddUserNewUsername. If AddUserFirstName is null, you will get a NullReferenceException, because you only check if the string is empty.

Change the getter to:

public string AddUserNewUsername { get { if (!String.IsNullOrEmpty(AddUserFirstName)) { return AddUserFirstName[0] + AddUserLastName; } else { return AddUserFirstName; } } } 

20 Comments

Since this is WPF and as such clearly post .NET 2.0, there is no object created for "" and it both is treated identically.
@JonnySmall Let's see the code for your add_user_label_uname style.
@EdPlunkett Located in a User_Management class which handles the creation, editing, and deletion of the users. The user creation method is really long but I'll add the top of it where the message box is created to the post.
@JonnySmall Let's step back for a moment and verify that you can see your label at all. Remove the Binding and set some explicit Content. Does that content show up where you expect? Also, you added UpdateSourceTrigger=PropertyChanged to your Label binding, but it makes no sense there. It needs to be on both text boxes.
Not manipulating, no. The only time it's used is when it's passed to the User_Management class to be available for the user creation process. EDIT: WAIT WAIT THERE IS! In my UI_Navigation class I have a method for clearing all data from a tab when exiting, which has ` parent.add_user_uname.Content = "";` in it.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.