Update another UI element when propertychanged Xaml - wpf

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?

Related

wpf xaml resource dictionary add custom property

I want to use the following xaml code for navigation in some pages:
<Button Content="Go to page2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Source" TargetObject="{Binding NavigationService, RelativeSource={RelativeSource AncestorType={x:Type Page}, Mode=FindAncestor}}">
<ei:ChangePropertyAction.Value>
<System:Uri>Page2.xaml</System:Uri>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
So I´m wondering if there is a possibility to outsource the interaction part into a style (in a resource dictionary) and add a custom property like "NavigationUri" where you can directly declare the page to navigate to.
Another idea (which would probably be the better approach) is to create a custom control and inherit from button class.
Anywhere I would prefer a more compact and lean way without code behind.
Please let me know, which is the more suitable solution and how to implement it.
Although there are various simple techniques to make our Behavior Xaml as static resource. But, we need a custom behavior, as we are using a parameter in the form of Page name to navigate to. This variable demands programming.
So, I came up with
a. Custom behavior(NavigationBehavior), and
b. Button subclassing(NavigationButton)
NavigationBehavior
using System;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace WpfApplication1.Navigation
{
public class NavigationBehavior:Behavior<NavigationButton>
{
protected override void OnAttached()
{
AssociatedObject.Click += AssociatedObject_Click;
base.OnAttached();
}
void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
{
((Page)AssociatedObject.DataContext).NavigationService.Source = new Uri(AssociatedObject.DestinationUri, UriKind.Relative);
}
}
}
NavigationButton
namespace WpfApplication1.Navigation
{
public class NavigationButton : Button
{
NavigationBehavior behavior = new NavigationBehavior();
public NavigationButton()
{
behavior.Attach(this);
}
public string DestinationUri { get; set; }
}
}
Usage :
<nav:NavigationButton Content="Navigate to Page2" DestinationUri="/Navigation/Page2.xaml" />
Important Note
We are using DataContext property in our behavior to get access to the containing page. So, set this.DataContext = this; in the constructor of your all pages.
One can try using a common base class / interface to avoid this.
you cannot have interaction triggers extracted but there is a workaround to get this done
you can create a Button in resources with interaction logic in it, and then where needed you can have a content control with content set to your resource.
something like this --
<Window.Resources>
<Button x:Key="MyButton"
Content="Go to page2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Source" TargetObject="{Binding NavigationService, RelativeSource={RelativeSource AncestorType={x:Type Page}, Mode=FindAncestor}}">
<ei:ChangePropertyAction.Value>
<System:Uri>Page2.xaml</System:Uri>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Window.Resources>
<Grid>
<ContentControl Name="MyLocalButton" Content="{StaticResource MyButton}" />
</Grid>
You should put the navigation logic in a command in each page's view model and bind the buttons' Command properties to those commands, or else give the view models each a NextPage property and bind to that.
And create separate buttons in the views. Define a Style in the resource dictionary to make them all look the same.
Defining a Button as a resource is a bad idea. Among other things, there's only one instance of it and it can have only one visual parent, so when you add it to one view it'll vanish from the last. And you run into ugly problems like this one. You're working against XAML, and XAML is already hard enough when you're working with it.

How to trigger ViewModel command for a specific button events

How can a command on a ViewModel be invoked by a specific event of a button, such as MouseDoubleClick?
You can use the EventTrigger in the System.Windows.Interactivity namespace, which is part of the so-called Prism framework. If you're just getting started with MVVM, don't care too much for Prism by now, but keep it in mind for later. Anyway, you can steel the EventTrigger
It works like this:
Reference the assembly System.Windows.Interactivity.dll
In XAML, reference the namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Then in your Button or any other control, add a EventTrigger like this:
<Button Content="Button">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding CommandToBindTo}"
CommandParameter="{Binding CommandParameterToBindTo}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
This way, you bind your event to a Command on your DataContext.
Remark
To clarify the usage, here's a kind of real life example including the ViewModel. The fictional requirement is to allow the user to select an item in a list and then perform a command which takes the selected item as a parameter:
<ListBox x:Name="ItemsList" ItemsSource="{Binding Items}" />
<Button Content="Do something with selected item">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding DoSomethingCommand}"
CommandParameter="{Binding SelectedItem,
ElementName=ItemsList}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
And that would be the ViewModel. Note how the parameter to the command is used, in the example with a generic version of a DelegateCommand object as you get it in every MVVM framework (sometimes RelayCommand). This class takes the type of the required parameter as a generic parameter (here ItemViewModel) and requires a method which takes an according parameter (here ExecuteDoSomethingWithItem(ItemViewModel ...)). The rest is WPF magic: The oject to which the CommandParameter property is bound in your XAML will be passed through as the parameter in your Execute(...) function.
public class ViewModel
{
ObservableCollection<ItemViewModel> Items { get; set; }
public ICommand DoSomethingCommand
{
get
{
return _doSomethingCommand ??
(_doSomethingCommand = new DelegateCommand<ItemViewModel>(ExecuteDoSomethingWithItem));
}
}
private DelegateCommand<ItemViewModel> _doSomethingCommand;
private void ExecuteDoSomethingWithItem(ItemViewModel itemToDoSomethingWith)
{
// Do something
}
public ViewModel()
{
Items = new ObservableCollection<ItemViewModel>();
// Fill the collection
}
}
Have fun with learning MVVM, it's worth it.
you can use attached command behaviors
=> http://geekswithblogs.net/HouseOfBilz/archive/2009/08/21/adventures-in-mvvm-ndash-generalized-command-behavior-attachments.aspx
You need to do a lot of pluming yourself if you going to use Command and Event Binding from out of the box WPF. You can gain a lot of just using existing framework such as MVVM Light Toolkit, or Cliburn Micro that already provide command and even binding.

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.

Mvvm Light & EventToCommand - Textbox LostFocus firing twice

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

Enable button based on TextBox value (WPF)

This is MVVM application. There is a window and related view model class.
There is TextBox, Button and ListBox on form. Button is bound to DelegateCommand that has CanExecute function. Idea is that user enters some data in text box, presses button and data is appended to list box.
I would like to enable command (and button) when user enters correct data in TextBox. Things work like this now:
CanExecute() method contains code that checks if data in property bound to text box is correct.
Text box is bound to property in view model
UpdateSourceTrigger is set to PropertyChanged and property in view model is updated after each key user presses.
Problem is that CanExecute() does not fire when user enters data in text box. It doesn't fire even when text box lose focus.
How could I make this work?
Edit:
Re Yanko's comment:
Delegate command is implemented in MVVM toolkit template and when you create new MVVM project, there is Delegate command in solution. As much as I saw in Prism videos this should be the same class (or at least very similar).
Here is XAML snippet:
...
<UserControl.Resources>
<views:CommandReference x:Key="AddObjectCommandReference"
Command="{Binding AddObjectCommand}" />
</UserControl.Resources>
...
<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=PropertyChanged}"> </TextBox>
<Button Command="{StaticResource AddObjectCommandReference}">Add</Button>
...
View model:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
OnPropertyChanged("ObjectName");
}
}
// Command bound to button
public ICommand AddObjectCommand
{
get
{
if (addObjectCommand == null)
{
addObjectCommand = new DelegateCommand(AddObject, CanAddObject);
}
return addObjectCommand;
}
}
private void AddObject()
{
if (ObjectName == null || ObjectName.Length == 0)
return;
objectNames.AddSourceFile(ObjectName);
OnPropertyChanged("ObjectNames"); // refresh listbox
}
private bool CanAddObject()
{
return ObjectName != null && ObjectName.Length > 0;
}
As I wrote in the first part of question, following things work:
property setter for ObjectName is triggered on every keypress in textbox
if I put return true; in CanAddObject(), command is active (button to)
It looks to me that binding is correct.
Thing that I don't know is how to make CanExecute() fire in setter of ObjectName property from above code.
Re Ben's and Abe's answers:
CanExecuteChanged() is event handler and compiler complains:
The event
'System.Windows.Input.ICommand.CanExecuteChanged'
can only appear on the left hand side
of += or -=
there are only two more members of ICommand: Execute() and CanExecute()
Do you have some example that shows how can I make command call CanExecute().
I found command manager helper class in DelegateCommand.cs and I'll look into it, maybe there is some mechanism that could help.
Anyway, idea that in order to activate command based on user input, one needs to "nudge" command object in property setter code looks clumsy. It will introduce dependencies and one of big points of MVVM is reducing them.
Edit 2:
I tried to activate CanExecute by calling addObjectCommand.RaiseCanExecuteChanged() to ObjectName property setter from above code. This does not help either. CanExecute() is fired few times when form is initialized, but after that it never gets executed again. This is the code:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
addObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Edit 3: Solution
As Yanko Yankov and JerKimball wrote, problem is static resource. When I changed button binding like Yanko suggested:
<Button Command="{Binding AddObjectCommand}">Add</Button>
things started to work immediately. I don't even need RaiseCanExecuteChanged(). Now CanExecute fires automatically.
Why did I use static resource in first place?
Original code was from WPF MVVM toolkit manual. Example in that manual defines commands as static resource and then binds it to menu item. Difference is that instead of string property in my example, MVVM manual works with ObservableCollection.
Edit 4: Final explanation
I finally got it. All I needed to do was to read comment in CommandReference class. It says:
/// <summary>
/// This class facilitates associating a key binding in XAML markup to a command
/// defined in a View Model by exposing a Command dependency property.
/// The class derives from Freezable to work around a limitation in WPF when
/// databinding from XAML.
/// </summary>
So, CommandReference is used for KeyBinding, it is not for binding in visual elements. In above code, command references defined in resources would work for KeyBinding, which I don't have on this user control.
Of course, sample code that came with WPF MVVM toolkit were correct, but I misread it and used CommandReference in visual elements binding.
This WPF MVVM really is tricky sometimes.
Things look much clearer now with the edits, thanks! This might be a stupid question (I'm somewhat tired of a long day's work), but why don't you bind to the command directly, instead of through a static resource?
<Button Command="{Binding AddObjectCommand}">Add</Button>
Since you are using the DelegateCommand, you can call it's RaiseCanExecuteChanged method when your text property changes. I'm not sure what you are trying to accomplish with your CommandReference resource, but typically you just bind the commands directly to the button element's Command property:
<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=ValueChanged}" />
<Button Command="{Binding AddObjectCommand}" Content="Add" />
This would be the relevant portion of your view model:
public string ObjectName
{
get { return objectName; }
set
{
if (value == objectName) return;
value = objectName;
AddObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Try raising CanExecuteChanged when your property changes. The command binding is really distinct from the property binding and buttons bound to commands are alerted to a change in status by the CanExecuteChanged event.
In your case, you could fire a check when you do the PropertyChanged on the bound property that would evaluate it and set the command's internal CanExecute flag and then raise CanExecuteChanged. More of a "push" into the ICommand object than a "pull".
Echoing Abe here, but the "right" path to take here is using:
public void RaiseCanExecuteChanged();
exposed on DelegateCommand. As far as dependencies go, I don't think you're really doing anything "bad" by raising this when the property that the command depends on changes within the ViewModel. In that case, the coupling is more or less contained wholly within the ViewModel.
So, taking your above example, in your setter for "ObjectName", you would call RaiseCanExecuteChanged on the command "AddObjectCommand".
I know this is an old question but I personally think it's easier to bind the textbox Length to button's IsEnabled property, e.g.:
<TextBox Name="txtbox" Width="100" Height="30"/>
<Button Content="SomeButton " Width="100" Height="30"
IsEnabled="{Binding ElementName=txtbox, Path=Text.Length, Mode=OneWay}"></Button>
If ElementName binding does not work, use:
<Entry x:Name="Number1" Text="{Binding Number1Text}" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="{Binding Number2Text}" Keyboard="Numeric"></Entry>
<Button Text="Calculate" x:Name="btnCalculate" Command="{Binding CalculateCommand}" IsEnabled="{Binding Source={x:Reference Number1, Number2}, Path=Text.Length, Mode=OneWay}"></Button>
or use:
<Entry x:Name="Number1" Text="{Binding Number1Text}" Placeholder="Number 1" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="{Binding Number2Text}" Placeholder="Number 2" Keyboard="Numeric"></Entry>
<Button VerticalOptions="Center" Text="Calculate" x:Name="btnCalculate" Command="{Binding CalculateCommand}">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Source={x:Reference Number1, Number2},
Path=Text.Length}"
Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>

Resources