How to make the command connection in silverlight MVVM? - silverlight

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 { }
}

Related

How to fire event from ComboBox inside StackPanel in WPF and MVVM

I have ComboBox inside StackPanel. I am using MVVM and try to bind 'GotFocus' event Command to Command in ViewModel but when I Click on 'ComboBox', it don't work (It don't call Command in ViewModel) but if I move that 'ComboBox' out of 'StackPanel' it's working properly.
How can I fire event from 'CombBox' inside 'StackPanel' in MVVM?
<StackPanel x:Name="StackPanel" Grid.Column="2" Grid.Row="6">
<ComboBox x:Name="ComboBox" ItemsSource="{Binding Values}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<cmd:EventToCommand Command="{Binding Path=GotFocusCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</StackPanel>
ViewModel's code is:
public ViewModelCommand GotFocusCommand { get; set; }
////Change your tag from EventToCommand to InvokeCommandAction
<StackPanel x:Name="StackPanel" Grid.Column="2" Grid.Row="6">
<ComboBox x:Name="ComboBox" ItemsSource="{Binding Values}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<cmd:InvokeCommandAction="{Binding Path=GotFocusCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
////Then, I use my commands this way in my view model:
private ICommand _GotFocusCommand;
public ICommand GotFocusCommand
{
get
{
if (_GotFocusCommand == null)
{
_GotFocusCommand =
new RelayCommand(
param => GotFocusCommand_Executed(),
GotFocusCommand_CanExecute
);
}
return _GotFocusCommand;
}
}
////RelayCommandClass.cs:
public class RelayCommand : ICommand
{
private Action _handler;
public RelayCommand(Action handler)
{
_handler = handler;
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if (value != _isEnabled)
{
_isEnabled = value;
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler();
}
}
////Finally, you can create an event in your view model:
private void GotFocusCommand_Executed()
{
//DoSomething here
}
private bool GotFocusCommand_CanExecute()
{
return true;
}

WPF Close Window Closes Source Window. Why?

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.

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>

Silverlight MVVM Light Listbox item Click Event

I'm using the MVVM Light Toolkit with Silverlight.
On my UserControl I have a ListBox that displays a list of files. Each file has a delete image next to the file name. In the DataTemplate for the listbox I have an image (or can use a button) and a TextBlock.
So I want to capture using the event when the user will clicks on the image(or button with image) to remove the file from the list of files.
But I cannot seem to capture the event. Maybe this is due to having the SelectedItem Event on the listbox?
public class MainViewModel : ViewModelBase
{
#region Properties
public const string SelectedListBoxFilePropertyName = "SelectedUploadFile";
private UploadFile _selectedUploadFile = null;
public UploadFile SelectedUploadFile
{
get
{
return _selectedUploadFile;
}
set
{
if (_selectedUploadFile == value)
return;
_selectedUploadFile = value;
RaisePropertyChanged(SelectedListBoxFilePropertyName);
}
}
public const string UploadFilesPropertyName = "UploadFiles";
private ObservableCollection<UploadFile> _uploadFiles = new ObservableCollection<UploadFile>();
public ObservableCollection<UploadFile> UploadFiles
{
get
{
return _uploadFiles;
}
set
{
if (_uploadFiles == value)
return;
_uploadFiles = value;
RaisePropertyChanged(UploadFilesPropertyName);
}
}
#endregion
public static ICommand BrowseCommand { get; private set; }
public static ICommand DragDropFileCommand { get; private set; }
public static ICommand RemoveCommand { get; private set; }
#region Constructor
public MainViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
UploadFiles = new UploadFileContainer().UploadFiles;
}
else
{
// Code runs "for real"
}
WireUpCommands();
}
#endregion
#region Event Handlers
private void OnBrowseFileCommand()
{
var dialog = new OpenFileDialog();
dialog.ShowDialog();
if (dialog.Files != null)
AddFiles(dialog.Files);
}
private void OnDropFileCommand(DragEventArgs e)
{
var files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
AddFiles(files);
}
private void OnRemoveFileCommand()
{
UploadFiles.Remove(_selectedUploadFile);
}
#endregion
#region Private Methods
private void WireUpCommands()
{
BrowseCommand = new RelayCommand(OnBrowseFileCommand);
DragDropFileCommand = new RelayCommand<DragEventArgs>(e => OnDropFileCommand(e));
RemoveCommand = new RelayCommand(OnRemoveFileCommand);
UploadCommand = new RelayCommand(OnClickUploadCommand);
}
#endregion
}
<ListBox Grid.Row="1" Height="214" HorizontalAlignment="Left" AllowDrop="True" Margin="6,26,0,0" Name="UploadFilesListBox" VerticalAlignment="Top" Width="415" ItemsSource="{Binding Path=UploadFiles}" SelectedItem="{Binding Path=SelectedListBoxFile, Mode=TwoWay}" ScrollViewer.VerticalScrollBarVisibility="Auto">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<cmd:EventToCommand Command="{Binding DragDropFileCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.Background>
<ImageBrush ImageSource="/FileUploadApplication;component/Resources/dragdrophere.png" Stretch="None" />
</ListBox.Background>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveCommand}">
<Image Source="/FileUploadApplication;component/Resources/delete.png"/>
</Button>
<Image Source="/FileUploadApplication;component/Resources/delete.png">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding RemoveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image> <TextBlock Text=" " />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Since your ItemsSource is UploadFiles it's probably sending the event to UploadFile and not the view model the user control is bound to.
Your button is the element of the ItemTemplate. you're binding the listbox ItemsSource to the ObservableCollection. Every Itemtemplate DataContext is no MainViewModel, but UploadFile, which has no RemoveCommand.
I was solving this by adding to every item the parent object using constructor. RemoveCommand was inside the item's ViewModel and insede the remove function i was calling the parent's method to delete the item.
Not sure if that's the best solution but it worked for me.

How to set EventArg for ICommand?

in VM, set ICommand like:
private RelayCommand<EventArgs> _myCommand = null;
public RelayCommand<EventArgs> MyCommand
{
get
{
if (_myCommand == null)
{
_myCommand = new RelayCommand<EventArgs>((e) =>
{
//....
}
);
}
return _myCommand;
}
}
In xaml, binding to this command like
<Button Content="Test Command" Margin="2,0,2,0" Command="{Binding Path=MyCommand}" CommandParameter="{Binding ElementName=InputTextBox, Path=Text}" />
then run the app. it say can't convert string to EventArgs.
How to set EventArgs for ICommand binding?
I'm not familiar with RelayCommand<T> I've only come across RelayCommand.
However it would seem that RelayCommand<EventArgs> can't possibly be useful. I suspect that you would at least need RelayCommand<string>.

Resources