If you do not use any Framework, then the program window itself or the currently active UserControl may well act as an intermediary. I give an example where the codecode program window acts as an intermediary.

Type calculator :) PageCalc.xaml
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0"> <TextBox x:Name="textBoxInput" Text="{Binding InputCalc, Mode=TwoWay}" Width="100" Margin="10" /> <Button Content="1" Margin="10" Command="{Binding NumberInputCommand}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"/> <Button Content="2" Margin="10" Command="{Binding NumberInputCommand}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"/> <Button Content="3" Margin="10" Command="{Binding NumberInputCommand}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"/> <Button Content="OK" Margin="10" Command="{Binding CalcOkCommand, Mode=OneTime}"/> </StackPanel> </Grid>
Its ViewModel CalcViewModel.cs
public class CalcViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged = delegate { }; //ctor public CalcViewModel() { } //Properties public IMainWindowCodeBehind CodeBehind { get; set; } private string _InputCalc; public string InputCalc { get { return _InputCalc; } set { _InputCalc = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(InputCalc))); } } //Commands private RelayCommand<string> _NumberInputCommand; public RelayCommand<string> NumberInputCommand { get { return _NumberInputCommand = _NumberInputCommand ?? new RelayCommand<string>(OnNumberInput); } } private void OnNumberInput(string number) { InputCalc += number; } /// <summary> /// ΠΠ°ΠΆΠ°ΡΠΈΠ΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ ΠΠ Π²ΠΎ ΡΡΠ΅ΠΉΠΌΠ΅ ΠΊΠ°Π»ΡΠΊΡΠ»ΡΡΠΎΡ /// </summary> private RelayCommand _CalcOkCommand; public RelayCommand CalcOkCommand { get { return _CalcOkCommand = _CalcOkCommand ?? new RelayCommand(OnCalcOk); } } private void OnCalcOk() { CodeBehind.CloseCalcPage(); } }
The program window MainWindow.xaml
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0"> <StackPanel Orientation="Horizontal" Margin="10"> <TextBlock Text="Leak Size:" /> <TextBox x:Name="textBoxLeakSize" Text="{Binding LeakSize}" Width="150" Margin="10,0" GotFocus="textBox_GotFocus"/> </StackPanel> <StackPanel Orientation="Horizontal" Margin="10"> <TextBlock Text="Other Size:" /> <TextBox x:Name="textBoxOtherSize" Text="{Binding OtherSize}" Width="150" Margin="10,0" GotFocus="textBox_GotFocus" /> </StackPanel> </StackPanel> <Frame x:Name="frame" Grid.Column="1" Margin="10" BorderBrush="LimeGreen" BorderThickness="2" NavigationUIVisibility="Hidden" /> </Grid>
Its ViewModel MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged = delegate { }; //ctor public MainViewModel() { } //Properties private string _LeakSize; public string LeakSize { get { return _LeakSize; } set { _LeakSize = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(LeakSize))); } } private string _OtherSize; public string OtherSize { get { return _OtherSize; } set { _OtherSize = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(OtherSize))); } } }
And the most important thing is the codebook program window
public interface IMainWindowCodeBehind { void CloseCalcPage(); } /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window, IMainWindowCodeBehind { // private MainViewModel _MainVM; private CalcViewModel _CalcVM; private TextBox _ActiveTextBox; public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; //Π·Π°Π³ΡΡΠ·ΠΊΠ° Π²ΡΡΠΌΠΎΠ΄Π΅Π», ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ° Π΄Π°Π½Π½ΡΡ
_MainVM = new MainViewModel(); this.DataContext = _MainVM; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { //ΠΏΡΡΡΠ°Ρ ΡΡΡΠ°Π½ΠΈΡΠ° Π²ΠΎ ΡΡΠ΅ΠΉΠΌ this.frame.NavigationService.Navigate(new Uri("PageEmpty.xaml", UriKind.Relative)); } /// <summary> /// Π‘ΠΎΠ±ΡΡΠΈΠ΅ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠΎΠΊΡΡΠ° ΠΊΠ°ΠΊΠΈΠΌ-ΡΠΎ TextBox /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void textBox_GotFocus(object sender, RoutedEventArgs e) { //ΡΡΡΠ»ΠΊΠ° Π½Π° ΡΠ΅Π΄Π°ΠΊΡΠΈΡΡΠ΅ΠΌΡΠΉ textbox _ActiveTextBox = sender as TextBox; //Π²ΡΠΊΠ»ΡΡΠ°Π΅ΠΌ textBoxLeakSize.IsEnabled = false; textBoxOtherSize.IsEnabled = false; //Π·Π°Π³ΡΡΠ·ΠΊΠ° ΡΡΡΠ°Π½ΠΈΡΡ ΠΈ Π΅Π΅ Π²ΡΡΠΌΠΎΠ΄Π΅Π», ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ° Π΄Π°Π½Π½ΡΡ
PageCalc page = new PageCalc(); _CalcVM = new CalcViewModel(); page.DataContext = _CalcVM; //Π΄Π°Π΅ΠΌ ΡΡΡΠ»ΠΊΡ Π½Π° ΡΡΠΎΡ ΠΊΠΎΠ΄Π±ΠΈΡ
Π°ΠΉΠ½Π΄, ΡΡΠΎΠ±Ρ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ Π²ΡΠ·Π²Π°ΡΡ CloseCalcPage() _CalcVM.CodeBehind = this; this.frame.NavigationService.Navigate(page); } /// <summary> /// ΠΠ΅ΡΠΎΠ΄ Π²ΡΠ·ΡΠ²Π°Π΅ΠΌΡΠΉ ΠΈΠ· CalcViewModel /// ΠΠ°ΠΊΡΡΡΠΈΠ΅ ΡΡΡΠ°Π½ΠΈΡΡ ΠΊΠ°Π»ΡΠΊΡΠ»ΡΡΠΎΡΠ° /// </summary> public void CloseCalcPage() { //Π²ΠΊΠ»ΡΡΠ°Π΅ΠΌ textBoxLeakSize.IsEnabled = true; textBoxOtherSize.IsEnabled = true; if (_ActiveTextBox != null) { if (_ActiveTextBox.Name == textBoxLeakSize.Name) { //ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅, ΡΠ΅ΡΠ΅Π· ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ Π²ΡΡΠΌΠΎΠ΄Π΅Π» _MainVM.LeakSize = _CalcVM.InputCalc; } else { _MainVM.OtherSize = _CalcVM.InputCalc; } } _ActiveTextBox = null; _CalcVM = null; this.frame.NavigationService.Navigate(new Uri("PageEmpty.xaml", UriKind.Relative)); } }
Please note that when you click on OK in the calculator, the CloseCalcPage() method is CloseCalcPage() from the code window of the program.
PS RelayCommand class or else you can find it on the Internet. DelegateCommand is a variation on the implementation of the ICommand interface. For example, you can take and use this
public class RelayCommand : ICommand { Action _TargetExecuteMethod; Func<bool> _TargetCanExecuteMethod; public RelayCommand(Action executeMethod) { _TargetExecuteMethod = executeMethod; } public RelayCommand(Action executeMethod, Func<bool> canExecuteMethod) { _TargetExecuteMethod = executeMethod; _TargetCanExecuteMethod = canExecuteMethod; } public void RaiseCanExecuteChanged() { CanExecuteChanged(this, EventArgs.Empty); } #region ICommand Members bool ICommand.CanExecute(object parameter) { if (_TargetCanExecuteMethod != null) { return _TargetCanExecuteMethod(); } if (_TargetExecuteMethod != null) { return true; } return false; } // Beware - should use weak references if command instance lifetime is longer than lifetime of UI objects that get hooked up to command // Prism commands solve this in their implementation public event EventHandler CanExecuteChanged = delegate { }; void ICommand.Execute(object parameter) { if (_TargetExecuteMethod != null) { _TargetExecuteMethod(); } } #endregion } public class RelayCommand<T> : ICommand { Action<T> _TargetExecuteMethod; Func<T, bool> _TargetCanExecuteMethod; public RelayCommand(Action<T> executeMethod) { _TargetExecuteMethod = executeMethod; } public RelayCommand(Action<T> executeMethod, Func<T,bool> canExecuteMethod) { _TargetExecuteMethod = executeMethod; _TargetCanExecuteMethod = canExecuteMethod; } public void RaiseCanExecuteChanged() { CanExecuteChanged(this, EventArgs.Empty); } #region ICommand Members bool ICommand.CanExecute(object parameter) { if (_TargetCanExecuteMethod != null) { T tparm = (T)parameter; return _TargetCanExecuteMethod(tparm); } if (_TargetExecuteMethod != null) { return true; } return false; } // Beware - should use weak references if command instance lifetime is longer than lifetime of UI objects that get hooked up to command // Prism commands solve this in their implementation public event EventHandler CanExecuteChanged = delegate { }; void ICommand.Execute(object parameter) { if (_TargetExecuteMethod != null) { _TargetExecuteMethod((T)parameter); } } #endregion }