Command not triggering on button click - c#

In my SL4 app, I'm trying to trigger a Command from a button. The Command code is standard stuff that I have used without issue elsewhere, but I cannot get the Command to be called when I click the button.
This was driving me mental, so I eventually created a test page that had nothing on it but a button. The data context of the page is set to my ViewModel, and the ViewModel has an ICommand property on it. The DataContext is working as I can bind a textbox to a string property in the ViewModel. It's so basic, I can include all the relevant stuff here:
From the XAML:
xmlns:models="clr-namespace:x3.ViewModels"
...
<UserControl.DataContext>
<models:TestViewModel/>
</UserControl.DataContext>
<Button x:Name="TestButton" Command="{Binding TestCommand}" Content="AAAAGHH" />
From the ViewModel:
public class TestViewModel:INotifyPropertyChanged
{
ICommand _testCommand;
public ICommand TestCommand
{
get
{
_testCommand = new DelegateCommand(
commandParameter =>
{
var testButton = commandParameter as Button;
},
(commandParameter) => {return true;});
return _testCommand;
}
}
}
The DelegateCommand is part of Telerik.Windows.Controls. If I put a breakpoint at
_testCommand = new DelegateCommand
it gets hit when the page loads, but after that, I can click the button until my mouse wears out, and the command is never called.
For the sake of my mental health, I'd appreciate any help on offer.
Thanks
Mick

The get accessor for TestCommand is only called once - when the binding engine binds the Command of the Button to the TestCommand property. Putting your breakpoint on the first line of the get, it should be expected that it only gets hit once.
What you need to do is put your breakpoint on the code that executes when your command gets run. In your original example, this means break inside the delegate - i.e. on return true.
Edit: you can force the debugger to break in code as well using System.Diagnostics.Debugger.Break():
ICommand _testCommand;
public ICommand TestCommand
{
get
{
_testCommand = new DelegateCommand(
commandParameter =>
{
var testButton = commandParameter as Button;
},
(commandParameter) =>
{
System.Diagnostics.Debugger.Break(); // Force debugger to break
return true;
}
);
return _testCommand;
}
}

In order to isolate the problem, try using your own simple ICommand implementation, instead of Telerik's DelegateCommand.
If the problem still happens, you'll know it's somewhere around your XAML / data binding, and then I'd suggest you post more complete parts of your code so that someone here may help you.
If the problem disappears, you'll know it's something in Telerik DelegateCommand that decides not to call your lambda.
If you can post a more complete reproduction of the problem, it may also help. Because you posted only a minimal part of your code, and maybe the cause of the problem is missing from here...

Related

mvvmlight wpf how to raise frame source property wpf c#

(See Solution below)
Struggling learning MVVMLight and WPF. Happy to get page navigation with a frame with 3 buttons and also get the CanExecute RelayCommand functionality working for the buttons.
However, when I change pages with the Back and Forward button from the frame, the frame navigation buttons seem to not raise the propertychanged for the Frame Source Property FrameURI in the viewmodel. In the RelayCommand execute I'm looking at the FrameURI property to determine button command can execute. I'm thinking that I need to raisepropertychange of FrameURI property in the viewmodel after a Frame FWD or BACK button press. It looks like I can do that in a Navigated or LoadCompleted EVENT from the NavigationService Class. Is this the best way to go about this? Maybe there's an easier way?
PROPER BEHAVIOR HERE:
NOT WORKING CORRECTLY HERE when the frame BACK button is pressed. The page changes (good), but the button Middle canexecute didn't update correctly. See property and relay commands further below.
MVVMINPCPROPERTY snippet
public const string FrameUriPropertyName = "FrameUri";
private Uri _frameUri;
/// <summary>
/// Sets and gets the FrameUri property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public Uri FrameUri
{
get
{
return _frameUri;
}
set
{
Set(FrameUriPropertyName, ref _frameUri, value);
}
}
XAML for Frame
<Frame x:Name="MainFrameDS" Source="{Binding FrameUri}" HorizontalAlignment="Left" Height="211" Margin="109,88,0,0" VerticalAlignment="Top" Width="258"/>
RelayCommand example
private RelayCommand _changeToLastPage;
/// <summary>
/// Gets the ChangeToLastPage.
/// </summary>
public RelayCommand ChangeToLastPage
{
get
{
return _changeToLastPage
?? (_changeToLastPage = new RelayCommand(
() =>
{
FrameUri = ViewModelLocator.LastPageUri;
},
() => FrameUri != ViewModelLocator.LastPageUri));
}
}
I have the source code on github and using Win7 and VS2017 Community. I also have other issues w/ the program and opened issues on github. Any help is appreciated.
My goal is to have a few basic MVVMLight program 'templates' that I can pull from and share them w/ other learners like me... Thanks.
Solution
Add Mode=twoway to the binding per #steve-teece.
Did some DEBUG.writeline (Break point suggestion per #Michael-Randall) to the output window and found out that the URI for the page was different depending on if it referred back to the viewmodellocater.IntroPageUri or called by the frame FWD or BACK button.
The two URI results were:
/IntroPage.xaml OR IntroPage.xaml
My assumption was that the 'non-fore slash' version didn't equal the version with a '/'.
I'm not sure of the best way to solve this so I converted the URI's to strings and compared them w/ the string Contains method in a boolean method.
I replaced the follow line:
FrameUri != ViewModelLocator.IntroPageUri
It was replaced with a method call:
CheckUri(FrameUri, ViewModelLocator.IntroPageUri)
And, the CheckUri method:
private Boolean CheckUri(Uri _frameUriToCheck, Uri _vmUri)
{
string StringUriToCheck = _frameUriToCheck.ToString();
string StringUriVM = _vmUri.ToString();
System.Diagnostics.Debug.WriteLine(StringUriToCheck, "StringUriToCheck");
System.Diagnostics.Debug.WriteLine(StringUriVM, "StringUriVM");
if (StringUriVM.Contains(StringUriToCheck))
{ return false; }
else
{ return true; }
}
That worked! If someone has a better way to solve it, I'm all ears.
Thanks all for the feedback!
try changing the XAML to
<Frame x:Name="MainFrameDS" Source="{Binding FrameUri, Mode=TwoWay}" HorizontalAlignment="Left" Height="211" Margin="109,88,0,0" VerticalAlignment="Top" Width="258"/>
Firstly, make sure you are referencing
GalaSoft.MvvmLight.CommandWpf;
and not
using GalaSoft.MvvmLight.Command;
If your CanExcute is not getting evaluated you might need to manually raise the change via RaiseCanExecuteChanged
public Uri FrameUri
{
get
{
return _frameUri;
}
set
{
if(Set(FrameUriPropertyName, ref _frameUri, value))
{
// assuming this is the command you are having trouble with
// this will force the command to reevaluate CanExecute
ChangeToLastPage.RaiseCanExecuteChanged();
}
}
}
Update
Omg i didnt even notice this
public const string FrameUriPropertyName = "FrameUri";
you need to at least be setting this properly
Set(() => FrameUri, ref _frameUri, value);

WPF: How to have KeyBinding on Window that doesn't get swallowed by textbox

I want to rebind the "Up" key for my window to perform a command on my ViewModel.
My window contains 2 controls: ListView, TextBox.
If I do
<Window.InputBindings>
<KeyBinding Key="F5" Command={Binding SomeCommand} />
</Window.InputBindings>
Everything works correctly. However, if I set it to "Up", or certain other keys, the command does not get executed if the TextBox has focus. This tells me that the TextBox is handling these keys and swallowing the event that triggers the command. Is there any way to prevent that and to allow the window to get them similar to the Preview events (PreviewKeyUp) ?
If you want an application level shortcut, you can use InputManager
InputManager.Current.PreProcessInput += (sender,args)
{
var keyboardEvent = args?.StagingItem?.Input as KeyboardEventArgs;
if(keyboardEvent != null)
{
var keyArgs = keyboardEvent.RoutedEvent as KeyEventArgs;
if(keyArgs != null)
{
if(keyArgs.Key == Key.F5)
{
// Do something
}
}
}
};
I would recommend taking the logic above and incorporating it into a behavior that you can bind/map Keys to Commands.
Is there any way to prevent that and to allow the window to get them similar to the Preview events (PreviewKeyUp) ?
You could hook up an event handler to the KeyDownEvent using the overload of the AddHandler method that accepts a "handledEventsToo" boolean parameter and invoke your command from this event handler:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
DataContext = vm;
AddHandler(KeyDownEvent, new KeyEventHandler((ss, ee) =>
{
vm.SomeCommand.Execute(null);
}), true);
}
}
There is no way to do this in pure XAML though but it doesn't really matter as far as the MVVM pattern is concerned as you simply move the invocation of the command from the XAML markup of the view to the code-behind class of the very same view. This doesn't break the MVVM pattern.

Prism SetProptery not firing Notification?

So having a play with PRISM and I have a grid who's Visibility property is bound to a property in a view model like so.
Xaml
Grid Grid.RowSpan="2" Grid.ColumnSpan="3" Background="#7F808080" Visibility="{Binding LoadingVisibility}">
Code Behind
private Visibility loadingVisibility = Visibility.Visible;
public Visibility LoadingVisibility
{
get
{
return loadingVisibility;
}
set
{
SetProperty(ref loadingVisibility, value);
}
}
Now if I do this LoadingVisibility = Visibility.Collapsed;, the grid does not disappear and is still visible.
If I then set a breakpoint at SetProperty(ref loadingVisibility, value); I can see the original value of loadingVisibility, which is set as Visibile, and I can see that value is set to Collapsed.
If I then step on I can see loadingVisiblity has now changed to collapsed as it should. At this point I expect the Grid to be notified which in turn executes 'Get' to retrieve the value. This does not occur.
The binding is working because the Get is called when loading up and if I change private Visibility loadingVisibility = Visibility.Visible; to Collapsed and run the code the grid starts invisible.
So my question is, after SetProperty is executed, why is the Get not?
EDIT:
Just so you can see where I set the property.
public ShellViewModel(IEventAggregator IEventAggregator)
{
IEventAggregator.GetEvent<PubSubEvent<HardwareLoaded>>().Subscribe(x =>
{
if (!x.HardwareOK)
{
MessageBox.Show("There was an issue loading hardware. See Log");
}
LoadingVisibility = Visibility.Collapsed;
});
}
EDIT 2:
Just found something interesting, if I comment out LoadingVisibility = Visibility.Collapsed; in the Subscribe and then add a button to the xaml and have the click event like so then everything works fine.
private void Button_Click(object sender, RoutedEventArgs e)
{
mvm.LoadingVisibility = Visibility.Collapsed;
}
So now I guess the question is why, when both methods call the set property, does only one fully work and cause the Get to work?
Quite often when using the EventAggregator, you're working on the UI thread. But seeing the HardwareLoaded type it came to me that you might be doing some checking on another thread. And as we know, bindings have to be updated from the UI thread. Normally you would use Dispather.BeginInvoke, but Prism's EventAggregator has an overload in the Subscribe method to tell the handler to offload to the UI thread.
IEventAggregator.GetEvent<PubSubEvent<HardwareLoaded>>().Subscribe(x =>
{
if (!x.HardwareOK)
{
MessageBox.Show("There was an issue loading hardware. See Log");
}
LoadingVisibility = Visibility.Collapsed;
}, ThreadOption.UIThread);

Calling UIElement Command from code behind

I am trying to execute a bound command from my code behind utilizing the UiElement. button.Command.Execute(button.CommandParameter)
However, at this point the Command property of the button is null. simultaneously when I check the command in my View Model the property is set. The only diagnosis I can come up with is that until the window is actually visible the command is not bound to the command property of the button. I feel like may I'm missing a step somewhere or my implementation is not sound. below is some snipits of the code, please let me know if you need more.
Window constructor:
public PlottingViewModel ViewModel { get; set; }
public PlottingGUI()
{
InitializeComponent();
DataContext = (ViewModel = new PlottingViewModel());
_setDefaultSelections();
}
IList<RadioButton> buttons;
Setting default selections:
private void _setDefaultSelections()
{
buttons = new List<RadioButton>();
_getRadioButtons(this);
foreach (var setting in ViewModel.Settings.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var settingValue = setting.GetValue(ViewModel.Settings);
var button = buttons.FirstOrDefault(btn => btn.Content.Equals(settingValue)
|| ((string)btn.CommandParameter).Equals(settingValue));
if (button == null)
continue;
button.IsChecked = true;
// NullReference here
// button.Command.Execute(button.CommandParameter);
}
}
one of the RadioButtons XAML:
<RadioButton Content="None"
Grid.Row="0"
Command="{Binding StampedCommand}"
CommandParameter="None"
Foreground="WhiteSmoke"/>
I feel, the only way i may be able to successfully complete this task is to execute the command directly from my viewmodel. (Which i don't want to do)
Thanks for reading..
To sum up comments at the point when you're calling _setDefaultSelections() bindings have not been updated yet, hence Command is still null, so you have to wait until everything is loaded. You can call _setDefaultSelections during Loaded event
Occurs when the element is laid out, rendered, and ready for interaction.

How to determine which user control is calling command

Relative new to MVVM - I realize this is simple question but can't seem to find the answer.
I have 4 grouped radio buttons that when one is selected will show its associated options. I assume these 4 radio buttons should be linked to the same viewmodel command which in this case is called UpdateIndex.
How do I determine which of the radio buttons is calling the UpdateIndex so that I can take appropriate action and show the appropriate options?
Note that my UpdateIndex and UpdateIndexExecute does get called correctly from my radio button command binding, I just don't know how to determine which radio button is calling it. I assume it has to do with CommandParameter - but not sure how to access it from the viewmodel.
An example of my radio button:
<RadioButton
Content="Option 1"
GroupName="GroupHeader"
Command="{Binding UpdateIndex}" />
Code snippet of my command being called from the radio button when clicked...
void UpdateIndexExecute()
{
}
bool CanUpdateIndex()
{
return true;
}
public ICommand UpdateIndex
{
get
{
return new RelayCommand(UpdateTabIndexExecute, CanUpdateTabIndex);
}
}
In a true MVVM implementation, you won't know which RadioButton executed the command, because the ViewModel should not have any information about the View. User Controls fall squarely in the category of "things that only exist within the View, not the ViewModel." You should instead pass something meaningful to the ViewModel.
You are correct there are ways to pass information into an ICommand using the "CommandParameter" of a Command Binding. For that, you would use the "generic" form of the RelayCommand (RelayCommand) class, where "T" represents the type of object you are passing as a parameter.
If you're just trying to pass an index as a parameter, I imagine it'll look something like this:
<!-- We are passing index "1" as a parameter -->
<RadioButton Content="Option 1" GroupName="GroupHeader"
Command="{Binding UpdateIndex, CommandParameter=1}"/>
Then in your ViewModel:
void UpdateIndexExecute(int index)
{
}
bool CanUpdateIndex(int index)
{
return true;
}
public ICommand UpdateIndex
{
get
{
return new RelayCommand<int>(UpdateTabIndexExecute, CanUpdateTabIndex);
}
}
Instead of binding the command, you can bind the content, use the INotifyPropertyChanged interface to handle changes made by the control.

Categories

Resources