I'm currently in the process of mastering the C# WPF MVVM pattern and have stumbled upon a pretty big hurdle...
What I am trying to do fire off a LoginCommand that when successfully executed will allow me to change the parent window's viewmodel. The only issue is I can't quite think of a way to change the parent window's viewmodel without breaking the MVVM design pattern because I can't access the parent window's ContentControl that sets its path to the active UserControlViewModel in the window.
Here's the scenario:
In our App.xaml we have two DataTemplates:
<DataTemplate DataType="{x:Type ViewModels:LoginViewModel}">
<Views:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:LoggedInViewModel}">
<Views:LoggedView />
</DataTemplate>
In our MainWindow we have:
<ContentControl Content="{Binding ViewModel}" />
The MainWindow code behind will set the ViewModel = LoginViewModel
In our LoginViewModel we have:
<Button Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=pwPasswordBoxControlInXaml}" />
Now for the money... the LoginCommand:
public void Execute(object parameter)
{
// Do some validation
// Async login task stuff
// ...
// Logged in... change the MainWindow's ViewModel to the LoggedInViewModel
}
How can I make the Execute method change the window's viewmodel without breaking the MVVM pattern?
Things I've tried thus far:
Making the MainWindow have a static Instance singleton that I can access and then change the ViewModel property from the command.
Attempting to implement some form of routed command listener in the MainWindow and then have commands fire off routed command events to be handled by the parent window.
I've done a quick demo to show one way of doing it. I've kept it as simple as possible to give the general idea. There are lots of different ways of accomplishing the same thing (e.g. you could hold a reference to MainWindowViewModel inside LoginViewModel, handle everything there then call a method on MainWindowViewModel to trigger the workspace change, or you could use Events/Messages, etc).
Definitely have a read of Navigation with MVVM though. That's a really good introduction that I found helpful when I was getting started with it.
The key thing to take away from this is to have an outer MainWindowViewModel or ApplicationViewModel which handles the navigation, holds references to workspaces, etc. Then the choice of how you interact with this is up to you.
In the code below, I've left out the clutter from defining Window, UserControl, etc. to keep it shorter.
Window:
<DockPanel>
<ContentControl Content="{Binding CurrentWorkspace}"/>
</DockPanel>
MainWindowViewModel (this should be set as the DataContext for the Window):
public class MainWindowViewModel : ObservableObject
{
LoginViewModel loginViewModel = new LoginViewModel();
LoggedInViewModel loggedInViewModel = new LoggedInViewModel();
public MainWindowViewModel()
{
CurrentWorkspace = loginViewModel;
LoginCommand = new RelayCommand((p) => DoLogin());
}
private WorkspaceViewModel currentWorkspace;
public WorkspaceViewModel CurrentWorkspace
{
get { return currentWorkspace; }
set
{
if (currentWorkspace != value)
{
currentWorkspace = value;
OnPropertyChanged();
}
}
}
public ICommand LoginCommand { get; set; }
public void DoLogin()
{
bool isValidated = loginViewModel.Validate();
if (isValidated)
{
CurrentWorkspace = loggedInViewModel;
}
}
}
LoginView:
In this example I'm binding a Button on the LoginView to the LoginCommand on the Window DataContext (i.e. MainWindowViewModel).
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding UserName}"/>
<Button Content="Login" Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.LoginCommand}"/>
</StackPanel>
LoginViewModel:
public class LoginViewModel : WorkspaceViewModel
{
private string userName;
public string UserName
{
get { return userName; }
set
{
if (userName != value)
{
userName = value;
OnPropertyChanged();
}
}
}
public bool Validate()
{
if (UserName == "bob")
{
return true;
}
else
{
return false;
}
}
}
LoggedInView:
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding RestrictedData}"/>
</StackPanel>
LoggedInViewModel:
public class LoggedInViewModel : WorkspaceViewModel
{
private string restrictedData = "Some restricted data";
public string RestrictedData
{
get { return restrictedData; }
set
{
if (restrictedData != value)
{
restrictedData = value;
OnPropertyChanged();
}
}
}
}
WorkspaceViewModel:
public abstract class WorkspaceViewModel : ObservableObject
{
}
Then some other classes you probably already have implemented (or alternatives).
ObservableObject:
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
}
RelayCommand:
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{ }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return canExecute == null ? true : canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
}
App.Xaml:
<DataTemplate DataType="{x:Type ViewModels:LoginViewModel}">
<Views:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:LoggedInViewModel}">
<Views:LoggedInView />
</DataTemplate>
<ContentControl Content="{Binding ViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type vm:LoginViewModelClass}">
<!-- some LoginView -->
</DataTemplate>
<DataTemplate DataType="{x:Type vm:LoggedInViewModelClass}">
<!-- some LoggedInView -->
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
I have a very simple solution below for tabs being populated based on MVVM. How do I setup the following two commands, and 'Add' and 'Remove'. From what I've read online it appears I need to setup ICommand or something along those lines. It wasn't clear enough to me in the demos for me to get it working.
The Add command would call the already existing function in the ViewModel class. Bu it would be called by a key command 'Ctrl + N'
The Remove command would be called when the user clicks the 'X' button, which would remove that particular tab. Otherwise it can be called by 'Ctrl + W' which would close whichever tab is currently selected.
The command stuff is new to me, so if someone could help me out it would be greatly appreciated. I hope to expand upon this and continue adding more to the tool.
Link to visual studio dropbox files. You'll see I've broken things out to classes and organized it in a way that makes things clear.
Snippets of tool below...
View Model
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication1
{
public class ViewModel : ObservableObject
{
private ObservableCollection<TabItem> tabItems;
public ObservableCollection<TabItem> TabItems
{
get { return tabItems ?? (tabItems = new ObservableCollection<TabItem>()); }
}
public ViewModel()
{
TabItems.Add(new TabItem { Header = "One", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Two", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString() });
}
public void AddContentItem()
{
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString() });
}
}
public class TabItem
{
public string Header { get; set; }
public string Content { get; set; }
}
}
MainWindow XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="250">
<Window.DataContext>
<data:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<Button Content="Add" Command="{Binding AddCommand}" Grid.Row="0"></Button>-->
<TabControl ItemsSource="{Binding TabItems}" Grid.Row="1" Background="LightBlue">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding Header}" VerticalAlignment="Center"/>
<Button Content="x" Width="20" Height="20" Margin="5 0 0 0"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
You've received two other answers already. Unfortunately, neither precisely addresses both the Add and Remove commands. Also, one prefers to focus primarily on code-behind implementation rather than XAML declarations, and is fairly sparse on details anyway, while the other more-correctly focuses on implementation in XAML where appropriate, but does not include correct, working code, and (slightly) obfuscates the answer by introducing the extra abstraction of the RelayCommand type.
So, I will offer my own take on the question, with the hopes this will be more useful to you.
While I agree that abstracting the ICommand implementation into a helper class such as RelayCommand is useful and even desirable, unfortunately this tends to hide the basic mechanisms of what's going on, and requires a more elaborate implementation that was offered in the other answer. So for now, let's ignore that.
Instead, just focus on what does need to be implemented: two different implementations of the ICommand interface. Your view model will expose these as the values of two bindable properties representing the commands to be executed.
Here is a new version of your ViewModel class (with the irrelevant and unprovided ObservableObject type removed):
class ViewModel
{
private class AddCommandObject : ICommand
{
private readonly ViewModel _target;
public AddCommandObject(ViewModel target)
{
_target = target;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_target.AddContentItem();
}
}
private class RemoveCommandObject : ICommand
{
private readonly ViewModel _target;
public RemoveCommandObject(ViewModel target)
{
_target = target;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_target.RemoveContentItem((TabItem)parameter);
}
}
private ObservableCollection<TabItem> tabItems;
public ObservableCollection<TabItem> TabItems
{
get { return tabItems ?? (tabItems = new ObservableCollection<TabItem>()); }
}
public ICommand AddCommand { get { return _addCommand; } }
public ICommand RemoveCommand { get { return _removeCommand; } }
private readonly ICommand _addCommand;
private readonly ICommand _removeCommand;
public ViewModel()
{
TabItems.Add(new TabItem { Header = "One", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Two", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString() });
_addCommand = new AddCommandObject(this);
_removeCommand = new RemoveCommandObject(this);
}
public void AddContentItem()
{
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString() });
}
public void RemoveContentItem(TabItem item)
{
TabItems.Remove(item);
}
}
Note the two added nested classes, AddCommandObject and RemoveCommandObject. These are both examples of nearly the simplest implementation of ICommand possible. They can always be executed, and so the return value of CanExecute() never changes (so there's no need to ever raise the CanExecuteChanged event). They do need the reference to your ViewModel object so that they can each call the appropriate method.
There are also two public properties added to allow binding of these commands. Of course, the RemoveContentItem() method needs to know what item to remove. This needs to be set up in the XAML, so that the value can be passed as a parameter to the command handler and from there to the actual RemoveContentItem() method.
In order to support the use of the keyboard for the commands, one approach is to add input bindings to the window. This is what I've chosen here. The RemoveCommand binding additionally needs the item to be deleted to be passed as the command parameter, so this is bound to the CommandParameter for the KeyBinding object (just as for the CommandParameter of the Button in the item).
The resulting XAML looks like this:
<Window.DataContext>
<data:ViewModel/>
</Window.DataContext>
<Window.InputBindings>
<KeyBinding Command="{Binding AddCommand}">
<KeyBinding.Gesture>
<KeyGesture>Ctrl+N</KeyGesture>
</KeyBinding.Gesture>
</KeyBinding>
<KeyBinding Command="{Binding RemoveCommand}"
CommandParameter="{Binding SelectedItem, ElementName=tabControl1}">
<KeyBinding.Gesture>
<KeyGesture>Ctrl+W</KeyGesture>
</KeyBinding.Gesture>
</KeyBinding>
</Window.InputBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabControl x:Name="tabControl1" ItemsSource="{Binding TabItems}" Grid.Row="1" Background="LightBlue">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding Header}" VerticalAlignment="Center"/>
<Button Content="x" Width="20" Height="20" Margin="5 0 0 0"
Command="{Binding DataContext.RemoveCommand, RelativeSource={RelativeSource AncestorType=TabControl}}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}">
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
EDIT:
As I mentioned above, there is in fact benefit to abstracting the ICommand implementation, using a helper class instead of declaring a new class for every command you want to implement. The referenced answer at Why RelayCommand mentions loose coupling and unit testing as motivations. While I agree these are good goals, I can't say that these goals are in fact served per se by the abstraction of the ICommand implementation.
Rather, I see the benefits as being the same ones primarily found when making such abstractions: it allows for code reuse, and in doing so improves developer productivity, along with code maintainability and quality.
In my above example, every time you want a new command, you have to write a new class that implements ICommand. On the one hand, this means that each class you write can be tailor-made to the specific purpose. Dealing with CanExecuteChanged or not, as the case requires, passing parameters or not, etc.
On the other hand, every time you write such a class, that's an opportunity to write a new bug. Worse, if you introduce a bug which is then later copy/pasted, then when you eventually find the bug, you may or may not fix it everywhere it exists.
And of course, writing such classes over and over gets tedious and time-consuming.
Again, these are just specific examples of the general conventional wisdom of the "best practice" of abstracting reusable logic.
So, if we've accepted that an abstraction is useful here (I certainly have :) ), then the question becomes, what does that abstraction look like? There are a number of different ways to approach the question. The referenced answer is one example. Here is a slightly different approach that I've written:
class DelegateCommand<T> : ICommand
{
private readonly Func<T, bool> _canExecuteHandler;
private readonly Action<T> _executeHandler;
public DelegateCommand(Action<T> executeHandler)
: this(executeHandler, null) { }
public DelegateCommand(Action<T> executeHandler, Func<T, bool> canExecuteHandler)
{
_canExecuteHandler = canExecuteHandler;
_executeHandler = executeHandler;
}
public bool CanExecute(object parameter)
{
return _canExecuteHandler != null ? _canExecuteHandler((T)parameter) : true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_executeHandler((T)parameter);
}
public void RaiseCanExecuteChanged()
{
EventHandler handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
In the ViewModel class, the above would be used like this:
class ViewModel
{
private ObservableCollection<TabItem> tabItems;
public ObservableCollection<TabItem> TabItems
{
get { return tabItems ?? (tabItems = new ObservableCollection<TabItem>()); }
}
public ICommand AddCommand { get { return _addCommand; } }
public ICommand RemoveCommand { get { return _removeCommand; } }
private readonly ICommand _addCommand;
private readonly ICommand _removeCommand;
public ViewModel()
{
TabItems.Add(new TabItem { Header = "One", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Two", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString() });
// Use a lambda delegate to map the required Action<T> delegate
// to the parameterless method call for AddContentItem()
_addCommand = new DelegateCommand<object>(o => this.AddContentItem());
// In this case, the target method takes a parameter, so we can just
// use the method directly.
_removeCommand = new DelegateCommand<TabItem>(RemoveContentItem);
}
Notes:
Of course, now the specific ICommand implementations are no longer needed. The AddCommandObject and RemoveCommandObject classes have been removed from the ViewModel class.
In their place, the code uses the DelegateCommand<T> class.
Note that in some cases, the command handler is not going to need the parameter passed to the ICommand.Execute(object) method. In the above, this is addressed by accepting the parameter in a lambda (anonymous) delegate, and then ignoring it while calling the parameterless handler method. Other ways to approach this would be to have the handler method accept the parameter but then ignore it, or to have a non-generic class in which the handler delegate itself can be parameterless. IMHO, there's no "right way" per se…just various choices that may be considered more or less preferable according to personal preference.
Note also that this implementation differs from the referenced answer's implementation in the handling of the CanExecuteChanged event. In my implementation, the client code is given fine-grained control over that event, at the expense of requiring the client code to retain a reference to the DelegateCommand<T> object in question and calling its RaiseCanExecuteChanged() method at the appropriate time. In the other implementation, it instead relies on the CommandManager.RequerySuggested event. This is a trade-off of convenience vs efficiency and, in some cases, correctness. That is, it is less convenient for the client code to have to retain references to commands which may change the executable status, but if one goes the other route, at the very least the CanExecuteChanged event may be raised much more often than is required, and in some cases it's even possible it may not be raised when it should have been (which is far worse than the possible inefficiency).
On that last point, yet another approach would be to make the ICommand implementation a dependency object, and provide a dependency property that is used to control the executable state of the command. This is a lot more complicated, but overall could be considered the superior solution as it allows fine-grained control over the CanExecuteChanged event's raising, while providing a good, idiomatic way to bind the executable state of the command, e.g. in XAML to whatever property or properties actually determine said executability.
Such an implementation might look something like this:
class DelegateDependencyCommand<T> : DependencyObject, ICommand
{
public static readonly DependencyProperty IsExecutableProperty = DependencyProperty.Register(
"IsExecutable", typeof(bool), typeof(DelegateCommand<T>), new PropertyMetadata(true, OnIsExecutableChanged));
public bool IsExecutable
{
get { return (bool)GetValue(IsExecutableProperty); }
set { SetValue(IsExecutableProperty, value); }
}
private static void OnIsExecutableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DelegateDependencyCommand<T> command = (DelegateDependencyCommand<T>)d;
EventHandler handler = command.CanExecuteChanged;
if (handler != null)
{
handler(command, EventArgs.Empty);
}
}
private readonly Action<T> _executeHandler;
public DelegateDependencyCommand(Action<T> executeHandler)
{
_executeHandler = executeHandler;
}
public bool CanExecute(object parameter)
{
return IsExecutable;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_executeHandler((T)parameter);
}
}
In the above, the canExecuteHandler argument for the class is eliminated, in lieu of the IsExecutable property. When that property changes, the CanExecuteChanged event is raised. IMHO it's unfortunate that there is this discrepancy in the ICommand interface between how it's designed and how WPF normally works (i.e. with bindable properties). It's a bit weird that we have essentially a property but which is exposed via an explicit getter method named CanExecute().
On the other hand, this discrepancy does serve some useful purposes, including making explicit and convenient the use of the CommandParameter for both the execution of the command, and the checking for executability. These are worthwhile goals. I'm just not sure personally whether I'd have made the same choice balancing them with the consistency of the usual way state is connected within WPF (i.e. through binding). Fortunately, it's simple enough to implement the ICommand interface in a bindable way (i.e. as above), if that's really desired.
You can start with remove.
First, you will need to create a RelayCommand class. More info about RelayCommand, see this post: Why RelayCommand
public class RelayCommand : ICommand
{
#region Private members
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
private readonly Action execute;
/// <summary>
/// True if command is executing, false otherwise
/// </summary>
private readonly Func<bool> canExecute;
#endregion
/// <summary>
/// Initializes a new instance of <see cref="RelayCommand"/> that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action execute): this(execute, canExecute: null)
{
}
/// <summary>
/// Initializes a new instance of <see cref="RelayCommand"/>.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
/// <summary>
/// Raised when RaiseCanExecuteChanged is called.
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// Determines whether this <see cref="RelayCommand"/> can execute in its current state.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed, this object can be set to null.
/// </param>
/// <returns>True if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
return this.canExecute == null ? true : this.canExecute();
}
/// <summary>
/// Executes the <see cref="RelayCommand"/> on the current command target.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed, this object can be set to null.
/// </param>
public void Execute(object parameter)
{
this.execute();
}
/// <summary>
/// Method used to raise the <see cref="CanExecuteChanged"/> event
/// to indicate that the return value of the <see cref="CanExecute"/>
/// method has changed.
/// </summary>
public void RaiseCanExecuteChanged()
{
var handler = this.CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
Next, add a "bindable" Remove property in your ViewModel of type RelayCommand. This is what your TabItem buttons will bind to, and how it will notify the ViewModel it was pressed. The ReplayCommand will have an execute method that will get called whenever a press occurs. Here, we remove our-self from the overall TabItem list.
public class ViewModel : ObservableObject
{
private ObservableCollection<TabItem> tabItems;
private RelayCommand<object> RemoveCommand;
public ObservableCollection<TabItem> TabItems
{
get { return tabItems ?? (tabItems = new ObservableCollection<TabItem>()); }
}
public ViewModel()
{
TabItems.Add(new TabItem { Header = "One", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Two", Content = DateTime.Now.ToLongDateString() });
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString() });
RemoveCommand = new RelayCommand<object>(RemoveItemExecute);
}
public void AddContentItem()
{
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString() });
}
private void RemoveItemExecute(object param)
{
var tabItem = param as TabItem;
if (tabItem != null)
{
TabItems.Remove(tabItem);
}
}
}
Now, update your XAML. Each TabItem will need to bind to the RemoveCommand in the parent ViewModel and pass itself in as a parameter. We can do this like:
<!--<Button Content="Add" Command="{Binding AddCommand}" Grid.Row="0"></Button>-->
<TabControl x:Name="TabItems" ItemsSource="{Binding TabItems}" Grid.Row="1" Background="LightBlue">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding Header}" VerticalAlignment="Center"/>
<Button Command="{Binding ElementName=TabItems, Path=DataContext.RemoveCommand}"
CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}"
Content="x"
Width="20"
Height="20"
Margin="5 0 0 0"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
At first u need to setup your commands
public static class Commands
{
private static RoutedUICommand add;
private static RoutedUICommand remove;
static Commands()
{
searchValue = new RoutedUICommand("Add", "Add", typeof(Commands));
showCSCode = new RoutedUICommand("Remove", "Remove", typeof(Commands));
add.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control));
remove.InputGestures.Add(new KeyGesture(Key.X));
}
public static RoutedUICommand Add { get { return add; } }
public static RoutedUICommand Remove { get { return remove; } }
}
In window loaded event you have to bind the command methods
<window ... Loaded="window_loaded">
The cs file
CommandBindings.Add(new CommandBinding(Commands.Remove, HandleRemoveExecuted, HandleCanRemoveExecuted));
Is command enabled:
private void HandleCanAddExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
What should the command do:
private void HandleAddExecute(object sender, ExecutedRoutedEventArgs e)
{
AddContentItem();
}
At last you just have to edit your existing files with
TabItems.Add(new TabItem { Header = "Three", Content = DateTime.Now.ToLongDateString(),
CommandBindings.Add(new CommandBinding(Commands.Add, HandleAddExecuted, HandleCanAddExecuted)); });
xaml:
<Window ...
xmlns:commands="clr-namespace:<NAMESPACE>">
<Button Content="x" Width="20"
Height="20" Margin="5 0 0 0"
Command="{x:Static commands:Commands.Remove}"/>
In this code:
<ListBox
x:Name="DataList1"
xmlns:local="clr-namespace:xaml_binding_commands"
>
<ListBox.Resources>
<local:CommandUp x:Key="CommandUp1"></local:CommandUp>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox
Text="{Binding Height,Mode=TwoWay,StringFormat=00\{0:d\}}"
InputScope="Number"/>
<RepeatButton
Content="+"
Command="{StaticResource CommandUp1}"
CommandParameter="{Binding }"
/>
<TextBox
Text="{Binding Weight,Mode=TwoWay}"
InputScope="Number"/>
<RepeatButton
Content="+"
Command="{StaticResource CommandUp1}"
CommandParameter="{Binding Weight}"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And this
namespace xaml_binding_commands
{
public class CommandUp : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter is Entry)
{
Entry EntryToUp = (Entry)parameter;
EntryToUp.Height +=1; // no worky, which field to increment?!
}
if (parameter is Int32)
{
Int32 EntryFieldToUp = (Int32)parameter;
EntryFieldToUp += 1; // no worky
}
}
}
public class Entry : INotifyPropertyChanged
{
private Int32 _Height;
public Int32 Height
{
get { return _Height; }
set { _Height = value; PropChange("Height"); }
}
private Int32 _Weight;
public Int32 Weight
{
get { return _Weight; }
set { _Weight = value; PropChange("Weight"); }
}
private void PropChange(String PropName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private ObservableCollection<Entry> _People = new ObservableCollection<Entry>();
public ObservableCollection<Entry> People
{
get { return _People; }
set { _People = value; }
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
DataList1.ItemsSource = People;
People.Add( new Entry() { Height=67, Weight=118 } );
}
}
}
Can I pass the field that the textbox is bound to by reference? If I pass the entire class, an Entry, to the CommandParameter so it can operate and increment, I have no way of knowing which set of TextBoxes and Buttons caused the Command.Execute. If I pass the same thing that the TextBox is bound to, namely, individually: Weight,Height then the Command.Execute has no way of affecting the input. Usually I would PassByReference and my Int32 would be boxed, and my general operate function could operate on the by-ref parameter. Can I do this somehow in XAML?
If I was doing this, I would use MVVM Light and RelayCommand<string>. This means that you can pass in one (or more) parameters in as part of the binding to the ICommand.
This means that you could have multiple bindings to a single event handler attached to a button, and each binding could have a different parameter that let you know where it came from.
Update 1
MVVM Light is an MVVM library that is compatible with pretty much everything, from standard WPF to Windows 8.1 to Windows phone. See http://www.mvvmlight.net/. It is probably the most popular MVVM lib according to NuGet download stats, and the one that I tend to prefer.
For an example of how to use MVVM light with a CommandParameter, see the top voted answer at MVVM Light RelayCommand Parameters.
For an example of how to pass in two or more parameters to a RelayCommand, see How to Passing multiple parameters RelayCommand?
Update 2
Just looking at your code, I would use MVVM. I generally prefer MVVM to code behind (this is a whole discussion in itself). If you put all of your data in the ViewModel, and used bindings to let your XAML View update the ViewModel, I think things would become a lot easier to develop and maintain.
I would like to understand how to correctly use MVVM and data binding when we are working with many properties.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="463" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" />
<Label Height="28" HorizontalAlignment="Left" Margin="12,242,0,0" Name="label1" VerticalAlignment="Top" Width="463" Content="{Binding ModifiedText}"/>
<CheckBox Content="Upper" Height="16" HorizontalAlignment="Left" Margin="12,41,0,0" Name="checkBox1" VerticalAlignment="Top" />
<CheckBox Content="Underline" Height="16" HorizontalAlignment="Left" Margin="12,63,0,0" Name="checkBox2" VerticalAlignment="Top" />
<CheckBox Content="Bold" Height="16" HorizontalAlignment="Left" Margin="12,85,0,0" Name="checkBox3" VerticalAlignment="Top" />
<CheckBox Content="Shadow" Height="16" HorizontalAlignment="Left" Margin="12,107,0,0" Name="checkBox4" VerticalAlignment="Top" />
<CheckBox Content="Red" Height="16" HorizontalAlignment="Left" Margin="12,129,0,0" Name="checkBox5" VerticalAlignment="Top" />
<CheckBox Content="Scary" Height="16" HorizontalAlignment="Left" Margin="12,151,0,0" Name="checkBox6" VerticalAlignment="Top" />
<CheckBox Content="Remove first letter" Height="16" HorizontalAlignment="Left" Margin="12,173,0,0" Name="checkBox7" VerticalAlignment="Top" />
<CheckBox Content="Remove last letter" Height="16" HorizontalAlignment="Left" Margin="12,195,0,0" Name="checkBox8" VerticalAlignment="Top" />
</Grid>
I have a OriginalText TextBox and a ModifiedText Label. When I check a box I would like to directly apply the modification without having to click a button. How should I do that?
In my ViewModel I created all the properties that are binded to the XAML CheckBox.
private string _originalText = string.Empty;
public string OriginalText
{
get { return _originalText; }
set
{
_originalText = value;
NotifyPropertyChanged("OriginalText");
}
}
private string _modifiedText;
public string ModifiedText
{
get { return _originalText; }
set
{
_originalText = value;
NotifyPropertyChanged("ModifiedText");
}
}
private bool upper;
public bool Upper
{
get { return upper; }
set
{
upper = value;
NotifyPropertyChanged("Upper");
// Should I notify something else here or call a refresh method?
}
}
private bool removeFirstLetter;
public bool RemoveFirstLetter
{
get { return removeFirstLetter; }
set
{
removeFirstLetter = value;
NotifyPropertyChanged("RemoveFirstLetter");
// Should I notify something else here or call a refresh method?
}
}
// ...
Then I created a Work method in the same ViewModel class at this moment. I ll move this method into the business later.
private void Work()
{
string result = _originalText;
if (Upper)
result = result.ToUpper();
if (removeFirstLetter)
result = result.Substring(1, result.Length);
// if ...
ModifiedText = result;
}
My question is when, where should I call the work method? Should I call it in each setter or getter? I dont like the idea. I do something wrong...
Thank you.
In your particular case, you should create a Boolean property using the INotifyPropertyChanged interface. Now bind this property to your "IsChecked" check box property. By calling your Work() method inside the setter, every time the check box is "ticked" the setter will trigger each time.
The answer to your question is very simple: Use Commands.
Commands are MVVM's way to realize the binding to a method in your ViewModel. The implementation of Commands follows a very standard pattern. You will find plenty of information over the Internet here is just a short sketch:
Commands implemented in your ViewModel have to be of type ICommand and every Command has to come along with to methods in your code one responsible for executing the actual method and the other one for checking if the execution is currently possible.
These methods have to be named CanExecute and Execute respectively. It is commonly the case to facilitate the use of several Commands with a small helping class called DelegateCommand which provides delegates for the previously mentioned methods.
Take this class as it is without any modifications:
public class DelegateCommand<T> : ICommand {
private Predicate<T> canExecute;
private Action<T> execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand (Predicate<T> canExecute, Action<T> execute) {
this.canExecute = canExecute;
this.execute = execute;
}
public bool CanExecute (object param) {
return canExecute((T)param);
}
public void Execute (object param) {
execute((T)param);
}
public void CanExecuteChangedRaised () {
CanExecuteChanged(this, new EventArgs());
}
}
Then your Command declarations are of type DelegateCommand rather than of type ICommand. See the following example to illustrate and you will get the idea:
Supose you have a method foo() in your ViewModel you want to be called with a click to a button:
class ViewModel {
// ...
public DelegateCommand<object> FooCommand { get; set; }
public ViewModel () {
FooCommand = new DelegateCommand<object>(CanExecuteFooCommand, ExecuteFooCommand);
}
public bool CanExecuteFooCommand (object param) {
return true;
}
public void ExecuteFooCommand (object param) {
foo();
}
// ...
}
Supposing you have set your ViewModel as the controls DataContext via it's DataContext property the only thing left to do is to bind the FooCommand to your button like this:
That's it!
APPENDIX (referring to comment):
In order to have some action take place without actually hitting the Button you would simply have to track any changed in the UI with your ViewModel and react accordingly - that's what MVVM is about: Track the data from the UI modify or process them and populate them back to the UI.
To react on a TextBox Text change create a corresponding string property in your ViewModel and track whether the new ioncoming value from the View is different to the current textBox text:
private string _text;
public string Text {
get { return _text; }
set {
// the text in the TextBox is about to change.
if (!_text.Equals(value))
{
doSomething();
}
_text = value;
FirePropertyChanged("Text");
}
}
For doing the same with your CheckBox you can apply ICommand as described above since CheckBox is derived from Button and is therefor offering the Command property.
I'm using two views which refers same view model. Both of my views contain a text box that binds to a value in the view model. My problem is that, if I change the value of textbox in one GUI, its not reflecting in another. What should I do to achieve this?
This is my view model
public class ProductViewModel:INotifyPropertyChanged
{
private int machineheight;
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public int MachineHeight
{
get
{
return this.machineheight;
}
set
{
this.machineheight = value;
RaisePropertyChanged("MachineHeight");
}
}
public ProductViewModel()
{
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater();
return mUpdater;
}
set
{
mUpdater = value;
}
}
private class Updater : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
SecondWindow w = new SecondWindow();
w.Show();
}
#endregion
}
}
}
The second window is another GUI. Once I click update button, second window opened. But the value that I have changed in first UI is not updated in the new window.
My Xaml is similar for both UI..
<Window x:Class="WPFDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFDemo"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ProductViewModel/>
</Window.DataContext>
<Grid Height="307" Width="480" Initialized="Grid_Initialized">
<Button Content="Update" Height="32" HorizontalAlignment="Left" Margin="165,158,0,0" Name="button1" VerticalAlignment="Top" Width="114" Command="{Binding Path=UpdateCommand}"/>
<TextBox Height="42" HorizontalAlignment="Left" Margin="125,82,0,0" Name="textBox1" VerticalAlignment="Top" Width="169" Text= "{Binding Path= MachineHeight, Mode=TwoWay}" />
</Grid>
</Window>
I actually don't know what is the problem.. thanks
<Window.DataContext>
<local:ProductViewModel/>
</Window.DataContext>
hi, if you put this in your 2 views, then each one has its own viewmodel. so you will never see any changes. you have to set the datacontext from your first view to your second view. Btw for your ICommand implementation look at some mvvm frameworks for easier implementations, eg RelayCommand, DelegateCommand.
For your actual implementation you can add the following to your xaml and ViewModel(CommandParameter) then it works.
<Button Content="Update" Height="32" HorizontalAlignment="Left" Margin="165,158,0,0" Name="button1" VerticalAlignment="Top" Width="114" Command="{Binding Path=UpdateCommand}"
CommandParameter="{Binding .}"/>
public void Execute(object parameter)
{
SecondWindow w = new SecondWindow();
w.DataContext = parameter;
w.Show();
}
There are a hundred things that can go wrong in this scenario, and one of my long-standing gripes with XAML-based databinding is that the MS tools give you precious little help figuring out which of those hundred things it is. This is especially the case if you're new to databinding, but even folks who've been doing it for years can spend obnoxious hours tracking down databinding issues.
Some things to check:
(1) Confirm that your databindings are two-way.
(2) Look in your debug output window to see if there are any error messages there.
(3) Set an IValueConverter in your databinding, and set a breakpoint in the converter to see what data is being passed where and when.
(4) Confirm that the data in the ViewModel is actually being updated.
(5) Confirm that the ViewModel implements INotifyPropertyChanged, and that the PropertyChanged event is firing.
(6) Post your actual code here so folks can look at it.
And so forth.
Hope this helps.
It must work if the ViewModel implements INotifyPropertyChanged.