0

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

ftp.x32
  • 55
  • 12
  • 1
    You are asking a lot of someone to ingest all of this code, can you reduce it [to a mcve](https://stackoverflow.com/help/mcve) ? – Crowcoder Aug 23 '18 at 12:15
  • If you can reduce your question in length by 85% then it will get many more responses. – mjwills Aug 23 '18 at 12:16
  • @Crowcoder working on it give me a few minutes please :) – ftp.x32 Aug 23 '18 at 12:48
  • I'll suggest that you give a name to each of your user controls and make a property for each on your viewmodel/datacontext – kenny Aug 23 '18 at 12:49
  • @Crowcoder can you take another look? I reduced the code while preserving the basic structure that I use. – ftp.x32 Aug 23 '18 at 13:34
  • @kenny even if I name the `TabItem` like `` I still get binding exceptions on the menubar where I changed it to ` ` where 'PwSettings' is the name of the dependency property. – ftp.x32 Aug 23 '18 at 13:37
  • How to retrieve the value from a PasswordBox using MVVM has been answered here: https://stackoverflow.com/questions/1483892/how-to-bind-to-a-passwordbox-in-mvvm. Please start reading this. – mm8 Aug 23 '18 at 14:46

0 Answers0