I have a problem with referencing properties or elements on different user controls in my MVVM WPF application.
Edit: Reduced code to a MCVE (hopefully). Also removed PropertyChanged Events to reduce code.
TLDR
- I split up all my MainWindow.xaml elements to different user controls
- In the menubar (one control) I want to fire a ICommand to save a Settings object (which is the datacontext of the MainWindow
- For the method that is called in the Command I need a value from a completely different UserControl that is neither parent nor child of the menubar
How can I retrieve the value from the PasswordBox in a MVVM approach (most preferably as the CommandParameter on the MenuBar)?
View
MainWindow.xaml
<Window x:Class="WpfApp1.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:ViewModel="clr-namespace:WpfApp1.ViewModel"
        xmlns:View="clr-namespace:WpfApp1.View"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <ViewModel:SettingsViewModel/>
    </Window.DataContext>
    <StackPanel>
        <DockPanel>
            <View:MenuBar DataContext="{Binding}"/>
        </DockPanel>
        <TabControl>
            <TabItem Header="Tab1" DataContext="{Binding AppSettings}">
                <View:TabItemContent/>
            </TabItem>
        </TabControl>
    </StackPanel>
</Window>
The MenuBar which is supposed to bind on ICommand properties on the ViewModel (the DataContext of MainWindow).
MenuBar.xaml
<UserControl x:Class="WpfApp1.View.MenuBar"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApp1.View"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" >
    <Menu DockPanel.Dock="Top" Height="Auto" >
          <!-- In the command I need a reference to the password on the tabItem -->
        <MenuItem Name="SaveItem" Height="Auto" Header="Save"
                Command="{Binding SaveCommand}"
                CommandParameter="Binding RelativeSource=
        {RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
    </Menu>
</UserControl>
TabItemContent.xaml
<UserControl x:Class="WpfApp1.View.TabItemContent"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApp1.View"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             DataContext="{Binding PasswordSettings}">
    <Grid>
        <PasswordBox Height="25" Width="100" />
    </Grid>
</UserControl>
In TabItemControl.xaml.cs I tried to introduce a Dependency Property and set the value to the datacontext of the control.
ViewModel
SettingViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using WpfApp1.Commands;
using WpfApp1.Model;
namespace WpfApp1.ViewModel
{
  public class SettingsViewModel
  {    
    public Settings AppSettings { get; private set; } = new Settings();
    public ICommand SaveCommand { get; private set; }
    public SettingsViewModel()
    {
      SaveCommand = new DelegateCommand( SaveSettings );
      // load settings and call
      // OnPropertyChanged( "AppSettings" );
    }
    public void SaveSettings( object o = null )
    {
      if( o is string encryptedPass )
      {
        // get the password and save it to AppSettings object
      }
      // call save method on settings
    }
  }
}
Model (Setting classes)
AppSetting.cs (encapsulating all the setting objects from the other tabs)
namespace WpfApp1.Model
{
  public class Settings
  {
    public PasswordSettings PwSettings { get; set; } = new PasswordSettings();
  }
}
PasswordSettings.cs
namespace WpfApp1.Model
{
  public class PasswordSettings
  {
    public string EncryptedPass { get; set; }
  }
}
and finally the DelegateCommand implementation in
DelegateCommand.cs see this gist
