Mvvm Light & EventToCommand - Textbox LostFocus firing twice - silverlight

I have a few textboxes on a form that, when focus is lost, I'd like to call a setter stored procedure to save the data, then in my callback function call a getter stored proc which will update a job costing summary on my form. I'm using Mvvm light & when I try & bind an EventToCommand on a LostFocus EventTrigger, my command is fired twice.
I understand this is due to event bubbling, but I'm not sure how to make sure my method is only actually fired once. Here's my xaml:
<TextBox x:Name="txtMiles" Grid.Row="1" Width="80" Grid.Column="2" Margin="2" Text="{Binding Miles, Mode=TwoWay}" HorizontalAlignment="Center" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<cmd:EventToCommand Command="{Binding UpdateJobCost}" CommandParameter="{Binding Text, ElementName=txtMiles}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
And my ViewModel:
public RelayCommand<string> UpdateJobCost { get; set; }
public WorkOrderControlViewModel(TSMVVM.Services.IWorkOrderService workOrderService)
{
WorkOrderService = workOrderService;
RegisterCommands();
LoadData();
}
private void RegisterCommands()
{
UpdateJobCost = new RelayCommand<string>((value) => updateJC(value));
}
private void updateJC(string value)
{
//Handle Setter service call here
}
Many thanks,
Scott

I haven't seen that problem before with EventToCommand. There might be something funky in your app that's causing the problem.
In general I don't rely on the UI to do the right thing. If updateJC shouldn't execute before a previous call has finished, consider adding an "isUpdatingJC" flag in your class. Only update the JC when the flag is false, and set it to true before you get started with the update. That way you don't get in a tight spot because some UI has issues.
Hope that helps...
Cheers!

The problem wasn't with updateJC firing async and not being complete when it fires again. I want it to only fire once. I ended up just creating a class for this form which contained a property for each of the fields. Whenever I update the property, I call updateJC which gathers the object & sends along for processing

Related

Update another UI element when propertychanged Xaml

I want to know if it's possible to update another UI element when propertychanged gets invoked.
Here's an example:
public string TestString
{
get { return testString; }
set
{
testString = value;
OnPropertyChanged("TestString");
}
}
public TestClass TestClassInstance { get; set; }
Suppose my test class has a method called Update. How do I set it so that Update gets called when TestString property has changed in Xaml? I know I could always attach the method to the eventhandler but I wanted to know if I could do it in Xaml. Maybe using Behaviours or something like that.
Can you tell me if this is possible and if so can you help lead me in the right direction?
It sounds like you are looking for Behaviours
<!-- This collection element might exist if you have already added other behaviors. -->
<i:Interaction.Triggers>
<i:EventTrigger SourceName="textBox" EventName="TextChanged">
<ic:CallMethodAction MethodName="Update" TargetObject="{Binding TestClassInstance}">
</ic:CallMethodAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Textbox x:Name="textBox" Content="{Binding TestString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
But I am not sure if you can catch up changes from code via this method. If your Update Method is a command, you should use InvokeActionCommand instead.
<cmd:InvokeCommandAction Command="{Binding TestClassInstance.Update}"/>
But why would you want to put this into XAML? Is it a Behavior very specific to a certain view or application type?

Issues with EventToCommand for the Loaded event

So I am using GalaSoft's EventToCommand for binding my View's Loaded event to my command in my ViewModel. The binding is working great but my Command is never being executed. The only way I have been able to get this to work is to handle the Loaded event in my View's code behind and then cast my DataContent to my VM and tell it to run my code (which the command is trying to do). Obviously this isnt very nice when trying to do it all MVVM like. FYI I did try the MouseEnter event and that worked great so that makes me think its a timing issue. Also, my View is a user control.
View:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand PassEventArgsToCommand="False" Command="{Binding Path=DownloadDataCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
ViewModel:
public RelayCommand DownloadDataCommand
{
get { return new RelayCommand(() => DownloadDataAsync()); }
}
Ive tried calling simple methods that do pretty much nothing and my relay command is still not invoked. So I doubt its the action within the RelayCommand. Does anyone see what I am doing wrong?

Wait cursor in WPF between PropertyChanged and Bind

I have a WPF App and I'm using MVVM.
In my view model I have:
private string logs;
public string Logs
{
get { return logs; }
set
{
logs = value;
OnPropertyChanged("Logs");
}
}
private void ExecLoadData()
{
using (new WaitCursor())
Logs = LogFile.ReturnContent();
}
private RelayCommand loadData;
public ICommand LoadData
{
get
{
if (loadData == null)
loadData = new RelayCommand(param => this.ExecLoadData());
return loadData;
}
}
In View:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadData}" />
</i:EventTrigger>
</i:Interaction.Triggers>
I'm noticing that between the shooting of the OnPropertyChanged and presentation of data on the page occurs a delay.
I need a way to display the wait cursor to the data to be displayed on the screen.
Already implemented the method WaitCursor() but the wait cursor only appears until the data file is loaded into memory, that is, between the loading of data in memory until the data is displayed on the page the cursor remains normal.
Any tips?
Edit (Final solution with help of AngelWPF):
private Boolean isBusy = false;
public Boolean IsBusy
{
get { return isBusy; }
set
{
if (isBusy == value)
return;
isBusy = value;
OnPropertyChanged("IsBusy");
}
}
private string logs;
public string Logs
{
get { return logs; }
set
{
logs = value;
OnPropertyChanged("Logs");
}
}
public void ExecuteBusy(DoWorkEventHandler doWorkEventHandler)
{
IsBusy = true;
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += doWorkEventHandler;
backgroundWorker.RunWorkerCompleted += (sender, e) => { IsBusy = false; };
backgroundWorker.RunWorkerAsync();
}
protected override void ExecLoadData()
{
LoadLogs();
}
private void LoadLogs()
{
ExecuteBusy((sender, e) =>
{
Logs = LogFile.ReturnContent();
});
}
<Page.Resources>
<ut:BooleanVisibilityConverter x:Key="BooleanVisibilityConverter" />
</Page.Resources>
<Page.DataContext>
<vm:ManutencaoMonitoracaoLogsViewModel/>
</Page.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadData}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<TextBox Text="{Binding Logs, Mode=OneWay}" VerticalScrollBarVisibility="Auto" IsReadOnly="True" BorderBrush="White" />
<Border BorderBrush="Black" BorderThickness="1" Background="#80DBDBDB" Grid.RowSpan="3"
Visibility="{Binding IsBusy, Converter={StaticResource BooleanVisibilityConverter}}">
<Grid>
<ct:LoadingAnimation HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
</Grid>
You shouldn't have any reference to your cursor in a model implementation, how do you call from UI the ExecelLoadData() method? I suggest to change cursor status before to make the call and rechange when it returned
This needs orchestrating any heavy functionalities via something we call as AsyncWorker. This is a asynchrnous command execution using background worker. It has a one time trigger flag that is initiated from the view model as true, so that it runs in the adorner of your window when any heavy functionality is delegated to it. When the functionality is executing the animation indicates the user that a possibly delayed functionality is running and he/she should wait. Then when the delegate finishes, AsyncWorker itself hides the animation and displays the Page properly back to user.
http://elegantcode.com/2009/08/21/a-simple-wpf-loading-animation/
I can envision it can be done this way...
Characteristics of `AsyncWorker`
1. It is a Control that runs an animation such as
a neverending progressing progress bar
or rotating circles etc. in the adorner of the UI.
2. It accepts the parent panel on which the waiter animation is shown.
3. It has a boolean dependency property say "StartOperation".
When this changes to true we start the animation.
When true this also begins excuting the `WorkerDelegateCommand` given below.
4. It also has a ICommand dependency property called "WorkerDelegateCommand"
This will be supplied from your `ViewModel`.
It will hold the time consuming operation as a delegate.
So basically when we set AsyncWorker.StartOperation to true, we render the adorner of the parent panel with an animating storyboard and kick off a background worker. This background worker runs the WorkerDelegateCommand on a different thread. Thus your slow operation runs on a another thread than UI. meanwhile the async worker animation keeps running. When the WorkerDelegateCommand delegate finishes its slow work, the background woker DoWork call exits and RunCompleted is called. In this we set StartOperation to false.
The way we can configure this AsyncWorker is this way...
<Grid>
<Button Content="Do some slow work"
Command="{Binding RunAsyncWorkerCommand}" />
<AsyncWorker
ParentPanel="{Binding RelativeSource={RelativeSource
AncestorType={x:Type Grid}}}"
StartOperation="{Binding StartSlowWork, Mode=TowWay}"
WorkerDelegateCommand="{Binding MySlowDelegateCommand}"
Visibility="Collapsed" />
</Grid>
So in above example, when the buton is clicked the grid, that contains the button, shows a waiter animation and begins to perform the slow operation. For this your DataContext and / or ViewModel needs three properties...
1. `StartSlowWork` - A boolean flag to start AsyncWorker which is TwoWay bound.
2. `RunAsyncWorkerCommand` - Command to set the `StartSlowWork` flag to true.
3. `MySlowDelegateCommand` - Command to execute slow work.
Once you have this in place, every slow executing operations can be moved to AsyncWorker.

WPF - View model updating property via background worker, but view is not updating some elements until focused

View model is loading data asynchronously using background worker thread in model. All properties in the model and view model raise the property changed events, and all properties are being updated in the view as expected, except 2 buttons whose IsEnabled state depends on the outcome of some properties that are loaded.
The frustrating part is that as soon as I focus on any part of the view, or set a breakpoint after the properties are updated (create a delay), then the buttons IsEnabled state is updated as expected. So it appears to be a timing issue.
Any clues as to how to the best way to solve this? I'm using mvvm-light framework, but that shouldn't matter.
I've tried binding IsEnabled to the button instead of just relying on the Command property, but that made no difference. I've confirmed via logging that view model properties are set and the PropertyChanged event is being raised for the properties associated with the buttons.
Considering sending a message using mvvm-light messenger from the view model to the view on the async completed event and then somehow? triggering a view refresh, but that seems like a kludge.
Update
Thanks to blindmeis' answer, I tested the button behaviour without the Command binding set, i.e. just binding IsEnabled property, and it works as expected!
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
IsEnabled="{Binding CanLoadProjects}" />
Obviously it's not great because I can no longer execute the command :) but as soon as I add the command back, it stops behaving:
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
Command="{Binding LoadProjectsCommand}" />
Leaving IsEnabled binding doesn't solve the problem, but that seems like a good clue.
The view model command code:
public ICommand LoadProjectsCommand
{
get
{
if (_loadProjectsCommand == null)
{
_loadProjectsCommand = new RelayCommand(loadProjects, () => CanLoadProjects);
}
return _loadProjectsCommand;
}
}
Workaround
Wire up the Click event and avoid Command. Would be nice to solve it from the view model, but this works:
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
IsEnabled="{Binding CanLoadProjects}"
Click="loadProjects_Click"/>
Code behind:
void loadProjects_Click(object sender, RoutedEventArgs e)
{
SettingsViewModel vm = (SettingsViewModel)DataContext;
vm.LoadProjectsCommand.Execute(null);
}
Answer from other thread:
When your BackgroundWorker completes, call CommandManager.InvalidateRequerySuggested();
By default, Commands are only requeried occasionally by WPF. Otherwise, there would be a huge amount of overhead in constantly calling "CanExecute" on every ICommand implementation. Calling the above method forces the CommandManager to update immediately.
This will force the Commands to re-enable/disable appropriately.
EDIT:
i use a simpler but not so beautiful workaround. i simply call OnPropertyChanged("MyICommand") for my commands in my BackgroundWorker Completed Event.
EDIT:
here is another nice solution.
You should bind command parameter property to any updatable property on viewmodel and can execute must use this command parameter to enable button. If command parameter's target is updated, binding will enable/disable based on return value of can excute.

XAML reference control and properties in x:Array

<RichTextBox x:Name="OrigText" Margin="0,0,8,0" d:LayoutOverrides="Width"/>
<Button x:Name="OrigFileBrowse" Command="{Binding BrowseCommand}" CommandParameter="{Binding ElementName=OrigText, Path=Document}" HorizontalAlignment="Center" Margin="0,0,8,2.442" Width="75" Content="Browse" Grid.Row="1" d:LayoutOverrides="Height"/>
<RichTextBox x:Name="ModifiedText" Grid.Column="1" Margin="8,0,0,0"/>
<Button x:Name="ModifiedFileBrowse" Command="{Binding BrowseCommand}" CommandParameter="{Binding ElementName=ModifiedText, Path=Document}" HorizontalAlignment="Center" Width="75" Content="Browse" Grid.Row="1" Grid.Column="1" Margin="0,0,0,2.442" d:LayoutOverrides="Height"/>
<Button x:Name="Compare" Command="{Binding CompareCommand}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Content="Compare" Grid.Row="2" Grid.ColumnSpan="2">
<Button.CommandParameter>
<x:Array Type="RichTextBox">
<local:CompareTextView/>
</x:Array>
</Button.CommandParameter>
</Button>
Trying to get 2 items to be passed when the Compare button is clicked as it will then execute a compare command. Attempted to make use of MultiBinding however that is firing on instantiation and therefore the converter then fires accordingly. It does NOT fire when I click compare and the compare command is executed.
With that not working, I am attempting to now reference the controls within XAML to pass within an ArrayExtension. Not sure of the syntax or if it is even possible as I know you cannot bind within the ArrayExtension. The above fails since it can not construct a new CompareTextView view, which has no default constructor since I am making use of Prism...
Pretty frustrating, hopefully someone can help me out...
EDIT:
Want to clear some things up. The issue is not that I want CanExecute called again. The issue is that at instantiation of the controls, the converter is called and executed and the values are returned...but where they go I have no clue? The converter is never called again. If I could get the initial references to the FlowDocument this would all be a moot point...but it doesn't return things anywhere per se...since this is a command...if that makes sense...when making use of MultiBinding.
<Button x:Name="Compare" Command="{Binding CompareCommand}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Content="Compare" Grid.Row="2" Grid.ColumnSpan="2">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource FlowDocumentConverter}">
<Binding ElementName="OrigText" Path="Document"/>
<Binding ElementName="ModifiedText" Path="Document"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
UPDATE:
Tried what refereejoe mentions here, scroll down a little bit to see his posting. While CanExecute continually fires, this does nothing to resolve the issue. In addition I switched the MultiBinding to be a single item, it is coming back null. Again when the converter fires on instantiation the FlowDocument references are there...
ANSWER:
Abe's mention that it was being cached led me to try something else. Since I knew that the FlowDocument references were available when the converter was called I knew they were there. Something was getting fouled up. The key piece appears to be in the converter itself. I was simply returning the object[]. Then when the command fired the arg was indeed an object[] but the two items were null. I created a class called Docs, which had two properties, one for each FlowDocument reference. When the converter fired I set the properties appropriately and then returned the Docs object. Now when I initiated the compare command, the Docs object was the args and it had the reference to the FlowDocuments just as I needed! Not sure if this is by design, but the fact that the items get lost when using the object[] doesn't make sense to me.
The proper way to do this is indeed with a MultiBinding on the CommandParameter. You won't see it call your CanExecute method unless WPF is informed that the method could return a different value than it had already cached (via the CanExecuteChanged event).
Since you are relying on the parameter passed in to determine this, we have to raise the event when the parameter changes. Since we can't really determine that in the command, we can use another technique: tell WPF to poll our command anytime it polls UICommands. This is done by implementing your ICommand like so:
public class MyCommand : ICommand
{
public void Execute(object parameter) { /* do stuff */ }
public bool CanExecute(object parameter { /* determine if we can do stuff */ }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
Obviously, this prevents you from using the Prism DelegateCommand, but this will respond to changes in the command parameters.
UPDATE
Another thing to consider is that the Document property on the RichTextBox isn't actually changing. Instead, when you type into it, the content of the FlowDocument changes. Since the property instances don't change, the converter won't get fired again, and the originally converted value will get stored in the CommandParameter property.
One of the ways to force the converter to be called again is to add a Binding to the MultiBinding that is bound to a property that will change every time the text of the RichTextBox changes.
A somewhat hacky solution would be to use the IsKeyboardFocusWithin property, as that will mimic the default binding behavior of TextBox.Text (i.e. when the TextBox loses focus, the Binding updates):
<MultiBinding Converter="{StaticResource FlowDocumentConverter}">
<Binding ElementName="OrigText" Path="Document" />
<Binding ElementName="ModifiedText" Path="Document" />
<Binding ElementName="OrigText" Path="IsKeyboardFocusWithin" />
<Binding ElementName="ModifiedText" Path="IsKeyboardFocusWithin" />
</MultiBinding>
Obviously, in your converter, you will need to ignore these additional values, as they aren't relevant to your conversion.

Resources