0

MainWindow.xaml

<Window x:Class="SDT.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        mc:Ignorable="d"
        xmlns:viewModels="clr-namespace:SDT.ViewModels"
        Height="500" Width="700" WindowStyle="None" AllowsTransparency="False" ResizeMode="NoResize" Background="#FF2C2C2C"
        TextElement.Foreground="{DynamicResource MaterialDesignBody}" TextElement.FontWeight="SemiBold">

    <Window.DataContext>
        <viewModels:UserViewModel />
    </Window.DataContext>

    <Grid>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="308,90,0,0" TextWrapping="Wrap" Text = "{Binding Login}" VerticalAlignment="Top" Width="120"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="152,200,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
        <Button Content="Submit" Command="{Binding SubmitLoginDataCommand}" HorizontalAlignment="Left" Margin="567,259,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>

MainWindows.cs

public partial class MainWindow : Window
{
    UserViewModel userViewModel = new UserViewModel();
    public MainWindow()
    {
        InitializeComponent();
        DataContext = userViewModel;

    }
}

UserViewmodel

public class UserViewModel : INotifyPropertyChanged
{
    private UserService userService = new UserService();

    public string _firstName;

    public string Login { get; set; }

    public void SubmitLoginData(object loginData)
    {
        userService.CheckUserExist(Login);
    }

    public ICommand SubmitLoginDataCommand => new RelayCommand(SubmitLoginData, param => true);

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName != value)
            {
                _firstName = value;
                OnPropertyChanged("FirstName");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

Hello. What is wrong with FirstName binding? Textbox shows nothing. public string FirstName{} - FirstName here have value in debugging. I tried without Window.DataContext and only with Text="{Binding FirstName}" but without success. Login binding working fine.

4
  • I once had a similar problem that was caused by an invisible character. If you comment out/delete the offending line, copy the line for the login binding and simply change the binding from "Login" to "FirstName", do you still get the same problem? Commented Feb 5, 2020 at 9:15
  • 1
    By the way, if you use OnPropertyChanged(nameof(FirstName)) instead of hard-coding the property name, your code will be less error prone. Commented Feb 5, 2020 at 9:23
  • @Frauke Unfortunately, no change Commented Feb 5, 2020 at 9:26
  • try initialize the properties in constructor of userviewmodel and remove the data context code from code behind. Commented Feb 5, 2020 at 11:22

3 Answers 3

1

You need to remove from MainWindow.xaml this part:

<Window.DataContext> <viewModels:UserViewModel /> </Window.DataContext>

It becouse you have Twice DataContext, In xaml and in cs so it is not know from where take the data.

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

Comments

1

I wanted to post a second answer to make a suggestion that I think you'll really like. We can have your OnPropertyChanged event automatically name the property, allowing you to just write "OnPropertyChanged()" to trigger the UI update.

To do this, we're going to use a property called "Caller Member Name" - which does what you'd think - provides the name of the object or property that's called the code!

To use this, we need to add a using statement to the top of your UserViewModel class:

using System.Runtime.CompilerServices;

Then, we will modify your OnPropertyChanged event to use the 'caller member name' unless you specify a specific name. It should look like this now:

    private void OnPropertyChanged([CallerMemberName] String name = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

Now - we will update your property to use the simplified method:

   public string FirstName
{
    get { return _firstName; }
    set
    {
        if (_firstName != value)
        {
            _firstName = value;
            OnPropertyChanged();
        }
    }
}

Lastly - I've recently learned an alternative way for get/set's that i prefer. What you're doing is completely OK and there is no need to change it, but i'd suggest trying it to see how you like it :)

    public string FirstName
    {
        get => _firstName; 
        set
        {
            if (_firstName == value) return;
            _firstName = value;
            OnPropertyChanged(); 
        }
    }

Reasons: I find it quicker to press == instead of !=, less brackets. If first name equals value it will simply return (exit). If not, it skips that return! I love that!

Comments

1

Let's test your binding! Let's add a text block to your form, and bind the FirstName property to it. Whatever you enter in the Textbox should be displayed in the textblock if your binding is working correctly.

Your MainWindow.xaml should look something like this:

<Window x:Class="SDT.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModels="clr-namespace:Junk.cats"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">

<Grid>
    <TextBox HorizontalAlignment="Left" Height="23" Margin="308,90,0,0" TextWrapping="Wrap" Text = "{Binding Login}" VerticalAlignment="Top" Width="120"/>
    <TextBox HorizontalAlignment="Left" Height="23" Margin="152,200,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
    <Button Content="Submit" Command="{Binding SubmitLoginDataCommand}" HorizontalAlignment="Left" Margin="567,259,0,0" VerticalAlignment="Top" Width="75"/>
    <TextBlock HorizontalAlignment="Left" Height="32" Margin="140,247,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="346"> 
       <Run Text="First Name: "/>
       <Run Text="{Binding Path=FirstName}"/>
    </TextBlock>
</Grid>

I expect that this test will work, and you're going to see that you're not having an issue with your get/set properties and UI updates. I believe your issue is now with the 'instance' (copy) of the UserViewModel.

Let's pretend we're working with a printed document. When you use the = new UserService(); assignment, you're printing a fresh copy of our document. If we print a new document and give it to MainWindow.cs (Let's call it "Bob"), AND you then print a new copy in your userService code (Let's call this "Frank") - these are two independent instances / copies of the document.

We need to make this object once, and tell "Bob" and "Frank" to work with the same copy of the object. Don't worry, this is easier than you think, and you'll start getting used to it as you use it.

I'm going to use some STATIC fields to simplify your troubleshooting - you do not need to create a static instance to make this work, but you do need to make sure your instance of the shared class is available to whoever needs it.

Step 1 - Create a new class, let's call it 'Views'. Step 2 - Make the class public static Step 3 - Create a Public static userViewModel here:

public static class views
{
    public static UserViewModel userViewModel = new UserViewModel();
}

Now - Let's change your MainWindow.cs to use the shared instance of this class:

    public partial class MainWindow : Window
{


    public MainWindow()
    {
        InitializeComponent();
        DataContext = views.userViewModel;

    }
}

The last thing you need to do - Make your external function work with the same copy of the 'userViewModel'! I don't have that code from you, so I'm pretending your function is called 'YourFunctioNToChangeTheName', and it's located in your 'UserService' class:

    public class UserService
{
   public void YourFunctionToChangeTheName()
    {
        views.userViewModel.FirstName = "FRANK"; 
    }
}

The key thing to spot here is that you're not creating a new "UserViewModel" - you're re-using the same instance that the MainWindow.cs is bound to - so the UI is getting a 'property changed notification' now!

Remember, the UserViewModel (class) itself isn't static, we've created a shared / static instance of it that can be accessed from anywhere in your program. I suggested this approach so that you can learn the basics of an instance :)

Good luck!!

2 Comments

Almost there but still so far. Yes, _firstName is private. I just forgot to change but doesn't work. Second answer too.
@Hoshie let me know if this worked for you - your binding works fine, i believe you're having trouble updating from an external class :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.