-1

I am running into a peculiar problem with MVVM and WPF application. I will explain it briefly to make it as clear as possible.

First of all I have binded the text of a TextBlock property in the XAML file

XAML

<TextBlock
    x:Name="ProjectCredentials"
    Text="{Binding Path=HeaderName}"/>

View Model - created in MainWindow

namespace Test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _tabitemHeader;
        public string HeaderName
        {
            get
            {   
                return _tabitemHeader;
            }
            set
            {
                _tabitemHeader = value;
                OnPropertyChanged("HeaderName");
            }
        }
    }
    // Continue in next code block
}

Below is the LoginScreen Window or Window 1 from where I want to give value to my TextBlock.Text property.

namespace Test
{
    public partial class LoginScreen : Window
    {
        public LoginScreen()
        {
            InitializeComponent();
        }

        private void LoginButton_Click(object sender, RoutedEventArgs e)
        {
            MainWindow win2 = new MainWindow();

            win2.DataContext = new MainWindow.MainWindowViewModel();

            MainWindow.MainWindowViewModel object_change = win2.DataContext as MainWindow.MainWindowViewModel;

            object_change.HeaderName = "Project Name: " + SQLSeverName.Text + " - " + SQLDatabasesComboBox.SelectedItem;
            Debug.WriteLine(object_change.HeaderName);
            //Successfully returns the string
        }
    }
}

Now that I have given the value "Project Name: ..." I should call this string in MainWindow inside the method GetHeaderDetails()

namespace Test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _tabitemHeader;
        public string HeaderName
        {
            get
            {   
                return _tabitemHeader;
            }
            set
            {
                _tabitemHeader = value;
                OnPropertyChanged("HeaderName");
            }
        }
    }
    public List<string> GetHeaderDetails()
    {
        string projectdetails_option1 = ?.HeaderName; //how can  I call the HeaderName of MainWindow DataContext which is already filled from LoginScreen window?

        Debug.Assert(projectdetails_option1 != null, "Project name not found!"); //this returns null

        string connectiondetails = projectdetails_option1.Split(": ")[1];

        string servernamedetails = connectiondetails.Split(" - ")[0].Trim();
        string projectnamedetails = connectiondetails.Split(" - ")[1].Trim();

        List<string> connectiondetailslist = new List<string>();
        connectiondetailslist.Add(servernamedetails);
        connectiondetailslist.Add(projectnamedetails);

        return connectiondetailslist;
    }

    public List<string> ConnectionDetailsList { get => GetHeaderDetails(); set => ConnectionDetailsList = value; }
}

The problem here is how can I call the HeaderName of MainWindow DataContext which is already filled from the LoginScreen window?

I know that if I open a new instance of the MainWindow DataContext in the GetHeaderDetails() method I will get a NullReferenceException because the HeaderName will be lost and a new DataContext will be introduced. So I want to find a way to bypass this.

Please note that I am aware of the NullReferenceException. Although, I am looking for answers that may help me to understand how to locate the problem.

Regards.

DelusionX
  • 79
  • 8
  • Might just as well post the rest of the code as one full chunk vs splitting it. I looks like unbalanced curly brackets in first, with the nested class within the MainWindow class. Don't know if intentional or not. – DRapp Oct 22 '20 at 21:21
  • 2
    In both of the relevant methods, **you create a new view model before attempting any access of the `HeaderName` property**. Why _shouldn't_ it always return `null` for the value, given that in every single case, you are always dealing with a completely new instance of the view model object? – Peter Duniho Oct 22 '20 at 21:24
  • @DRapp I added the two code blocks as per your comment. Hope it's not confusing. – DelusionX Oct 22 '20 at 21:28
  • @PeterDuniho How can I call the MainWindow.MainWindowViewModel() without first calling a new instance of it? I guess I miss something here. But if I call this ```MainWindow.MainWindowViewModel.HeaderName = "Project Name: " + SQLSeverName.Text + " - " + SQLDatabasesComboBox.SelectedItem;``` in the ```LoginScreen``` window I get an Object Reference exception for method non-static field. – DelusionX Oct 22 '20 at 21:30
  • 2
    _"How can I call the MainWindow.MainWindowViewModel() without first calling a new instance of it?"_ -- the same way everyone else does. Just don't create a new instance. You should be setting the `DataContext` property _once_, during the initialization of the view (window) itself. There's no need to set that property again, and doing so completely replaces whatever view model object you already had there, throwing away any values it might have stored. – Peter Duniho Oct 22 '20 at 21:32
  • 1
    Your MVVM is incomplete and broken. You shouldn't mix it with Button_Click etc. Find a decent MVVM library or tutorial to learn how/when to create a ViewModel. – H H Oct 23 '20 at 12:00
  • You have a ViewModel and a View but this is by no means an MVVM. You need to instantiate it ViewModel in xaml, this ensures it is created during init of the window. Then all of the properties bindings will work. I also skipped most of your question as it is just too long to read. – XAMlMAX Oct 23 '20 at 12:08
  • @HenkHolterman I have search about MVVM light so I will try to use this. However, regarding the Button_Click don't I have to go like this? Because in my head I want to set the property of the TextBlock when the button is clicked. Why is this wrong? Maybe because I make the Code dependent to the View? – DelusionX Oct 23 '20 at 12:11
  • @XAMlMAX ty for the comment. Indeed the question is long as long as you consider the updates. But I had to write them because I had negative votes. Also, I think that I instantiate the ViewModel in my updated code snippet. Could you please check it out? – DelusionX Oct 23 '20 at 12:14
  • 1
    Your updated code sets the same vm to login and main window windows. Unless that's what you want, then this should work for data context property. As to click event handler, look into RelayCommand or DelegateCommand. Change that propert in your view model instead of your code behind. – XAMlMAX Oct 23 '20 at 12:31
  • @XAMlMAX Thank you for the comment. The problem in my case is that when I click the Login Button, the property ```MainWindowViewModel.HeaderName``` gets successfully the string text I want. The null exception error is when I move to MainWindow screen where I use again the text of ```MainWindowViewModel.HeaderName``` but this time it's null. So somehow it's loosing the assigned string text value at the transition from LoginScreen to MainWindow DataContext. That's what I am trying to solve. In any case I will search more about MVVM. – DelusionX Oct 23 '20 at 12:41
  • 2
    People have already explained why your value isn't there. It's a new instance of mainwindowviewmodel you're looking at. You need just one. You have multiple instances. – Andy Oct 23 '20 at 16:18

1 Answers1

0

I have figured it out, finally. To retrieve the value of the TextBlock.Text value in MainWindow, the only thing that I had to do is:

Instead of this:

string projectdetails_option1 = ?.HeaderName;

I tried this:

string projectdetails_option1 = ProjectCredentials.Text; //The TextBlock that got its value from LoginScreen.

And successfully I retrieved the value of the ProjectCredentials textblock, without opening a new instance of the DataContext.

DelusionX
  • 79
  • 8