0

My main goal here is to change the sidebar text after the user UPDATES the username.

My sidebar text (the one that displays the username) is housed in my MainWindow.xaml:

<Window //namespaces and properties here  
        WindowStyle="None"
        AllowsTransparency="True"
        Background="Transparent">

    <Border Background="#222529"
            CornerRadius="20">
        <Grid>
        
            <!--Removed some UI elements for simplicity-->

            <!--I have a sidebar menu here which I can navigate-->

            <!--Username (The text that I want to change after updating User's username-->
            <TextBlock Text="{Binding CurrentUserAccount.Username}" />

            <!--Where the views are going to be displayed-->
            <ContentControl Content="{Binding CurrentView}" />
            
        </Grid>
    </Border>
</Window>

This is my UserAccountView.xaml, and I update User details here

<UserControl //namespaces and properties
             Background="Transparent">
    <Grid HorizontalAlignment="Center">
             <!--Removed some UI elements for simplicity-->
             
            <!--Username Textbox-->
            <TextBox Style="{StaticResource TextBoxTheme}"
                     Text="{Binding CurrentUserAccount.Username}"/>

           
            <!--Password Textbox-->
            <usercontrols:BindablePasswordBox Style="{StaticResource BindablePasswordBoxTheme}"
                                              Password="{Binding CurrentUserAccount.Password}" />

            <!--Update Profile Button-->
            <Button Content="Update Profile"
                    Style="{StaticResource GeneralButtonTheme}"
                    Command="{Binding UpdateCommand}" />

            <!--Check current user details (just a temporary button to check user details)-->
            <Button Content="Check details"
                    Style="{StaticResource GeneralButtonTheme}"
                    Command="{Binding CheckDetailsCommand}" />
        </Grid>
    </Grid>
</UserControl>

I know that the User's details are Updated because of the temporary button that can check the user's details (and of course, the user details change when I login again). This is how I implement the check:

MessageBox.Show($"Id: {CurrentUserAccount.Id} 
                \nUsername: {CurrentUserAccount.Username} 
                \nPassword: {CurrentUserAccount.Password} 
                \nDate Created: {CurrentUserAccount.DateCreated}");

This is how I navigate through my menu (if this is relevant to the question)

class MainWindowViewModel : BaseViewModel
{
    public ICommand DashboardViewCommand { get; }
    public ICommand ProfileViewCommand { get; }
   // and other commands for each View
   
    public DashboardViewModel DashboardVM { get; set; }
    public UserAccountViewModel ProfileVM { get; set; }
   // and other ViewModels 

    public object CurrentView { get; set; }
    public UserAccount CurrentUserAccount { get; set; }

    public MainWindowViewModel(UserAccount currentUserAccount)
    {
        CurrentUserAccount = currentUserAccount;

        DashboardVM = new DashboardViewModel();
        ProfileVM = new UserAccountViewModel(currentUserAccount.Username);

        CurrentView = DashboardVM;

        DashboardViewCommand = new NavigationCommand(o => { CurrentView = DashboardVM; });
        ProfileViewCommand = new NavigationCommand(o => { CurrentView = ProfileVM; });
    }
}

The DataTemplate of my Views (in App.xaml):

<Application.Resources>
    <ResourceDictionary>

        <!--Binding the View to its ViewModel-->

        <!--Dashboard-->
        <DataTemplate DataType="{x:Type viewModel:DashboardViewModel}">
            <view:DashboardView />
        </DataTemplate>

        <!--User Account (if I share ViewModels and remove this, then I can't see my View now)-->
        <DataTemplate DataType="{x:Type viewModel:UserAccountViewModel}">
            <view:UserAccountView />
        </DataTemplate>

    </ResourceDictionary>
</Application.Resources>

How is it possible to change the value of an element from a Window if I am "changing" if from a ViewModel (that is is a ContentControl)?

I have to ask this question since I have found nothing similar about this problem, and I don't really know what "Keywords" I should look up to, since this is my first time creating a WPF application.


EDIT: I already solved this problem thanks to @Clemens' comment (which gave me an idea on what to search), I was able to share ViewModels for my main page and my UserAccountView by removing the DataContext for my UserAccountView and set the DataContext at the UserControl level:

<!--this is what my UserAccountView looks like-->
<UserControl <!--properties and namespaces-->
             DataContext="{Binding Path=DataContext, 
          RelativeSource={RelativeSource AncestorType={x:Type Window}}}">

ANOTHER EDIT : I found another way of doing this by separating the MainWindowViewModel and UserAccountViewModel so that the MainWindowViewModel (where my UserAccount related code stays, because of having a "shared" ViewModel) will not get messy.

This is what my MainWindowViewModel looks like now

class MainWindowViewModel : BaseViewModel
{
    public ICommand DashboardViewCommand { get; }
    public ICommand ProfileViewCommand { get; }
   
    public DashboardViewModel DashboardVM { get; set; }
    public UserAccountViewModel ProfileVM { get; set; }

    public object CurrentView { get; set; }

    public MainWindowViewModel(UserAccount currentUserAccount)
    {
        DashboardVM = new DashboardViewModel();

        //this is the change (the UserAccountViewModel now accepts an instance of the useraccount, 
        //instead of the user's name, so when I change a property of that instance, 
        //it will be reflected to the binding text of the sidebar as well)
        ProfileVM = new UserAccountViewModel(currentUserAccount);

        CurrentView = DashboardVM;

        DashboardViewCommand = new NavigationCommand(o => { CurrentView = DashboardVM; });
        ProfileViewCommand = new NavigationCommand(o => { CurrentView = ProfileVM; });
    }
}

Now all code related about UserAccount will all be done in UserAccountViewModel instead of in the MainWindowViewModel.

paraJdox1
  • 805
  • 9
  • 23
  • 1
    As usual, the Window and the UserControil should share a common view model. A UserControl must never have its own private view model. – Clemens Jun 01 '21 at 06:56
  • wait... all of my "Views" have their own ViewModels, and all of my "View" are UserControl. so does this mean that all of my logic will be in one big ViewModel? – paraJdox1 Jun 01 '21 at 07:33
  • Or in child view models of a main view model. Private view models of controls do not know each other and can hence not exchange data. – Clemens Jun 01 '21 at 07:49
  • Wait. I'm sorry. but I don't understand the meaning of what you said. I am just embarking in wpf and mvvm and I still don't know the ins and out of things and I find your reply confusing. – paraJdox1 Jun 01 '21 at 07:55
  • Well, start with a single view model class and a single instance of it assigned to the DataContext of the top level element, i.e. the Window. Do not explicitly set the DataContext of the UserControls and just let them bind to the main view model. There is your communication. – Clemens Jun 01 '21 at 07:58
  • so if they "share" viewmodels now. how can I navigate to my UserAccount `UserControl` (my view)? as you can see in my question above, I navigate to each view (UserControl) using a `DataTemplate` that is in my App.xaml (I'll update the question so you can see my App.xaml) – paraJdox1 Jun 01 '21 at 09:15
  • The DataTemplates are applied to a ContentControl or a ContentPresenter when you assign an object (the data item) to their Content property with a type that matches the DataType. In that case the DataContext of the UserControl is automatically set to the data item. You are breaking this mechanism when you explicitly set the DataContext. – Clemens Jun 01 '21 at 09:25

0 Answers0