MetroWindow.RightWindowCommands in dynamically created MetroWindow - c#

How to bind/create MetroWindow.RightWindowCommands in dynamically created MetroWindow through Caliburn.Micro IWindowManager Show method?
For example, I've created a custom IWindowManager implementation to always create MetroWindow instead of default Window. So whenever a new Window is created from Caliburn, it will be MetroWindow instance.
I have a logic that creates dynamically windows through IWindowManager:
ChatManager
public class ChatManager : IChatManager
{
private readonly IChatWindowSettings chatWindowSettings;
private readonly IWindowManager windowManager;
private readonly IChatFactory chatFactory;
private IDictionary<WeakReference, WeakReference> chats;
public ChatManager(IChatWindowSettings chatWindowSettings, IWindowManager windowManager, IChatFactory chatFactory)
{
this.chatWindowSettings = chatWindowSettings;
this.windowManager = windowManager;
this.chatFactory = chatFactory;
chats = new Dictionary<WeakReference, WeakReference>();
}
public void OpenFor(ISender sender)
{
var settings = chatWindowSettings.Create();
var viewModel = CreateOrGetViewModel(sender);
windowManager.ShowWindow(viewModel, null, settings);
}
private IChat CreateOrGetViewModel(ISender sender){//code...}
Those windows are chat windows. This works great. However, I'd like to bind/create a button directly in the MetroWindow RightCommands. This button would be bound to the IChat implementation (which is a view-model):
public class ChatViewModel : Screen, IChat
{
public void DoSomething(){}
}
How can I accomplish such thing?

here are some thoughts for your problem
calling sample
var view = new MainWindow(new ChatViewModel() { ChatName = "Chat name" });
view.Show();
model sample
public class ChatViewModel
{
public string ChatName { get; set; }
private ICommand chatCommand;
public ICommand ChatCommand
{
get
{
return chatCommand
?? (chatCommand = new SimpleCommand() {
CanExecutePredicate = o => true,
ExecuteAction = o => MessageBox.Show("Hurray :-D")
});
}
}
}
window code behind
public partial class MainWindow : MetroWindow
{
public MainWindow(ChatViewModel chatViewModel)
{
this.DataContext = chatViewModel;
InitializeComponent();
}
}
window xaml
<Controls:MetroWindow x:Class="MahAppsMetroSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
Title="MainWindow"
GlowBrush="{DynamicResource AccentColorBrush}"
Height="350"
Width="525">
<Controls:MetroWindow.RightWindowCommands>
<Controls:WindowCommands>
<Button Content="{Binding ChatName}"
Command="{Binding ChatCommand}" />
</Controls:WindowCommands>
</Controls:MetroWindow.RightWindowCommands>
<Grid>
<!-- the content -->
</Grid>
</Controls:MetroWindow>
simple command
public class SimpleCommand : ICommand
{
public Predicate<object> CanExecutePredicate { get; set; }
public Action<object> ExecuteAction { get; set; }
public bool CanExecute(object parameter)
{
if (CanExecutePredicate != null)
return CanExecutePredicate(parameter);
return true; // if there is no can execute default to true
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
if (ExecuteAction != null)
ExecuteAction(parameter);
}
}
hope that helps

Related

Call a function from another ViewModel

I´m a beginner in this language, trying to learn more about the best practices and hows to do the things better...
I have this repo as example application: https://github.com/Albvadi/NavigationMVVM
If you run it, all works well. You can navigate to others views and increment a counter shared in all views.
But, If you uncomment the ActualView assignation in MainViewModel.cs file at line 24 and put a login view in front of the InitialView I don´t know how to redirect the user to the Initialview after login success.
When the login is correct, I fill the user data in the ManagerData and with all of this I need to call the function in the MainViewModel to redirect the user to the Initial view. How can I make that call from the LoginViewModel to the other MainViewModel instead from the View?
UPDATE: Add the code relevant
App.xaml
<Application
x:Class="NavigationMVVM.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:NavigationMVVM"
xmlns:Views="clr-namespace:NavigationMVVM.Views"
xmlns:ViewModels="clr-namespace:NavigationMVVM.ViewModels"
StartupUri="MainWindow.xaml">
<Application.Resources>
<DataTemplate
DataType="{x:Type ViewModels:MainViewModel}">
<local:MainWindow />
</DataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:LoginViewModel}">
<Views:Login />
</DataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:InitialViewModel}">
<Views:Initial />
</DataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:FirstViewModel}">
<Views:First />
</DataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:SecondViewModel}">
<Views:Second />
</DataTemplate>
</Application.Resources>
</Application>
MainWindow.xaml
<Window
x:Class="NavigationMVVM.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:NavigationMVVM"
xmlns:viewModels="clr-namespace:NavigationMVVM.ViewModels"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="MainWindow"
Height="450"
Width="800">
<Window.DataContext>
<viewModels:MainViewModel />
</Window.DataContext>
<Grid>
<ContentControl
Content="{Binding ActualView}" />
</Grid>
</Window>
BaseViewModel.cs
using System.ComponentModel;
namespace NavigationMVVM.Common
{
public class BaseViewModel : INotifyPropertyChanged
{
private BaseViewModel _ActualView;
public BaseViewModel ActualView
{
get => _ActualView;
set
{
_ActualView = value;
RaisePropertyChanged(null);
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
#endregion
}
}
MainViewModel.cs
using NavigationMVVM.Common;
using NavigationMVVM.Models;
using System.Windows.Input;
namespace NavigationMVVM.ViewModels
{
public class MainViewModel : BaseViewModel
{
public DataManager SharedData = new DataManager();
public InitialViewModel InitialVM;
public FirstViewModel FirstVM;
public SecondViewModel SecondVM;
public LoginViewModel LoginVM;
public MainViewModel()
{
LoginVM = new LoginViewModel(SharedData);
InitialVM = new InitialViewModel(SharedData);
FirstVM = new FirstViewModel(SharedData);
SecondVM = new SecondViewModel(SharedData);
ActualView = InitialVM;
//ActualView = LoginVM;
}
public ICommand DisplayFirstView
{
get
{
return new RelayCommand(action => ActualView = FirstVM,
canExecute => true);
}
}
public ICommand DisplaySecondView
{
get
{
return new RelayCommand(action => ActualView = SecondVM,
canExecute => true);
}
}
public ICommand DisplayInitialView
{
get
{
return new RelayCommand(action => ActualView = InitialVM,
canExecute => true);
}
}
}
}
LoginViewModel.cs
using NavigationMVVM.Common;
using NavigationMVVM.Models;
using System.Diagnostics;
using System.Windows.Input;
namespace NavigationMVVM.ViewModels
{
public class LoginViewModel : BaseViewModel
{
private DataManager _DataManager;
public ICommand LoginCmd;
public RelayCommand DoLoginCmd { get; }
private string _Username;
public string Username
{
get => _Username;
set
{
_Username = value;
RaisePropertyChanged(null);
}
}
private string _Password;
public string Password
{
get => _Password;
set
{
_Password = value;
RaisePropertyChanged(null);
}
}
private string _MessageInfo;
public string MessageInfo
{
get => _MessageInfo;
set
{
_MessageInfo = value;
RaisePropertyChanged(null);
}
}
public LoginViewModel(DataManager sharedData)
{
_DataManager = sharedData;
DoLoginCmd = new RelayCommand(param => DoLogin(), canExec => (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)));
}
public void DoLogin()
{
if (Username == "admin" && Password == "password")
{
_DataManager.User.Name = "Administrator";
_DataManager.User.Mail = "admin#company.com";
MessageInfo = "Login OK!... How to redirect??";
}
else
{
MessageInfo = "Username or Password incorrect!";
}
}
}
}
Thank you.
There are multiple ways to accomplish what you want. I'll post two approaches that are very common when working in an MVVM pattern
Event based approach:
public MainViewModel()
{
LoginVM = new LoginViewModel(SharedData);
LoginVM.PropertyChanged += LoginVM_PropertyChanged;
InitialVM = new InitialViewModel(SharedData);
FirstVM = new FirstViewModel(SharedData);
SecondVM = new SecondViewModel(SharedData);
//ActualView = InitialVM;
ActualView = LoginVM;
}
private void LoginVM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(sender.GetType() == typeof(LoginViewModel) && e.PropertyName == "MessageInfo")
{
var loginVM = (LoginViewModel)sender;
if (loginVM.MessageInfo == "OK")
{
ActualView = InitialVM;
}
}
}
Cunstructor Injection:
private Action _loginAction;
public LoginViewModel(DataManager sharedData, Action loginAction )
{
_DataManager = sharedData;
DoLoginCmd = new RelayCommand(param => DoLogin(), canExec => (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)));
_loginAction = loginAction;
}
public void DoLogin()
{
if (Username == "admin" && Password == "password")
{
_DataManager.User.Name = "Administrator";
_DataManager.User.Mail = "admin#company.com";
MessageInfo = "Login OK!... How to redirect??";
_loginAction.Invoke();
}
else
{
MessageInfo = "Username or Password incorrect!";
}
}
and
public MainViewModel()
{
LoginVM = new LoginViewModel(SharedData, () => ActualView = InitialVM);
InitialVM = new InitialViewModel(SharedData);
FirstVM = new FirstViewModel(SharedData);
SecondVM = new SecondViewModel(SharedData);
//ActualView = InitialVM;
ActualView = LoginVM;
}
Side note: to put a property "ActualView" on the BaseViewModel is a rather odd choice

Clicking ContextMenu-option only triggers RelayCommand once [duplicate]

This question already has an answer here:
WPF MenuItem style parameters not available on menu first open
(1 answer)
Closed 2 years ago.
Each ListBox item has a ContextMenu "Kick" option. It does trigger the CanKickPlayer() method when I right-click the first ListBox item for the first time, but never again if I repeat the process on the same or a different ListBox item. Questions:
How to do so CanKickPlayer() triggers every time I choose the ContextMenu "Kick" option?
Why is the passed parameter in CanKickPlayer() method null?
MainWindow.xaml
<Window x:Class="ContextMenuTriggeredOnce.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"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="200">
<GroupBox Header="Players">
<ListBox ItemsSource="{Binding Players}" SelectedItem="{Binding SelectedPlayer}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Kick" Command="{Binding KickPlayerCommand}" CommandParameter="{Binding SelectedPlayer}" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</GroupBox>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainViewModel MainViewModel { get; set; }
public MainWindow()
{
MainViewModel = new MainViewModel();
DataContext = MainViewModel;
InitializeComponent();
}
}
MainViewModel.cs
public class MainViewModel : BaseViewModel
{
public ObservableCollection<string> Players { get; set; } = new ObservableCollection<string>();
private string _selectedPlayer;
public string SelectedPlayer
{
get => _selectedPlayer;
set
{
if (value == null)
{
return;
}
SetProperty(ref _selectedPlayer, value);
}
}
private readonly RelayCommand _kickPlayerCommand;
public ICommand KickPlayerCommand => _kickPlayerCommand;
public MainViewModel()
{
Players.Add("Player1");
Players.Add("Player2");
_kickPlayerCommand = new RelayCommand(OnKickPlayer, CanKickPlayer);
}
private void OnKickPlayer(object command)
{
Players.Remove(command.ToString());
_kickPlayerCommand.InvokeCanExecuteChanged();
}
private bool CanKickPlayer(object command)
{
return command != null;
}
}
BaseViewModel.cs
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
}
RelayCommand.cs
class RelayCommand : ICommand
{
private readonly Action<object> _executeAction;
private readonly Func<object, bool> _canExecuteAction;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<object> executeAction, Func<object, bool> canExecuteAction)
{
_executeAction = executeAction;
_canExecuteAction = canExecuteAction;
}
public void Execute(object parameter) => _executeAction(parameter);
public bool CanExecute(object parameter) => _canExecuteAction?.Invoke(parameter) ?? true;
public void InvokeCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
You need to set the CommandParameter before Command.
<MenuItem Header="Kick" CommandParameter="{Binding SelectedPlayer}" Command="{Binding KickPlayerCommand}" />
(I don't use the Command pattern. I prefer using the Click event and from the Click event handler calling a function in the ViewModel. If I need to disable the menu item, I use a bool property in the ViewModel and bind that to the IsEnabled property on the menu item.)

How to Bind Drop and PreviewMouseLeftButtonDown Events in ViewModel in WPF

I have to bind Grid Drop Event and PreviewMouseLeftButtonDown event in ViewModel. I have a RelayCommand. But it is done only for passing the object, I have to pass the routed event by using the command and also for MouseButtonEventArgs. my sample code is as below, please give any suggestion for using the routed event args and MouseButtonEventArgs in viewmodel.
<Grid
x:Name="mainGrid"
AllowDrop="True"
Background="#F0F0F0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<cmd:EventCommandExecuter Command="{Binding GridDrop}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
<Grid Background="LightBlue" PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown">
EventCommandExecuter
public class EventCommandExecuter : TriggerAction<DependencyObject>
{
#region Constructors
public EventCommandExecuter()
: this(CultureInfo.CurrentCulture)
{
}
public EventCommandExecuter(CultureInfo culture)
{
Culture = culture;
}
#endregion
#region Properties
#region Command
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
#region EventArgsConverterParameter
public object EventArgsConverterParameter
{
get { return (object)GetValue(EventArgsConverterParameterProperty); }
set { SetValue(EventArgsConverterParameterProperty, value); }
}
public static readonly DependencyProperty EventArgsConverterParameterProperty =
DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
public IValueConverter EventArgsConverter { get; set; }
public CultureInfo Culture { get; set; }
#endregion
protected override void Invoke(object parameter)
{
var cmd = Command;
if (cmd != null)
{
var param = parameter;
if (EventArgsConverter != null)
{
param = EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.InvariantCulture);
}
if (cmd.CanExecute(param))
{
cmd.Execute(param);
}
}
}
}
I want to pass object and RoutedEventArgs like below in viewmodel. Please help
public void Grid_Drop(object sender, RoutedEventArgs e)
{
}
I feel like commands are often overkill for such simple tasks.
You can simply declare your ViewModel in the code behind of your view like so:
public partial class MainWindow : Window
{
private ViewModel _vm;
public ViewModel Vm
{
get { return _vm;}
set
{
_vm = value ;
}
}
//....Constructor here....
}
Then create a public event :
public event RoutedEventHandler OnGridDrop;
and call it in :
public void Grid_Drop(object sender, RoutedEventArgs e)
{
OnGridDrop?.Invoke(sender,e)
}
Now you only need to initialize your ViewModel:
public MainWindow()
{
InitializeComponent();
Vm = new ViewModel();
OnGridDrop += Vm.OnGridDrop;
}
and subscribe a corrsponding handler that you declared in your ViewModel.

Create a ViewModel with sub ViewModel

Is there a proper way to create a C#/WPF ViewModel containing subViewModel ?
Objective is:
I have a MainWindow. That window is use to read/create images. There is a button on that windows who switch between 2 UserControl one with IHM used to read image, the other one used to create.
The MainWindow has a MainWindowViewModel with :
command switch
image length
application parameters
I want that both UserControls can acces to MainWindowViewModel field/properties and have they own commands.
Construction will be something like this:
public partial class ReadUserControl : UserControl
{
public ReadUserControl()
{
InitializeComponent();
DataContext = MainViewModel.ReadViewModel;
}
}
public partial class CreateUserControl : UserControl
{
public CreateUserControl()
{
InitializeComponent();
DataContext = MainViewModel.CreateViewModel;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = MainViewModel;
}
}
For example, if a MainViewModel contain a field ImageWidth setting ImageWidth in CreateUserControl change the value for ReadUserControl.
I hope to have been clear, I don't know how design my MainViewModel to achieve this result
EDIT1:
I've created the MainWindowViewModel as a Singleton but i'm still unable to get MainViewModel.CreateViewModel and MainViewModel.ReadViewModel
public class MainWindowViewModel : ViewModelBase
{
private static MainWindowViewModel _instance = null;
public static MainWindowViewModel Instance
{
get
{
if (_instance == null)
_instance = new MainWindowViewModel();
return _instance;
}
}
private MainWindowViewModel()
: base()
{
}
#region CreateViewModel
/* How to create ? */
#endregion
#region ReadViewModel
/* How to create ? */
#endregion
}
Your example will work. At least if you have made your MainViewModel a Singleton.
A more professional approach might be an Constructor-Injection like this.
public partial class ReadUserControl : UserControl
{
public ReadUserControl(MainViewModel vm)
{
InitializeComponent();
DataContext = vm.ReadViewModel;
}
}
With such DependencyInjections you can achieve a higher level of abstraction, since your UserControls can be generalized. (They will all have the same Constructor)
On the other hand, you give every such UserControl the ability, to manipulate the MainViewModel, not aware of side-effects.
In your special case, it would be more safe, to pass only the needed parameters to the UserControl, instead of giving them a bunch of informations, they will never need.
public partial class ReadUserControl : UserControl
{
public ReadUserControl(Icommand command, int imageLength, AppParams appParams)
{
InitializeComponent();
...
// Do with your Constructorparameters what ever you have to
}
}
Edit:
Here a small, dumb implementation of how it could be done:
Code
public class MainViewModel : INotifyPropertyChanged {
private INotifyPropertyChanged _selectedViewModel;
public MainViewModel() {
var cmd = new RelayCommand(x => {
MessageBox.Show("HelloWorld");
}, x => true);
this.RVM = new ReadViewModel(cmd);
this.WVM = new WriteViewModel(cmd);
this.SelectedViewModel = WVM;
}
private ICommand _switchViewModelCommand;
public ICommand SwitchViewModelCommand => this._switchViewModelCommand ?? (this._switchViewModelCommand = new RelayCommand(x => {
if (this.SelectedViewModel == RVM) {
this.SelectedViewModel = WVM;
return;
}
this.SelectedViewModel = RVM;
}));
public INotifyPropertyChanged SelectedViewModel {
get {
return this._selectedViewModel;
}
set {
if (Equals(value, this._selectedViewModel))
return;
this._selectedViewModel = value;
this.OnPropertyChanged();
}
}
public ReadViewModel RVM {
get; set;
}
public WriteViewModel WVM {
get; set;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ReadViewModel : INotifyPropertyChanged {
public ReadViewModel(ICommand sayHelloCommand) {
this.HelloCommand = sayHelloCommand;
}
public ICommand HelloCommand {
get;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class WriteViewModel : INotifyPropertyChanged {
public WriteViewModel(ICommand sayHelloCommand) {
this.HelloCommand = sayHelloCommand;
}
public ICommand HelloCommand {
get;
}
public ICommand HelloMoonCommand => new RelayCommand(x => { MessageBox.Show("Hello Moon"); });
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid Height="200">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ContentControl Content="{Binding SelectedViewModel, UpdateSourceTrigger=PropertyChanged}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:ReadViewModel}">
<StackPanel>
<Button Content="Say Hello world" Command="{Binding HelloCommand}"></Button>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:WriteViewModel}">
<StackPanel>
<Button Content="Say Hello world" Command="{Binding HelloCommand}"></Button>
<Button Content="Say Hello Moon" Command="{Binding HelloMoonCommand}"></Button>
</StackPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
<Button Content="Switch VM" Command="{Binding SwitchViewModelCommand}" Grid.Row="1"/>
</Grid>
You can pass in the MainViewModel as DataContext for your user control and set the data context of elements as Read/Create model
something like
<Grid> <!--using MainWindowViewModel as data context-->
<Grid DataContext="{Binding Path=CreateViewModel}"> <!--using CreateViewModel as data context-->
.....
</Grid>
<Grid>

how to create Master Page in MVVM?

I want to create master page in mvvm. I created a viewbox that it's name is container for showing my usercontrols and I have two classes, RelayCommand and ViewModel.
Here is my code:
public class ViewModel
{
MainWindow objMainWindow = new MainWindow();
UserControls.History objHistory = new UserControls.History();
UserControls.NewItem objNewItem = new UserControls.NewItem();
UserControls.SideEffect objSideEffect = new UserControls.SideEffect();
public ViewModel()
{
OpenCommand = new RelayCommand(Open);
}
private ICommand openCommand;
public ICommand OpenCommand
{
get { return openCommand; }
set { openCommand = value; }
}
public void Open(object sender)
{
if (sender.ToString() == "btnHistory")
{
objMainWindow.Container.Child = objHistory;
}
if (sender.ToString() == "btnNewItem")
{
}
if (sender.ToString() == "btnSideEffect")
{
}
}
}
And this is my RelayCommand:
public class RelayCommand:ICommand
{
public RelayCommand(Action<object> _action)
{
actionCommand = _action;
}
private Action<object> actionCommand;
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
if (parameter !=null)
{
actionCommand(parameter);
}
else
{
actionCommand("Null");
}
}
}
but when I run solution I faced with NullRefrenceException when it wanted to show my child of container.
I don't know how to make this work.
Your MainWindow instantiates when your program starts. So you shouldn't instantiate it again in your ViewModel (i.e. this line: MainWindow objMainWindow = new MainWindow();). You should use DataBinding instead.
Here is a sample code that gives you an idea:
First define a property of type FrameworkElement in you ViewModel and set it's value to your desired UserControl in the Open method.
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
FrameworkElement _myUc;
public FrameworkElement MyUserControl
{
get
{
return _myUc;
}
set
{
_myUc= value;
OnPropertyChanged("MyUserControl");
}
}
public ViewModel()
{
OpenCommand = new RelayCommand(Open);
}
public void Open(object sender)
{
if (sender.ToString() == "btnHistory")
{
MyUserControl = objHistory;
}
}
// rest of your view model ...
}
Then instantiate your ViewModel as the DataContext of your MainWindow in the Constructor.
MainWindow:
public ViewModel MyViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
MyViewModel = new ViewModel();
DataContext = MyViewModel;
}
And Finally use a ContentControl (instead of ViewBox) [see my note] and bind it's Content to the MyUserControl property of your ViewModel.
XAML:
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{Binding MyUserControl}" x:Name="Container"/>
<Button Grid.Row="1" Name="btnHistory" Content="ShowHistory" Command="{Binding OpenCommand}" />
</Grid>
This way each time MyUserControl changes, the ContentControl shows your desired UserControl.
Note that Child property of ViewBox is not a DependencyProperty and thus not bind-able.

Categories