Application Bar Button Command and Can Execute Method - xaml

I have an application bar button that I have hooked up to a command using Prism like so:
<i:Interaction.Behaviors>
<prismInteractivity:ApplicationBarButtonCommand ButtonText="save" CommandBinding="{Binding SaveCommand}" />
</i:Interaction.Behaviors>
The save command looks like this:
SaveCommand = new RelayCommand( Save, CanSave );
The CanSave method is only getting called when the page loads. The button then gets disabled because false is returned the first time (due to validation).
How can I get the CanSave method to fire again so it can be enabled?

I figured this out by looking at the source to the TailSpin Prism demo app.
When you want it to fire again, like when text in a text box has changed, just call:
SaveCommand.RaiseCanExecuteChanged();
So, in my case, when the Name property is set, I want it to run again.
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged( () => Name );
SaveCommand.RaiseCanExecuteChanged();
}
}

Related

Cannot enable a Ribbon button programmatically

I developed a VSTO 4 add-in for Excel. It works perfect, however, I have a button placed in the custom tab of its Ribbon control that is initially disabled.
After clicked other ribbon button in my custom tab, I need to enable the initially disabled button.
I tried with:
btnCancelar.Visible = true;
In the Click event of a button, but button is not shown. The strange thing is that when debugging, it still does not appear, but if a MessageBox is shown, the button get visible at last.
I don't understand this behaviour. How can I enable or disable a ribbon button dynamically by code?
I'm not sure what your language is used in your project, but I guess you can tranform it to your own language used. I'll show the example here in C#:
First you need to implement a so called Callback function in the RibbonXML definition:
<button id="buttonSomething" label="Content" size="large" getVisible="EnableControl"/>
then the next step is to implement the Callback function:
public bool EnableControl(IRibbonControl control)
{
return true; // visible ... false = invisible
}
VSTO will trigger the getVisible Callback and depending on the return value enable or disable the visible state (don't forget to remove any Visible property from the RibbonXML, otherwise the Callback is not triggered)
In case of the Ribbon Designer you need to make sure your Click signature is correct, the easies way to do that is by double clicking the button on the ribbon designer. This will create the Click method for you, for instance:
I created a Ribbon with the Ribbon designer and added two buttons. Double clicked the first button to get an empty method like below, and added the code.
private void button1_Click(object sender, RibbonControlEventArgs e)
{
// Toggle button visibility and make sure the button is enabled
// Visible (obviously) makes it visible, while Enabled is grayed if
// false. You don't need this it is Enabled by default, so just for
// demo purposes
button2.Visible = !button2.Visible;
button2.Enabled = button2.Visible;
// Force Ribbon Invalidate ...
this.RibbonUI.Invalidate();
// Long running proces
}
This worked perfectly for me, so if it doesn't work for you please provide more details of your coding.
I have created a workaround to this.
It was simple. Just started the long running process in different thread. That way, cancel button is shown when it should and then hidden after the process ends.
I used this code to launch the process in the Ribbon.cs code:
btnCancelar.Visible = true;
Action action = () => {
Formatter.GenerateNewSheet(Formatter.TargetType.ImpresionEtiquetas, frm.CustomerID, workbook, btnCancelar);
};
System.Threading.Tasks.Task.Factory.StartNew(action);
And inside the process method I have this code:
public static bool GenerateNewSheet(TargetType type, string customerID, Excel.Workbook workbook, Microsoft.Office.Tools.Ribbon.RibbonButton btnCancelar)
{
try
{
_cancelled = false;
InfoLog.ClearLog();
switch (type)
{
case TargetType.ImpresionEtiquetas:
return GenerateTagPrinting(customerID, workbook);
}
return false;
}
finally
{
btnCancelar.Visible = false;
}
}
The interesting thing here I have discovered is that Excel is thread safe, so it was not necessary to add a synchronization mechanism neither when adding rows in the new sheet nor when setting Visible property to false again.
Regards
Jaime

WP8.1 Silverlight - BackNavigation maintains the old NavigationEventArgs for OnNavigatedTo function

I am working on a Windows Phone 8.1 Silverlight app. In this app I have a launch string attached to the toast notification which helps in navigating to the MainPage with some parameters for e.g /MainPage.xaml?data=test
So when I click this notification, I am able to get this data value from NavigationEventArgs of OnNavigatedTo function of MainPage. Based on some logic associated with data I navigate to a new Test.xaml screen.
The problem is when I GoBack from this Test.xaml screen to MainPage.xaml, the old OnNavigatedTo NavigationEventArgs remains the same, i.e the Uri in NavigationEventArgs is being preserved.
Is there a way to delete the NavigationEventArgs once done and dealt with?
Please check the NavigationMode inside the OnNavigatedTo method on your main page for example if you will come back form test.xaml page e.NavigationMode==NavigationMode.Back will call and you can code there.
protected override void OnNavigatedTo ( NavigationEventArgs e )
{
if ( e.NavigationMode==NavigationMode.New )
{
//do somthing
}
if ( e.NavigationMode==NavigationMode.Back )
{
//do somthing
}
}
}

showandwait changing variable value

I'm having a problem with a variable I'm using to track the status of a user activity. In a GUI I have a button that, on clicking the button launches a second GUI. In that GUI, the user can either complete the activity started in the first GUI or not.
If the user cancels the second GUI, then the idea is to go back to the first GUI, leaving all variables and lists with their current values. If the second GUI completes the activity of the first GUI, then all variables and lists should be reset.
To track this, I have a variable (Boolean complete) initially set to FALSE. In the second GUI, when the "OK" button is clicked (rather than the "Cancel" button), the second GUI calls a method in the first GUI, changing the value of "complete" to TRUE.
To see what the heck is going on, I have System.out.println at several points allowing me to see the value of "complete" along the way. What I see is this:
Launching first GUI - complete = FALSE
Launching second GUI - complete = FALSE
Clicking "OK" in second GUI - complete = TRUE
Second GUI closes itself, returning to complete first GUI activity
First GUI finishes activity with complete = FALSE
I'm assuming it is because I am launching the second GUI with a showandwait, and when the method containing the showandwait begins, the value of "complete" = FALSE. The value changes in the WAIT part of show and wait, then the method continues and that is where I get the value still being FALSE, though it was changed to TRUE.
Here is a summary of the code in question (if you need exact code, it's longer, but I can post on request):
completeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
try {
System.out.println("b4 calc = " + complete); // complete = FALSE
// all the code to create the calcStage
calcStage.showAndWait(); // second GUI, which calls a method in THIS
// class that changes complete to TRUE. That method
// (in THIS file) also has a println that shows the change.
getComplete(); // tried adding this method to check the value of
// "complete" after the change made by the calcStage
// (which calls a method in this same file)
System.out.println("Complete? " + complete);
// this shows complete = FALSE,
// though in the calcStage it was changed to TRUE
if (salecomplete) {
// code that should reset all variables and lists if the activity was completed
}
}
}
}
The question here is why does the second GUI successfully change the value of "complete", but when I return to the first GUI it still sees complete as FALSE? And how can I get around this?
Try having the controller of the second GUI calling a method in the first GUI's controller to modify that complete variable
For example:
// Code to handle the OK button being pressed
#Override
public void handle(ActionEvent t) {
// Do validation and work
//reference to the first controller object
firstController.setComplete(true);
}

StackOverflowException in Silverlight MVVM after page navigation

I am attempting to use MVVM Light to create a simple page for adding/editing/deleting Contacts and for users to send faxes from the other parts of the software. The MainPage has a simple header with some corporate info and two links, one to the Contacts page and the other to the Fax History page; most of the MainPage page is a navigation frame that is the target of the links, where the pages are loaded. I am using VS 2010/.NET 4.0/SL 4.0.
The Contacts page has two datagrids, one for Search Contacts Results ('gridContacts') and the other for Selected Contacts (those contacts who will receive the fax--'gridSelected'). There's a Delete button for the user to delete the contact of the selected row in the Search grid. There's also a Send Fax button to send the fax to those contacts in the gridContacts grid.
Problem is, if I ever select a row in the Search grid, navigate to Fax History, then come back, I get a StackOverflowException before the navigation occurs. If I navigate to the other page first, then come back, and then select a row, I'll get the exception at that point. I found that the Send Fax button also has causes the same exception when I removed the Delete button. On both buttons, I am able to work around the problem just coding everything in code-behind; obviously this is not ideal, so I want to learn to get it to work with MVVM.
When I step through the code for the problem with the Delete button, I see that the exception occurs in the DelegateCommand that the Delete button is bound to. In the CanExecute method, the first time through, the object parameter is correctly a Contact (since the grid is bound to a List of Contacts). It fires CanExecuteChanged(this,new EventArgs()) as part of the code logic--but then comes BACK into the function, but with null as the parameter passed in. Here is where an infinite loop becomes evident: CanExecute fires in a loop and alternates between having a Contact object and null as the parameter passed in. The stack trace shows External Code between the calls to CanExecute, but that's the only method present. The infinite loop eventually causes the StackOverflowException.
Here's the code in question:
public bool CanExecute(object parameter) //parameter alternates between Contact object and null
{
bool temp = _funcCanExecute(parameter); //_funcCanExecute is set to CanDelete in the DelegateCommand constructor.
if (_bCanExecuteCache != temp)
{
_bCanExecuteCache = temp;
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, new EventArgs()); //this line somehow leads to another call into here --> infinite loop
}
}
return _bCanExecuteCache;
}
The Search grid ItemSource is bound to a VM via a Locator like so:
<sdk:DataGrid AutoGenerateColumns="False" x:Name="gridContacts" IsTabStop="False" Grid.Row="3" ItemsSource="{Binding Path=SearchContactsViewModel.Contacts, Mode=OneWay, Source={StaticResource Locator}}" SelectionMode="Single">
Here are the Delete button Command and CommandParameter (the overall data context is set as DataContext="{Binding Source={StaticResource Locator}, Path=SearchContactsViewModel}"):
<Button x:Name="btnDelete" Style="{StaticResource ButtonStyle}" Command="{Binding Path=SearchContactsViewModel.DeleteContactCommand, Source={StaticResource Locator}}" Grid.Row="1" Grid.Column="1"
CommandParameter="{Binding Path=SelectedItem, ElementName=gridContacts}" HorizontalAlignment="Right">
Here's the simplified code in the SearchContactsViewModel:
public DelegateCommand DeleteContactCommand
{
get;
private set;
}
private bool CanDeleteContact(object param)
{
Contact c = param as Contact;
if (c == null)
return false;
return true;
}
private void DeleteContact(object param)
{
int contactID = ((Contact)param).ID;
_ServiceAgent.DeleteContact(contactID,
(s, e) =>
{
if (!CheckErrorAndResult(e.Error, e.Result, e.errMsg))
return;
SearchContacts();
MessageBox.Show("Delete successful.");
});
}
Here is the line of code that wires up the command in the VM:
DeleteContactCommand = new DelegateCommand(DeleteContact, CanDeleteContact);
BUT, if I replace CanDeleteContact with "(x) => true" for testing, I do NOT get the problem.
Please let me know if I can provide more information. I'm trying to figure out the problem with the Delete button first and hopefully apply the same solution to the Send Fax button.
Thanks,
J
***UPDATE 9/6/2011: I have noticed that in Silverlight navigation, a new instance of the page is created each time. So would that account for why this problem only happens after navigation? And why the values handled in what I thought was an infinite loop are alternating between null and non-null values? Like maybe the old page now has null, but the new page is being shown and is correct, but both are tied to the same static VM object. Just shooting in the dark here.
I think problem is that you are mixing CanExecute and CanExecuteChanged. CanExecuteChanged is definitely going to make somebody call CanExecute (which is calling CanExecuteChanged...an so on...).
So those call should be separate - CanExecute should only return some value (bCanExecuteCache). And CanExecuteChanged should be called on specific events/calls/cases.
Here is some example on Silverlight forum that might help you (shows the use of CanExecuteChanged): concrete post | whole thread

Dojo OnKeyPress Handler: TextBox value is blank

I have a Dojo form that does not contain a submit button. Instead, I added an onkeypress handler to calls a method when Enter is pressed. The problem I am having is that when I hit enter before blurring off the current field, the _process method thinks that field is empty.
Or in other words: type in field1. hit tab. type in field2. hit enter. field2 is blank unless i click off the field or shift-tab back.
Any ideas?
dojo.connect(dijit.byId("fkrform"),"onKeyPress",function(e) {
if (e.keyCode == dojo.keys.ENTER) {
_process();
}
and the method it calls:
function _process()
{
var field1 = dijit.byId("field1").value;
var field2 = dijit.byId("field2").value;
alert(username);
alert(password);
...do stuff...
}
The fields are of dojoType: dijit.form.TextBox, and the form is: dijit.form.Form
Use dijit.byId('field1').get('value') instead of directly try to access the property "value". In your example you saved the value in the variable field1 and field2 and in the alert you use the variable username and password could be the answer why you don't get anything. But you still should use the get method to get a property instead of directly access the property.
When you press "Enter" your form will submit. So you need to connect to the "onSubmit" event on the form, instead of onkeyPress or onKeyUp.
The first example i created prints the value of the input box on every key someone pressed in the console.
http://jsfiddle.net/a8FHg/
But what you really wanted was hooking into the submit. I modified the example. The new example connects to "onSubmit" and creates an alert box with the text of the user input.
http://jsfiddle.net/a8FHg/1/
For completness if jsfiddle doesn't work some day. You JavaScript should looks like this.
dojo.ready(function(){
var form = dijit.byId('form');
var box = dijit.byId('box');
var submit = function(event) {
dojo.stopEvent(event);
alert("User input was " + box.get('value'));
};
dojo.connect(form, 'onSubmit', submit);
});
Assuming your form in your HTML has the id form and your box have the id box.