WPF Close Window Closes Source Window. Why? - wpf

The problem that I am having is a little hard to describe, so please hear it out.
I'm simply opening one window from another and then trying to close the second one. If I use Command of the InputBindings of the second one, the second one closes fine. If I call the close directly it closes both the first and second window. I expect code will help is this scenario.
WPF: Window1View (key part)
<Grid>
<Button Content="Button" Command="{Binding RptKeyDownCommand}" />
</Grid>
Window1ViewModel: (shortened for listing)
using GalaSoft.MvvmLight.Command;
var _runCommand = new RelayCommand(() => Run(), () => CanRun());
public void Run()
{
var v = new Window2();
var vm = new Window2ViewModel();
vm.RequestClose += v.Close;
v.DataContext = vm;
v.ShowDialog();
}
public event Action RequestClose;
var _closeCommand = new RelayCommand(() => Close(), () => CanClose());
public void Close()
{
if (RequestClose != null)
RequestClose();
}
WPF: Window2View
<Window.InputBindings>
<KeyBinding Key="Escape" Command="{Binding CloseCommand}" />
</Window.InputBindings>
<TextBox Text="Hello">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<cmd:EventToCommand
Command="{Binding Close2Command, Mode=OneWay}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Window2ViewModel: (has the same Close Command plus EventToCommand end point)
var _close2Command = new RelayCommand<KeyEventArgs>(p => Close2(p), p => CanClose2(p));
public void Close2(KeyEventArgs e)
{
if (e.Key == Key.Escape)
Close(); <- Here closes both Window1View and Window2View?
}

See this answer on your other thread for a solution.

From Window2ViewModel you should Call RequestClose not Close.
Here is the code for Window2ViewModel
RelayCommand _close2Command;
public ICommand Close2Command
{
get
{
if (_close2Command == null)
{
_close2Command = new RelayCommand(param => CloseEx(), param => CanClose());
}
return _close2Command;
}
}
public virtual void CloseEx()
{
Close();
}
public event Action RequestClose;
public virtual void Close()
{
if (RequestClose != null)
{
*RequestClose();*
}
}
public virtual bool CanClose()
{
return true;
}
Also Window1ViewModel should have code as :
using GalaSoft.MvvmLight.Command;
var _runCommand = new RelayCommand(() => Run(), () => CanRun());
var vm;
public void Run()
{
var v = new Window2();
vm = new Window2ViewModel();
vm.RequestClose += CloseV2;
v.DataContext = vm;
v.ShowDialog();
}
public event Action RequestClose;
var _closeCommand = new RelayCommand(() => Close(), () => CanClose());
public void CloseV2()
{
vm.Close();
}
public void Close()
{
if (RequestClose != null)
RequestClose();
}
Try to understand your code.
Note in you code you are binding event of both V1.RequestClose to V2.RequestClose to same method Close. In my case I have them separate and V2.RequestClose will always call vm.Close.
hope this helps.

Related

EventAggregator with prism in a MVVM Wpf application, subscriber VM can't get the data

I have been trying to implement the prism EventAggregator in my MVVM Wpf application. I made this I'm about to show with inspiration from this blog post: Prism EventAggregator
The overall goal is sending a list to the other viewModel.
Have a ViewModel as a Publisher, where I'm trying to publish an ObserverableCollection.
Event class:
public class RoomsSelectedEvent : PubSubEvent<ObservableCollection<Room>>
{
}
I'm using Unity to inject the IEventAggregator interface, like:
Unity startup method:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//view & viewModels
_container = new UnityContainer();
_container.RegisterType<IViewMainWindowViewModel, MainWindow>();
_container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
_container.RegisterType<IViewBookingViewModel, BookingView>();
_container.RegisterType<IViewBookingViewModel, BookingViewModel>();
_container.RegisterType<IViewContactViewModel, ContactDetailsView>();
_container.RegisterType<IViewContactViewModel, ContactViewModel>();
_container.RegisterType<IGetRoomsService, GetRoomsService>();
_container.RegisterType<IPostReservationService, PostReservationService>();
_container.RegisterType<IGetReservationsListService, GetReservationsListService>();
//types
_container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
_container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));
_container.Resolve<MainWindow>().Show();
}
In my publisher ViewModel, I'm creating the event.
Publisher ViewModel:
public class BookingViewModel : INotifyPropertyChanged, IViewBookingViewModel
{
//aggregator
protected readonly IEventAggregator _eventAggregator;
//commands
public ICommand ContinueCommand { get; set; }
public ObservableCollection<Room> RoomsList { get; private set; }
public ObservableCollection<RoomList> DropDownRooms { get; private set; }
public ObservableCollection<CustomerList> DropDownCustomers { get; private set; }
//enities
private readonly IDialogService<ContactDetailsView> _dialogServiceContactView;
private readonly IGetRoomsService _getRoomsService;
public BookingViewModel(IDialogService<ContactDetailsView> dialogServiceContactview, IGetRoomsService GetRoomsService, IEventAggregator eventAggregator)
{
// Injection
_dialogServiceContactView = dialogServiceContactview;
_getRoomsService = GetRoomsService;
_eventAggregator = eventAggregator;
//Instantiation
RoomsList = new ObservableCollection<Room>();
//Instantiation commands
ContinueCommand = new RelayCommand(ContinueCommand_DoWork, () => true);
}
// Continue Command
public void ContinueCommand_DoWork(object obj)
{
ObservableCollection<Room> RoomsSelected = new ObservableCollection<Room>();
ObservableCollection<Room> RoomsListNew = new ObservableCollection<Room>();
RoomsSelected = _getRoomsService.FilterSelectedRooms(RoomsList);
//Publish event:
_eventAggregator.GetEvent<RoomsSelectedEvent>().Publish(RoomsSelected);
_eventAggregator.GetEvent<RoomsSelectedEvent>().Subscribe((data) => { RoomsListNew = data; });
// Open new dialog
_dialogServiceContactView.ShowDialog();
}
}
On my Subscriber ViewModel, I need to retrive this list.
Subscriber ViewModel:
public class ContactViewModel : IViewContactViewModel, INotifyPropertyChanged
{
//aggregator
protected readonly IEventAggregator _eventAggregator;
//properties
public ObservableCollection<Room> SelectedRooms { get; set; }
public ContactViewModel(IEventAggregator eventAggregator)
{
//Injection
_eventAggregator = eventAggregator;
//Subscripe to event
_eventAggregator.GetEvent<RoomsSelectedEvent>().Subscribe(handleSelectedRoomsEvent);
_eventAggregator.GetEvent<RoomsSelectedEvent>().Subscribe((data) => { SelectedRooms = data; });
}
private void handleSelectedRoomsEvent(ObservableCollection<Room> room)
{
if (room != null)
{
SelectedRooms = room;
}
}
public ObservableCollection<Room> Rooms
{
get { return SelectedRooms; }
set { SelectedRooms = value; NotifyPropertyChanged(); }
}
}
When I debug, the handleSelectedRoomsEvent method is not even being called. Also the:
Subscribe((data) => { SelectedRooms = data; });
Here data and SelectedRooms is equal to null all the time. I can see in debug mode that an event is fired.
Hope someone can see what could be wrong here.
Note: Using the prism.Core 6.1 version. (Have also tried prism version 5.0)
Good day!
Forgot to mention that in my view, I'm using a ListBox where the itemsSource should get the selectedRooms (the one I'm subscribing to)
<ListBox Foreground="#ffffff" Background="#336699" BorderBrush="#336699" ItemsSource="{Binding Rooms, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RoomNumber}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Beds}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding RoomType}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You're not doing it right. You need to be creating a bootstrapper in the OnStartup and nothing else. You should not be creating an instance of the Unity container. This is all done for you automatically.
Check out this simple sample on how you should setup your app.
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/HelloWorld
As a constructor of ContactViewModel is not invoked before event is published, it is necessary just send a parameter(ObservableCollection<Room> room) into constructor of ContactViewModel. Moreover, you should instantiate your ContactViewModel from BookingViewModel, consequently you should mode instantiating of ContactViewModel from App.xaml.cs to ContactViewModel.
Let me show the full example.
App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
_container = new UnityContainer();
_container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
_container.RegisterType<IViewMainWindowViewModel, MainWindow>();
_container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
_container.RegisterType<IViewBookingViewModel, BookingView>();
_container.RegisterType<IViewBookingViewModel, BookingViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<IViewContactViewModel, ContactDetailsView>(new ContainerControlledLifetimeManager());
_container.RegisterType<IGetRoomsService, GetRoomsService>();
_container.RegisterType<IPostReservationService, PostReservationService>();
_container.RegisterType<IGetReservationsListService, GetReservationsListService>();
_container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));
_container.Resolve<MainWindow>().Show();
}
BookingViewModel:
public void ContinueCommand_DoWork(object obj)
{
ObservableCollection<Room> RoomsSelected = new ObservableCollection<Room>();
ObservableCollection<Room> RoomsListNew = new ObservableCollection<Room>();
RoomsSelected = _getRoomsService.FilterSelectedRooms(RoomsList);
_unityContainer.RegisterType<IViewContactViewModel, ContactViewModel>(new InjectionConstructor(RoomsSelected));
_dialogServiceContactView.ShowDialog(_unityContainer);
}
Constructor of ContactViewModel:
public ContactViewModel(ObservableCollection<Room> room)
{
// Initialize dropdown data for titlelist
DropDownTitle = GenerateDropDownDataForTitle();
DropDownCountry = GenerateDropDownDataForCountry();
ContactModel = new ContactDetails(1, "", "", "", "", 1);
// Initialize commands
BookCommand = new RelayCommand(BookCommand_DoWork, () => true);
BackCommand = new RelayCommand(BackCommand_DoWork, () => true);
if (room != null)
{
SelectedRooms = room;
}
}

Binding doesn't update when property is set inside a command

I am having a surprising difficulty trying to make a simple thing work, that is, setting a property in a method called by a Command bound to a Button.
When I set the property in the ViewModel constructor, the correct value is properly displayed in View, but when I set this property with the command's method, the View doesn't update, although any breakpoint I create is reached (even inside RaisePropertyChanged in my ViewModelBase). I am using vanilla RelayCommand found easily in online tutorials (from Josh Smith if I am not mistaken).
My project can be downloaded here (Dropbox);
Some important code blocks are below:
ViewModel:
public class IdiomaViewModel : ViewModelBase
{
public String Idioma {
get { return _idioma; }
set {
_idioma = value;
RaisePropertyChanged(() => Idioma);
}
}
String _idioma;
public IdiomaViewModel() {
Idioma = "nenhum";
}
public void Portugues () {
Idioma = "portu";
}
private bool PodePortugues()
{
if (true) // <-- incluir teste aqui!!!
return true;
return false;
}
RelayCommand _comando_portugues;
public ICommand ComandoPortugues {
get {
if (_comando_portugues == null) {
_comando_portugues = new RelayCommand(param => Portugues(),
param => PodePortugues());
}
return _comando_portugues;
}
}
public void Ingles () {
Idioma = "ingle";
}
private bool PodeIngles()
{
if (true) // <-- incluir teste aqui!!!
return true;
return false;
}
RelayCommand _comando_ingles;
public ICommand ComandoIngles {
get {
if (_comando_ingles == null) {
_comando_ingles = new RelayCommand(param => Ingles(),
param => PodeIngles());
}
return _comando_ingles;
}
}
}
View with no extra code behind:
<Window x:Class="TemQueFuncionar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:app="clr-namespace:TemQueFuncionar"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<app:IdiomaViewModel/>
</Window.DataContext>
<StackPanel>
<Button Content="Ingles" Command="{Binding ComandoIngles, Mode=OneWay}"/>
<Button Content="Portugues" Command="{Binding ComandoPortugues, Mode=OneWay}"/>
<Label Content="{Binding Idioma}"/>
</StackPanel>
</Window>
Youdid fill the Interface implementation put you did not mention it to the base view model.
You are missing this : INotifyPropertyChanged linking Interface to the base class, this makes the the View refreshes the content.
You missed the statement ViewModelBase:INotifyPropertyChanged on ViewModelBase

WPF - Observable Collection new record from xml binding

I have a page with tabcontrol.
I am binding a list of locations a tabitem in the control.
The records are listed in a listview.
I am able to edit records by binding input controls to the listview.selecteditem.
My problem is when I want to add a new record. I want to minimise code behind.
ViewModel:
private ObservableCollection<LocationViewModel> _locations;
public ObservableCollection<LocationViewModel> Locations
{
get { return _locations; }
}
public LocationListViewModel()
{
_locations = new ObservableCollection<LocationViewModel>();
foreach (Service.Location l in service.GetLocationList().OrderBy(l => l.Building).ThenBy(l => l.Floor))
{
_locations.Add(new LocationViewModel
{
id = l.id,
Building = l.Building,
Floor = l.Floor,
RoomNo = l.RoomNo,
MapTitle = l.MapTitle,
MapExtension = l.MapExtension,
Map = l.Map,
DateCreated = l.DateCreated,
CreatedByID = l.CreatedByID,
CreatedByDesc = l.CreatedByDesc,
DateEdited = l.DateEdited,
EditedByID = l.EditedByID,
EditedByDesc = l.EditedByDesc
}
);
}
}
XML:
<TabItem x:Name="tabSettingsLocations" x:Uid="tabSettingsLocations"
Header="Locations"
DataContext="{StaticResource ResourceKey=LocationList}"> .....
Example of successful binding to listview for edits
<TextBox x:Name="txtSettingLocationBuildingEdit"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
Text="{Binding SelectedItem.Building, ElementName=lvwSettingsLocations,
Mode=TwoWay}" />
Example of unsuccessful binding for new record (uses different set of input controls)
<TextBox x:Name="txtSettingLocationBuildingAdd"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
Text="{Binding Building, ElementName=lvwSettingsLocations,
Mode=OneWayToSource}"/>
I also tried to bind the child tab item to the same data source
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=LocationList}">
<TextBox x:Name="txtSettingLocationBuildingAdd"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
Text="{Binding Building}"/>
To no avail.
I also tried creating a new child dataview but I want it all to be bound together so that the interface updates whatever I do add or edit.
Anyone help?
Okay so I nailed this in the end. Just wanted to share ... thanks to Silvermind for a good tip on best practice.
Command:
class Location_Add : ICommand
{
private ObservableCollection<LocationViewModel> _llvm;
public ObservableCollection<LocationViewModel> llvm
{
get { return _llvm; }
}
public Location_Add(ObservableCollection<LocationViewModel> passedllvm)
{
_llvm = passedllvm;
}
public bool CanExecute(object parameter)
{
LocationViewModel lvw = parameter as LocationViewModel;
return lvw != null;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
LocationViewModel lvm = parameter as LocationViewModel;
llvm.Add(lvm);
AddLocation(lvm);
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public void AddLocation(LocationViewModel lvm)
{
try
{
Service.SchoolMonitorServiceClient service = new Service.SchoolMonitorServiceClient();
Service.Location loc = new Service.Location();
loc.Building = lvm.Building.Trim();
loc.Floor = lvm.Floor.Trim();
loc.RoomNo = lvm.RoomNo.Trim();
loc.MapTitle = lvm.MapTitle;
loc.MapExtension = lvm.MapTitle.Substring(lvm.MapTitle.IndexOf("."));
loc.Map = lvm.Map;
loc.DateCreated = DateTime.Now;
loc.CreatedByID = (Int32)Application.Current.Resources["UserID"];
loc.DateEdited = lvm.DateEdited;
service.AddLocation(loc);
MessageBox.Show("Your new Location was entered successfully", "Success", MessageBoxButton.OK);
}
catch (Exception e)
{
.....
}
}
}
ViewModel:
class LocationListViewModel
{
Service.SchoolMonitorServiceClient service = new Service.SchoolMonitorServiceClient();
#region Members
private ObservableCollection<LocationViewModel> _locations;
private Location_Add _AddCommand;
#endregion
#region Properties
public ObservableCollection<LocationViewModel> Locations
{
get { return _locations; }
}
#endregion
public LocationListViewModel()
{
_locations = new ObservableCollection<LocationViewModel>();
foreach (Service.Location l
in service.GetLocationList()
.OrderBy(l => l.Building).ThenBy(l => l.Floor))
{
_locations.Add(new LocationViewModel
{
id = l.id,
Building = l.Building,
Floor = l.Floor,
RoomNo = l.RoomNo,
MapTitle = l.MapTitle,
MapExtension = l.MapExtension,
Map = l.Map,
DateCreated = l.DateCreated,
CreatedByID = l.CreatedByID,
CreatedByDesc = l.CreatedByDesc,
DateEdited = l.DateEdited,
EditedByID = l.EditedByID,
EditedByDesc = l.EditedByDesc
}
);
}
_AddCommand = new Location_Add(_locations);
}
public ICommand AddCommand
{
get
{
return _AddCommand;
}
}
}
XML:
xmlns:local="clr-namespace:SchoolMonitor_WPF.ViewModels"
<Page.Resources>
<local:LocationListViewModel x:Key="LocationList" />
<local:LocationViewModel x:Key="NewLocation" />
</Page.Resources>
<TextBox x:Name="txtSettingLocationBuildingAdd" x:Uid="txtSettingLocationBuildingAdd"
Margin="90,17,0,0" Style="{DynamicResource SettingsTextBoxStyle}"
DataContext="{StaticResource ResourceKey=NewLocation}"
Text="{Binding Path=Building}"/>
<Button x:Name="btnSettingsLocationSaveAdd" Content="Submit" Margin="0,80,10,0"
VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}"
HorizontalAlignment="Right" Width="75"
DataContext="{StaticResource ResourceKey=LocationList}"
CommandParameter="{StaticResource ResourceKey=NewLocation}"
Command="{Binding AddCommand}">
Hope this helps someone.

How to make the command connection in silverlight MVVM?

I trying to use MVVM in some silverlight modal that i wrote -
I wrote the view - and the viewmodel part - but i need to make the command between them and i don't know how to do it.
In the view i have single button that will launch the command.
How to do it ?
Thanks for the help.
In View Model
private RelayCommand _Command;
public RelayCommand Command
{
get
{
if (_Command == null)
{
_Command= new RelayCommand(() =>
{
});
}
return _Command;
}
private set { }
}
USE PARAMETERS
private RelayCommand<string> _Command;
public RelayCommand<string> Command
{
get
{
if (_Command == null)
{
_Command= new RelayCommand<string>((X) =>
{
});
}
return _Command;
}
private set { }
}
In View
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:gs_cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4"
<Button Grid.Row="1" Grid.Column="1" Margin="4" HorizontalAlignment="Right" Name="btnSelect" Content="..." Width="25" Height="25" TabIndex="2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<gs_cmd:EventToCommand Command="{Binding Path=Command,Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Another version with Parameters, to add to Masoomian's anser:
private RelayCommand<MyViewModel> _Command;
public RelayCommand<MyViewModel> Command
{
get
{
if (_Command == null)
{
_Command= new RelayCommand<MyViewModel>((vm) =>
{
vm.IsBusy = true; // Set a Parameter
vm.DoSomething(); // Do some work
// Call other methods on the View Model as needed
// ...
});
}
return _Command;
}
private set { }
}

In WPF, can you use a Command.CanExecute to set the ToolTip?

This is not real code, I know. But it is what I would like to do.
MyBinding.CanExecute += (s, e) =>
{
e.CanExecute = Something.Allow;
if (!e.CanExecute)
e.ToolTip = Something.Reason;
}
Is there a simple way to do it?
Thank you.
From your question, I assume you are doing this from a ViewModel. If so, the simplest thing to do is to have an observable "CanExecute" property for your command, and another string "Reason" property for your tooltip.
Then, you listen for the PropertyChanged event within the ViewModel. When the CanExecute property changes, you simply update the reason.
Here is some sample code, which simply sets the CanExecute property to false when the command is executed:
public MyViewModel()
: base()
{
this.PropertyChanged += (s, e) =>
{
if (e.PropertyName == "SomeCommandCanExecute")
{
if (mSomeCommandCanExecute)
this.Reason = "Some Command Can Execute";
else
this.Reason = "Some Command Cannot Execute Because....";
}
};
}
private RelayCommand mSomeCommand = null;
private Boolean mSomeCommandCanExecute = true;
public RelayCommand SomeCommand
{
get
{
if (mSomeCommand == null)
{
mSomeCommand = new RelayCommand(
cmd => this.ExecuteSomeCommand(),
cmd => this.SomeCommandCanExecute);
}
return mSomeCommand;
}
}
public Boolean SomeCommandCanExecute
{
get { return mSomeCommandCanExecute; }
set { SetProperty("SomeCommandCanExecute", ref mSomeCommandCanExecute, value); }
}
private void ExecuteSomeCommand()
{
this.SomeCommandCanExecute = false;
}
private string mReason = "Some Command Can Execute";
public string Reason
{
get { return mReason; }
set { SetProperty("Reason", ref mReason, value); }
}
And then in your View:
<StackPanel>
<Button Command="{Binding SomeCommand}"
ToolTip="{Binding Reason}"
Content="Some Command"/>
<TextBlock Text="{Binding Reason}"
ToolTip="{Binding Reason}" />
</StackPanel>
Note that you won't see the ToolTip on the disabled button when CanExecute is set to false, which is why I added the TextBlock to show it. You will see the ToolTip on the TextBlock.
This is the best way I believe to accomplish this.
This is the Command definition:
class CustomCommand : RoutedUICommand, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string m_Reason;
public string Reason
{
get { return m_Reason; }
set
{
if (m_Reason == value)
return;
m_Reason = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Reason"));
}
}
}
public class MyCommands
{
public static CustomCommand DoThis = new CustomCommand();
public static CommandBinding DoThisBinding = new CommandBinding
{ Command = DoThis };
public static void SetupCommands()
{
DoThisBinding.CanExecute += (s, e) =>
{
var _Something = DoSomeTest(e.Parameter);
e.CanExecute = _Something.Allow;
if (!e.CanExecute)
(e.Command as CustomCommand).Reason = _Something.Reason;
}
}
}
And this is the XAML implementation:
xmlns:commands="MyNamespace.WhereAreCommands"
<Button Command="{x:Static commands:MyCommands.DoThis}"
ToolTip="{Binding Path=Reason,
Source={x:Static commands:MyCommands.DoThis}}">
Click</Button>

Resources