How to enable KeyBinding for DelegateCommand in Prism 4? - wpf

I would like to enable a KeyBinding for a DelegateCommand in a Prism4 desktop application. For example, in my XAML file I have this:
<Grid.InputBindings>
<KeyBinding Gesture="CTRL+A" Command="{Binding Command3}"/>
</Grid.InputBindings>
<StackPanel>
<Button Grid.Row="0" Grid.Column="1" Content="HitMe" prism:Click.Command="{Binding Command3}" />
</StackPanel>
and in my ViewModel I have this:
public DelegateCommand<string> Command3 { get; private set; }
private void ExecuteCommand3(string commandParameter)
{
Debug.WriteLine("ExecuteCommand3");
}
private bool CanExecuteCommand3(string commandParameter)
{
return true;
}
When I press the HitMe button the debug line outputs but pressing CTRL+A has no effect.
I have considered using the CommandReference class from TestMvvmExample2341 but that seems to duplicate the functionality of Prism 4 mechanisms.
Is there a an easy way to have CTRL+A invoke the Command3 in Prism4?

That's it, maybe your problem is related to the Focus in your view, try this:
At runtime set the focus over the Button and then apply the keystroke. Also take a look at these posts:
WPF MVVM KeyBinding not being recognized right away and not always working
http://joyfulwpf.blogspot.com/2009/05/mvvm-commandreference-and-keybinding.html

Related

WPF - clicking on a button inside an user control doesn't call the command, and neither does clicking on the control itself

I have a serious issue with binding any command to my user control. Everything compiles, but the command is never called. I have tried two approaches - first, I tried to bind the command to a button inside my control, and when I was unable to do it, I tried to bind the command to an inputcommand of the control itself to see if it would work. It didn't. The control itself is within an ItemsControl, in case that matters.
Here's a simplified version of what I did. In the xaml.cs file of the control:
public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register(
"CloseCommand",
typeof(ICommand),
typeof(Thumbnail),
new UIPropertyMetadata(null)
);
public ICommand CloseCommand
{
get { return (ICommand)GetValue(CloseCommandProperty); }
set { SetValue(CloseCommandProperty, value); }
}
In the UserControl's xaml file, the offending button (the UserControl has Name="Control", and Hash is another dependency property):
<Button Command="{Binding ElementName=Control, Path=CloseCommand}" CommandParameter="{Binding ElementName=Control, Path=Hash}">
<TextBlock Text="X"/></Button>
Now, a simplified (irrelevant properties not included) datatemplate part of the xaml file of the view (which has a datacontext, if that matters), where I use this control:
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:Thumbnail Hash="{Binding Hash}"
CloseCommand="{Binding ElementName=Control, Path=DataContext.RemoveImageCommand}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
Just for the sake of completeness, I'll include the command from the viewmodel.
private bool CanRemoveImageCommandExecute(string hash)
{
return true;
}
private void RemoveImageCommandExecute(string hash)
{
MessageBox.Show("ABC","ABC");
}
public ICommand RemoveImageCommand
{
get { return new RelayCommand<string>(RemoveImageCommandExecute, CanRemoveImageCommandExecute);}
}
The RelayCommand class comes from MicroMVVM, and it just creates a command from two functions (and works everywhere else).
Can you tell me why clicking the button does nothing and how to fix it?
It seems that, even though I wasted a few hours on that, I was too quick to ask the question. Literally a few minutes after posting it, I realized that my binding in ItemTemplate is wrong.
The problem was that I used ElementName instead of RelativeSource:
CloseCommand="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:AddImage}
Where local:AddImage is the name of the view which has the DataContext set to the viewmodel..

Silverlight Switching views in a view using MVVM Light framework

I have searched and tried for days and finally must ask the question here.
I have a Silverlight 5 application, Using MVVM Light, where I want to be able to dynamically switch views in the main view.
For the sake of simplicity, lets say I have 2 buttons.
Button1 will switch to TestView1.
Button2 will switch to TestView2.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
The way I have done it is by binding a relaycommand to the button and then instanciating a new viewmodel of the corresponding view.
ie:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = ViewModelLocator.NinjectKernel.Get<TestViewModel1>();
}));
}
}
The CurrentViewmodel is then set to the new viewmodel.
In the MainView I have bound the CurrentView to a ContentControl:
<Border x:Name="displayedView" Grid.Row="2">
<ContentControl Content="{Binding CurrentView}" />
</Border>
This will actually work to some extend, since the CurrentView will change but instead of actually showing the content of the view it simply shows the Namespace of the ViewModel that is instanciated.
So far I have primarily used the knowledge taken from these sources:
http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Loading Views into ContentControl and changing their properties by clicking buttons
but they do not solve my problem, or I do not quite understand how to actually show the views.:-(
So does anyone have a good explanation on how to switch the views correct in Silverlight 5 using MVVM Light from GalaSoft.
Thanks
The part you are missing is the DataTemplates that tell WPF how to render your ViewModels
<Window.Resources>
<DataTemplate TargetType="{x:Type local:TestViewModel1}">
<local:TestView1 />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TestViewModel2}">
<local:TestView2 />
</DataTemplate>
</Window.Resources>
When you insert an object in the Visual Tree, such as placing a ViewModel object in ContentControl.Content, it will get drawn by default using a TextBlock bound to the .ToString() of the object, which is why you are only seeing the namespace.classname of the ViewModel in your ContentControl
By defining an implicit DataTemplate in your Resources somewhere (that's a DataTemplate with only a TargetType defined - no x:Key), you are telling WPF to draw the specified object using the specified DataTemplate anytime it tries to draw that object, instead of using the default TextBlock bound to the .ToString() of the object.
It should be noted that implicit DataTemplates are not supported in earlier versions of Silverlight, however they are supported in 5.0+. For earlier versions of Silverlight, I usually use a DataTemplateSelector instead.
Id first suggest that you do not display your views via a ContentControl but look into using the navigation Frame in the silverlight toolkit. Also, we dont want our ViewModel creating Views... that'd not be so good. We don't mind, however, if our ViewModel does business logic and DETERMINES which view to show. Get the toolkit here: http://silverlight.codeplex.com/
Now setup your XAML as so in your main page:
<Border x:Name="displayedView" Grid.Row="2">
<navigation:Frame x:Name="ContentFrame" />
</Border>
Since you are using MVVM Light, we will use messaging. Your View model will get the command to change views, determine which view to change, then send a message to the main page to instruct it to change views.
Setup a listener in your main page for a navigate request as so:
public MainPage()
{
InitializeComponent();
Messenger.Default.Register<Uri>(this, "NavigationRequest", (uri) => ContentFrame.Navigate(uri));
}
Next, setup your command in your view model.
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
Messenger.Default.Send<Uri>(new Uri("/Views/.../Page.xaml", UriKind.Relative), "NavigationRequest");
}));
}
}
These are the basics that work for me. You can expand on this and get real "architecty". For example, you can create a base class for you view models that sends the navigation requests, create a helper class that generates URIs (so they are not hard coded everywhere in your app, etc etc. Good luck!
So i actually solved this problem, in a way where there is no need to create datatemplates in the MainView, which i did not like. imo the MainView should know nothing about the views it is displaying, when we are talking about switching the views.
Prerequisite: You must use MVVM Light from GalaSoft for this solution
This is my test solution:
Two buttons are added to my MainView, Each button will open a new view. The clickevent are bound to Commands.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
In the MainView i have a Border that should contain the views than can switch.
Since all views inherit from UserControl i bind the content to the property CurrentView of the MainViewModel
<Border x:Name="displayedView" Grid.Row="2">
<UserControl Content="{Binding CurrentView}" />
</Border>
In the MainViewModel i have the property CurrentView.
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
/// <summary>
/// Sets and gets the "CurrentView property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (_currentView == value)
{
return;
}
RaisePropertyChanging(CurrentViewPropertyName);
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
When a button is clicked the corresponding Command is called in the MainViewModel:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = new TestView1();
}));
}
}
private RelayCommand _callTestView2Command;
public RelayCommand CallTestView2Command
{
get
{
return _callTestView2Command ??
(_callTestView2Command = new RelayCommand(() =>
{
CurrentView = new TestView2();
}));
}
}
As seen each command will set CurrentView to a new view, and the views will switch in the MainView, because CurrentView will raise a ProperTyChanged Event.
This will actually work to some extend, since the CurrentView will
change but instead of actually showing the content of the view it
simply shows the Namespace of the ViewModel that is instanciated.
Because you are changing the CurrentView property to a viewmodel instance and bind that as the Content. This is wrong as the Content should be a view and you should set the DataContext of that view to a viewmodel.
The simplest thing you can do here is to create a View instance inside the command and set the viewmodel as its DataContext and then you can set the view to the CurrentView property. Of course this would violate the MVVM pattern so you should move this responsibility to a separate component. Instead of writing your own navigating logic I suggest you to pick up an existing solution as this kind of task is not as straightforward as it seems.
I suggest to use the Prism library

How to handle the SelectionChanged event of ComboBox with MVVM in wpf?

How to raise / handle the SelectionChanged event of WPF's ComboBox using the MVVM pattern?
Explain in detail please I am new to WPF.
What I want, is to do some operations when the ComboBox item selection changed. How can I achieve it, in an MVVM way?
MVVM solution:
Bind the ItemsSource and SelectedItem properties of the ComboBox to properties in your ViewModel:
<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem}"/>
In MainViewModel.cs:
public ObservableCollection<string> MyItems { get; set; }
private string _mySelectedItem;
public string MySelectedItem
{
get { return _mySelectedItem; }
set
{
// Some logic here
_mySelectedItem = value;
}
}
Code-behind solution:
If you don't want to use MVVM, you can add use this:
<ComboBox SelectionChanged="ComboBox_SelectionChanged" />
And add this in MainWindow.xaml.cs:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Some logic here
}
I'm a big fan of this method.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<ComboBox Grid.Column="2" DisplayMemberPath="Data.name" ItemsSource="{Binding Model.Regions}" SelectedItem="{Binding Model.SelectedRegion}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding RegionChangedCmd}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Your ViewModel needs to implement INotifyPropertyChanged.
public class MyViewModel : INotifyPropertyChanged
{
private string _mySelectedItem;
public string MySelectedItem
{
get
{
return _mySelectedItem;
}
set
{
if (_mySelectedItem != value)
{
_mySelectedItem = value;
// Perform any pre-notification process here.
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("MySelectedItem"));
}
}
}
}
}
The previously posted XAML is correct:
<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem}"/>
Just an enhancement of this solution which exists above, In case you are using Prism Library (if not, then stop reading now, there is nothing for you)
I really like this solution and I think it is better than any other solution, I just want to make a small enhancement to that solution provided by the Prism Library.
that solution is using
<i:InvokeCommandAction Command="{Binding RegionChangedCmd}" />
notice the i: before the InvokeCommandAction. It means that the InvokeCommandAction class exists in the xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" namespace. This is good and fine, but notice that the Prism library has exactly the same class with the same name InvokeCommandAction. It just exists in another namespace, in the xmlns:prism="http://prismlibrary.com/" namespace.
So actually you can replace the following XAML
<i:InvokeCommandAction Command="{Binding RegionChangedCmd}" />
with this XAML
<prism:InvokeCommandAction Command="{Binding RegionChangedCmd}" />
OK, we can do this, what is the benefit?
To notice the benefit, write the following command in the ViewModel
public ICommand RegionChangedCmd { get; }
public ViewModelConstructor()
{
RegionChangedCmd = new DelegateCommand<SelectionChangedEventArgs>(RegionChangedCmdExecuted);
}
public void RegionChangedCmdExecuted(SelectionChangedEventArgs e)
{
// e parameter is null if you use <i:InvokeCommandAction>
// e parameter is NOT null if you use <prism:InvokeCommandAction>
}
e parameter is null if you use <i:InvokeCommandAction>
e parameter is NOT null if you use <prism:InvokeCommandAction>
As first let's make things clear - you can not change event rather you can subscribe to.
Since you've not provided any information regarding where from you want to handle selection changes I will assume most common scenario - handling in the underlying ViewModel. According to MVVM ViewModel should not know anything about View so you can not subscribe directly from ViewModel to the event of a View's control. But you can bind a property of ViewModel to either SelectedItem or SelectedIndex so it would trigger whilst selection changes.
<ComboBox
SelectedIndex="{Binding SelectedIndexPropertyName}"
... />
There are other solutions doing handling in code behind of a View by accessing a ViewModel via view.DataContext but I would suggest avoid such practice, this are work around cases.

How to build a generic/re-usable modal dialog for WPF following MVVM

I would like to build a generic/re-usable modal dialog that I can use in our WPF (MVVM) - WCF LOB application.
I have a Views and associated ViewModels that I would like to display using dialogs. Bindings between Views and ViewModels are done using Type-targeted DataTemplates.
Here are some requirements that I have been able to draft:
I prefer this to be based on a Window instead of using Adorners and controls that act like a modal dialog.
It should get its minimum size from the content.
It should center on the owner window.
The window must not show the Minimize and Maximize buttons.
It should get its title from the content.
What is the best way to do this?
I usually deal with this by injecting this interface into the appropriate ViewModels:
public interface IWindow
{
void Close();
IWindow CreateChild(object viewModel);
void Show();
bool? ShowDialog();
}
This allows the ViewModels to spaw child windows and show them modally on modeless.
A reusable implementation of IWindow is this:
public class WindowAdapter : IWindow
{
private readonly Window wpfWindow;
public WindowAdapter(Window wpfWindow)
{
if (wpfWindow == null)
{
throw new ArgumentNullException("window");
}
this.wpfWindow = wpfWindow;
}
#region IWindow Members
public virtual void Close()
{
this.wpfWindow.Close();
}
public virtual IWindow CreateChild(object viewModel)
{
var cw = new ContentWindow();
cw.Owner = this.wpfWindow;
cw.DataContext = viewModel;
WindowAdapter.ConfigureBehavior(cw);
return new WindowAdapter(cw);
}
public virtual void Show()
{
this.wpfWindow.Show();
}
public virtual bool? ShowDialog()
{
return this.wpfWindow.ShowDialog();
}
#endregion
protected Window WpfWindow
{
get { return this.wpfWindow; }
}
private static void ConfigureBehavior(ContentWindow cw)
{
cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true));
}
}
You can use this Window as a reusable host window. There's no code-behind:
<Window x:Class="Ploeh.Samples.ProductManagement.WpfClient.ContentWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:Ploeh.Samples.ProductManagement.WpfClient"
xmlns:pm="clr-namespace:Ploeh.Samples.ProductManagement.PresentationLogic.Wpf;assembly=Ploeh.Samples.ProductManagement.PresentationLogic.Wpf"
Title="{Binding Path=Title}"
Height="300"
Width="300"
MinHeight="300"
MinWidth="300" >
<Window.Resources>
<DataTemplate DataType="{x:Type pm:ProductEditorViewModel}">
<self:ProductEditorControl />
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" />
</Window>
You can read more about this (as well as download the full code sample) in my book.
I'm answering my own question to help others find all answers I struggled to find in one place. What above seems like a straight forward problem, actually presents multiple problems that I hope to answer sufficiently below.
Here goes.
Your WPF window that will serve as the generic dialog can look something like this:
<Window x:Class="Example.ModalDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ex="clr-namespace:Example"
Title="{Binding Path=mDialogWindowTitle}"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner"
WindowStyle="SingleBorderWindow"
SizeToContent="WidthAndHeight"
ex:WindowCustomizer.CanMaximize="False"
ex:WindowCustomizer.CanMinimize="False"
>
<DockPanel Margin="3">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Content="Cancel" IsCancel="True" Margin="3"/>
<Button Content="OK" IsDefault="True" Margin="3" Click="Button_Click" />
</StackPanel>
<ContentPresenter Name="WindowContent" Content="{Binding}"/>
</DockPanel>
</Window>
Following MVVM, the right way to show a dialog is through a mediator. To use a mediator, you typically require some service locator as well. For mediator specific details, look here.
The solution I settled on involved implementing an IDialogService interface that is resolved through a simple static ServiceLocator. This excellent codeproject article has the details on that. Take note of this message in the article forum. This solution also solves the problem of discovering the owner window via the ViewModel instance.
Using this interface, you can call IDialogService.ShowDialog(ownerViewModel, dialogViewModel). For now, I'm calling this from the owner ViewModel, meaning I have hard references between my ViewModels. If you use aggregated events, you will probably call this from a conductor.
Setting the minimum size on the View that will eventually be displayed in the dialog doesn't automatically set the minimum size of the dialog. Also, since the logical tree in the dialog contains the ViewModel, you can't just bind to the WindowContent element's properties. This question has an answer with my solution.
The answer I mention above also includes code that centers the window on the owner.
Finally, disabling the minimize and maximize buttons is something WPF can't natively do. The most elegant solution IMHO is using this.

Using a Button to navigate to another Page in a NavigationWindow

I'm trying to use the navigation command framework in WPF to navigate between Pages within a WPF application (desktop; not XBAP or Silverlight).
I believe I have everything configured correctly, yet its not working. I build and run without errors, I'm not getting any binding errors in the Output window, but my navigation button is disabled.
Here's the app.xaml for a sample app:
<Application x:Class="Navigation.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="First.xaml">
</Application>
Note the StartupUri points to First.xaml. First.xaml is a Page. WPF automatically hosts my page in a NavigationWindow. Here's First.xaml:
<Page x:Class="Navigation.First"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="First">
<Grid>
<Button
CommandParameter="/Second.xaml"
CommandTarget="{Binding RelativeSource=
{RelativeSource
FindAncestor,
AncestorType={x:Type NavigationWindow}}}"
Command="NavigationCommands.GoToPage"
Content="Go!"/>
</Grid>
</Page>
The button's CommandTarget is set to the NavigationWindow. The command is GoToPage, and the page is /Second.xaml. I've tried setting the CommandTarget to the containing Page, the CommandParameter to "Second.xaml" (First.xaml and Second.xaml are both in the root of the solution), and I've tried leaving the CommandTarget empty. I've also tried setting the Path to the Binding to various navigational-related public properties on the NavigationWindow. Nothing has worked so far.
What am I missing here? I really don't want to do my navigation in code.
Clarification.
If, instead of using a button, I use a Hyperlink:
<Grid>
<TextBlock>
<Hyperlink
NavigateUri="Second.xaml">Go!
</Hyperlink>
</TextBlock>
</Grid>
everything works as expected. However, my UI requirements means that using a Hyperlink is right out. I need a big fatty button for people to press. That's why I want to use the button to navigate. I just want to know how I can get the Button to provide the same ability that the Hyperlink does in this case.
According to the documentation, only DocumentViewer and FlowDocumentViewer implement this command specifically. You'll need to either find a command for navigation that NavigationWindow implements, or set up a CommandBinding for this command and handle it yourself.
In XAML:
<Button Command="{x:Static Views:Commands.NavigateHelp}" Content="Help"/>
In Views (We have a Commands.cs file that contains all of these):
public static RoutedCommand NavigateHelp = new RoutedCommand();
In the Page contstructor, you can connect the two:
CommandBindings.Add(new CommandBinding(Commands.NavigateHelp, NavigateHelpExecute));
NavigateHelpExecute can be in the code behind (which is what we do), hook into a ViewModel event handler, or whatever. The beauty of this is that you can disable other navigation like so:
CommandBindings.Add(new CommandBinding(NavigationCommands.Refresh, null));
Hope this helps.
You will want to use the NavigationService of your NavigationWindow as follows:
XAML:
<Button HorizontalAlignment="Right" Name="continueButton" Width="75" Margin="0,0,8,11" Height="23" VerticalAlignment="Bottom" Click="continueButton_Click">
Continue
</Button>
C#:
private void continueButton_Click(object sender, RoutedEventArgs e)
{
this.NavigationService.GoForward();
//or
this.NavigationService.Navigate("Second.xaml")
}
With either of this you can use use this, I only show the NavigationService here for clarity
public class NavigateButton : Button
{
public Uri NavigateUri { get; set; }
public NavigateButton()
{
Click += NavigateButton_Click;
}
void NavigateButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
var navigationService = NavigationService.GetNavigationService(this);
if (navigationService != null)
navigationService.Navigate(NavigateUri);
}
}
And then you can put the following in your xaml:
<local:NavigateButton Content="NavigateButton" NavigateUri="Page2.xaml"/>
Then you still don't need code behind your pages, and you don't need to add commands to your viewmodel.

Resources