DataContext Switching for 2 different ViewModels using Frame @Hui Lui-MSFT
Hello @Hui Liu-MSFT
I have created 2 View Buttons. In View1 I am able to increment the 2 texts and can view the result. In View2, I am able to drag and drop multiple copies of TextBlock into Canvas region and can change its width and height property from handycontrol. I navigated to View1 and was able to view the change. However, on again navigating to View2 I cannot see multiple instances of Textblocks at its various positions in the Canvas which I did. Indeed the default View2 is there with texblock at left, Canvas at middle and handycontrol in right. WHere is the problem and how should I tackle this. The code is provided below.
Commands.cs:
using P1_DataSwitching.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace P1_DataSwitching.Commands
{
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
public void Execute(object parameter)
{
_execute();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
-------------------------------------------------------------------------------------------
UC_Tb.xaml:
<UserControl x:Class="P1_DataSwitching.View.UC_Tb"
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:P1_DataSwitching.View"
mc:Ignorable="d"
x:Name="UC_Add"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox Text="{Binding Number, ElementName=UC_Add}" />
</Grid>
</UserControl>
-------------------------------------------------------------------------------------------
UC_Tb.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace P1_DataSwitching.View
{
/// <summary>
/// Interaction logic for UC_Tb.xaml
/// </summary>
public partial class UC_Tb : UserControl
{
public UC_Tb()
{
InitializeComponent();
}
public string Number
{
get { return (string)GetValue(NumberProperty); }
set { SetValue(NumberProperty, value); }
}
// Using a DependencyProperty as the backing store for Number. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NumberProperty =
DependencyProperty.Register("Number", typeof(string), typeof(UC_Tb), new PropertyMetadata(null));
}
}
-------------------------------------------------------------------------------------------
UC1.xaml:
<UserControl x:Class="P1_DataSwitching.View.UC1"
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:P1_DataSwitching.View"
xmlns:v="clr-namespace:P1_DataSwitching.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<TextBlock Text="Number1" Margin="5" />
<!--<local:UC_Add Number="{Binding Text1}" Margin="5" />-->
<v:UC_Tb Number="{Binding Text1}" Margin="5" />
<TextBlock Text="Number2" Margin="5" />
<!--<local:UC_Add Number="{Binding Text2}" Margin="5" />-->
<v:UC_Tb Number="{Binding Text2}" Margin="5" />
<TextBlock Text="Addition of Number1 and Number2" Margin="5" />
<!--<local:UC_Add Number="{Binding Sum}" Margin="5" />-->
<v:UC_Tb Number="{Binding Sum}" Margin="5" />
<Button Content="Increment Text1" Command="{Binding IncrementNum1Command}" Margin="5" />
<Button Content="Increment Text2" Command="{Binding IncrementNum2Command}" Margin="5" />
</StackPanel>
</UserControl>
-------------------------------------------------------------------------------------------
UC1.xaml.cs:
using P1_DataSwitching.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace P1_DataSwitching.View
{
/// <summary>
/// Interaction logic for UC1.xaml
/// </summary>
public partial class UC1 : UserControl
{
// UC1_ViewModel myviewmodel = new UC1_ViewModel();
public UC1()
{
InitializeComponent();
// this.DataContext = myviewmodel;
}
}
}
-------------------------------------------------------------------------------------------
UC2.xaml:
<UserControl x:Class="P1_DataSwitching.View.UC2"
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:P1_DataSwitching.View"
xmlns:v="clr-namespace:P1_DataSwitching.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Border BorderBrush="Blue" BorderThickness="1">
<Canvas x:Name="canvas1"
AllowDrop="True"
Background="Transparent">
<v:UC3 x:Name="Tb1" Canvas.Left="4" />
</Canvas>
</Border>
<v:UC4 Grid.Column="2" />
<v:UC5 ScrollViewer.VerticalScrollBarVisibility="Visible"
Grid.Column="1" Margin="5" />
</Grid>
</UserControl>
-------------------------------------------------------------------------------------------
UC2.xaml.cs:
using P1_DataSwitching.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace P1_DataSwitching.View
{
/// <summary>
/// Interaction logic for UC2.xaml
/// </summary>
public partial class UC2 : UserControl
{
UC2_ViewModel mymodel;
public UC2()
{
InitializeComponent();
mymodel = new UC2_ViewModel();
this.DataContext = mymodel;
}
}
}
-------------------------------------------------------------------------------------------
UC3.xaml:
<UserControl x:Class="P1_DataSwitching.View.UC3"
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:P1_DataSwitching.View"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="200">
<Border BorderBrush="Red" BorderThickness="2">
<Canvas x:Name="canvas2" AllowDrop="True"
Background="Transparent">
<TextBlock Width="100"
Height="100"
Margin="5"
Background="Gray"
MouseMove="TextBlock_MouseMove"
/>
</Canvas>
</Border>
</UserControl>
-------------------------------------------------------------------------------------------
UC3.xaml.cs:
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Xml;
namespace P1_DataSwitching.View
{
/// <summary>
/// Interaction logic for UC3.xaml
/// </summary>
public partial class UC3 : UserControl
{
public UC3()
{
InitializeComponent();
}
private void TextBlock_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
{
GlobalVariable.MyCheck = 0;
var xaml = XamlWriter.Save(sender);
var xamlString = new StringReader(xaml);
var xmlTextReader = new XmlTextReader(xamlString);
var deepCopyObject = XamlReader.Load(xmlTextReader);
DragDrop.DoDragDrop(sender as DependencyObject, new DataObject(DataFormats.Serializable, deepCopyObject), DragDropEffects.Move);
}
}
}
}
-------------------------------------------------------------------------------------------
UC4.xaml:
<UserControl x:Class="P1_DataSwitching.View.UC4"
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:P1_DataSwitching.View"
mc:Ignorable="d"
xmlns:hc="https://handyorg.github.io/handycontrol"
d:DesignHeight="600" d:DesignWidth="400">
<Grid>
<hc:PropertyGrid
x:Name="mypg"
Margin="10,0,10,0"
SelectedObject="{Binding SelectedTb}"
/>
</Grid>
</UserControl>
-------------------------------------------------------------------------------------------
UC4.xaml.cs:
using P1_DataSwitching.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace P1_DataSwitching.View
{
/// <summary>
/// Interaction logic for UC4.xaml
/// </summary>
public partial class UC4 : UserControl
{
public UC4()
{
InitializeComponent();
}
public void MyselectedObject1()
{
UC2_ViewModel.currentprop.SelectedTb = GlobalVariable.SelectedViewModel;
}
}
}
-------------------------------------------------------------------------------------------
UC5.xaml:
<UserControl x:Class="P1_DataSwitching.View.UC5"
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:P1_DataSwitching.View"
mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="450">
<Border Margin="5" BorderBrush="Green" BorderThickness="2">
<Canvas
Background="LightBlue"
AllowDrop="True"
x:Name="canvas"
MouseDown="canvas_MouseDown"
Drop="canvas_Drop"
MouseMove="canvas_MouseMove"
/>
</Border>
</UserControl>
-------------------------------------------------------------------------------------------
UC5.xaml.cs:
using P1_DataSwitching.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace P1_DataSwitching.View
{
/// <summary>
/// Interaction logic for UC5.xaml
/// </summary>
public partial class UC5 : UserControl
{
UC2_ViewModel vmt;
UC4 mypg;
int check;
public UC5()
{
InitializeComponent();
vmt = new UC2_ViewModel();
this.DataContext = vmt;
mypg = new UC4();
}
private Point mousePosition;
private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
// startPoint = e.GetPosition(null); // Save the initial click position
check = 1;
mousePosition = e.GetPosition(canvas);
Method_SelectedControl(e);
// DragDrop.DoDragDrop(vmt.SelectedControl, vmt.SelectedControl, DragDropEffects.Move);
}
}
private void Method_SelectedControl(MouseButtonEventArgs e)
{
vmt.SelectedControl = e.OriginalSource as UIElement;
vmt.Height = ((FrameworkElement)vmt.SelectedControl).Height.ToString();
vmt.Width = ((FrameworkElement)vmt.SelectedControl).Width.ToString();
GlobalVariable.SelectedViewModel = vmt;
mypg.MyselectedObject1();
}
private void canvas_Drop(object sender, DragEventArgs e)
{
if (check == 1 && GlobalVariable.MyCheck == 1)
{
// UIElement element = e.OriginalSource as UIElement;
Point dropPosition = e.GetPosition(canvas);
Canvas.SetLeft(vmt.SelectedControl, dropPosition.X);
Canvas.SetTop(vmt.SelectedControl, dropPosition.Y);
check = 0;
GlobalVariable.MyCheck = 0;
}
else
{
Object data = e.Data.GetData(DataFormats.Serializable);
if (data is UIElement element)
{
Point dropPosition = e.GetPosition(canvas);
Canvas.SetLeft(element, dropPosition.X);
Canvas.SetTop(element, dropPosition.Y);
if (!canvas.Children.Contains(element))
{
canvas.Children.Add(element);
}
}
GlobalVariable.MyCheck = 1;
// GlobalVariable.SelectedControl = data as UIElement;
//vmt.Height = ((FrameworkElement)vmt.SelectedControl).Height.ToString();
//vmt.Width = ((FrameworkElement)vmt.SelectedControl).Width.ToString();
//GlobalVariable.SelectedViewModel = vmt;
mypg.MyselectedObject1();
}
}
private void canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && check == 1)
{
var position = e.GetPosition(canvas);
var offset = position - mousePosition;
mousePosition = position;
Canvas.SetLeft(vmt.SelectedControl, Canvas.GetLeft(vmt.SelectedControl) + offset.X);
Canvas.SetTop(vmt.SelectedControl, Canvas.GetTop(vmt.SelectedControl) + offset.Y);
}
}
}
}
-------------------------------------------------------------------------------------------
BaseViewModel.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace P1_DataSwitching.ViewModel
{
public class BaseViewModel : INotifyPropertyChanged
{
//Method for property change at run time
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
-------------------------------------------------------------------------------------------
MainViewModel.cs:
using P1_DataSwitching.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace P1_DataSwitching.ViewModel
{
public class MainViewModel: BaseViewModel
{
UC1_ViewModel myuc1viewmodel;
UC2_ViewModel myuc2viewmodel;
// SelectedViewModel property controls the navigation between view models
private BaseViewModel _selectedViewModel;
public BaseViewModel SelectedViewModel
{
get { return _selectedViewModel; }
set {
_selectedViewModel = value;
OnPropertyChanged(nameof(SelectedViewModel));
}
}
public ICommand SwitchToUC1Command { get; }
public ICommand SwitchToUC2Command { get; }
public MainViewModel()
{
//UpdateViewCommands = new UpdateViewCommand(this);
myuc1viewmodel = new UC1_ViewModel();
myuc2viewmodel = new UC2_ViewModel();
SwitchToUC1Command = new RelayCommand(SwitchToUC1);
SwitchToUC2Command = new RelayCommand(SwitchToUC2);
}
private void SwitchToUC1()
{
// Set the stored instance of UC1ViewModel as the current view model
SelectedViewModel = myuc1viewmodel;
}
private void SwitchToUC2()
{
// Set the stored instance of UC2ViewModel as the current view model
SelectedViewModel = myuc2viewmodel;
GlobalVariable.SelectedViewModel = SelectedViewModel;
}
}
}
-------------------------------------------------------------------------------------------
UC1_ViewModel.cs:
using P1_DataSwitching.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace P1_DataSwitching.ViewModel
{
public class UC1_ViewModel:BaseViewModel
{
#region Text1
private int MyText1 = 30;
public int Text1
{
get
{
return MyText1;
}
set
{
MyText1 = value;
OnPropertyChanged(nameof(Text1));
OnPropertyChanged(nameof(Sum));
}
}
#endregion
#region Text2
private int MyText2 = 40;
public int Text2
{
get
{
return MyText2;
}
set
{
MyText2 = value;
OnPropertyChanged(nameof(Text2));
OnPropertyChanged(nameof(Sum));
}
}
#endregion
#region Sum
int _sum = 0;
public int Sum
{
get
{
try
{
return Text1 + Text2;
}
catch (FormatException)
{
return 0;
}
}
}
#endregion
public ICommand IncrementNum1Command { get; }
public ICommand IncrementNum2Command { get; }
public UC1_ViewModel()
{
IncrementNum1Command = new RelayCommand(IncrementNum1);
IncrementNum2Command = new RelayCommand(IncrementNum2);
}
private void IncrementNum1()
{
Text1++;
}
private void IncrementNum2()
{
Text2++;
}
}
}
-------------------------------------------------------------------------------------------
UC2_ViewModel.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace P1_DataSwitching.ViewModel
{
public class UC2_ViewModel: BaseViewModel
{
public static UC2_ViewModel currentprop { get; set; }
public UC2_ViewModel()
{
currentprop = this;
}
#region Making By default these properties as false on loading
// These properties will be hidden by default in the PropertyGrid
[Browsable(false)]
public System.Windows.Threading.Dispatcher Dispatcher { get; set; }
[Browsable(false)]
public bool IsSealed { get; set; }
[Browsable(false)]
public System.Windows.DependencyObjectType DependencyObjectType { get; set; }
#endregion
// properties definition
/// <summary>
/// Selected Control
/// </summary>
private UIElement _selectedControl;
[Browsable(false)]
public UIElement SelectedControl
{
get { return _selectedControl; }
set
{
_selectedControl = value;
OnPropertyChanged("SelectedControl");
}
}
private Object _selectedTb;
[Browsable(false)]
public Object SelectedTb
{
get { return _selectedTb; }
set
{
if (_selectedTb != value)
{
_selectedTb = value;
OnPropertyChanged(nameof(SelectedTb));
}
}
}
#region Width
private string _tbWidth;
[Category("Size")]
public string Width
{
get { return _tbWidth; }
set
{
_tbWidth = value;
if (SelectedControl != null)
{
if (SelectedControl is TextBlock tblock)
{
if (_tbWidth != "")
{
tblock.Width = Convert.ToDouble(_tbWidth);
}
}
}
OnPropertyChanged("Width");
}
}
#endregion
#region Height
private string _tbHeight;
[Category("Size")]
public string Height
{
get { return _tbHeight; }
set
{
_tbHeight = value;
if (SelectedControl != null)
{
if (SelectedControl is TextBlock tblock)
{
if (_tbHeight != "")
{
tblock.Height = Convert.ToDouble(_tbHeight);
}
}
}
OnPropertyChanged("Height");
}
}
#endregion
}
}
-------------------------------------------------------------------------------------------
App.xaml:
<Application x:Class="P1_DataSwitching.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:P1_DataSwitching"
xmlns:views="clr-namespace:P1_DataSwitching.View"
xmlns:viewmodels="clr-namespace:P1_DataSwitching.ViewModel"
xmlns:hc="https://handyorg.github.io/handycontrol"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<hc:ThemeResources/>
<hc:Theme/>
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type viewmodels:UC1_ViewModel}">
<views:UC1/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:UC2_ViewModel}">
<views:UC2/>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
-------------------------------------------------------------------------------------------
GlobalVariable.cs:
using P1_DataSwitching.ViewModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace P1_DataSwitching
{
public class GlobalVariable:BaseViewModel
{
private static BaseViewModel _selectedViewModel;
public static BaseViewModel SelectedViewModel
{
get { return _selectedViewModel; }
set { _selectedViewModel = value; }
}
private static int check;
public static int MyCheck
{
get { return check; }
set { check = value; }
}
private static UIElement _selectedControl;
[Browsable(false)]
public static UIElement SelectedControl
{
get { return _selectedControl; }
set
{
_selectedControl = value;
//OnPropertyChanged("SelectedControl");
}
}
}
}
-------------------------------------------------------------------------------------------
MainWindow.xaml:
<Window x:Class="P1_DataSwitching.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:local="clr-namespace:P1_DataSwitching"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" WindowState="Maximized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Frame Content="{Binding SelectedViewModel}"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" >
<Button Height="30" Width="60" Content="View1" Command="{Binding SwitchToUC1Command}" />
<Button Height="30" Width="60" Content="View2" Command="{Binding SwitchToUC2Command}" Margin="50 0 0 0" />
</StackPanel>
</Grid>
</Window>
-------------------------------------------------------------------------------------------
MainWindow.xaml.cs:
using P1_DataSwitching.ViewModel;
using System.Windows;
namespace P1_DataSwitching
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
}