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.