I saw this example - Binding.UpdateSourceTrigger Property
in the example the UpdateSourceTrigger set to Explicit and then in the view code he call to UpdateSource of the TextBox name.
But if i use the MVVM dp i dont want to have names to my controls and source properties are in the VM and not in the view so what is the right way to bind controls to VM properties and set the UpdateSourceTrigger to explicit?
I want to do this because in my case its ShowDialog window and I want that the source will update only if the user click "ok"
Thanks in advance!
If you are using MVVM truely then your OK button click must be handled by some Command. This command must be coming from your ViewModel. The Expliticly bound properties must be coming from your ViewModel again. So whats stopping you.
Do not use Explicit binding but use OneWay binding.
In you button, bind a command and bind a command parameter to the OneWay bound Dependency property.
In your Command's Execute handler (which must be some method from your ViewModel), change the ViewModel's property with the parameter coming.
Raise the NotifyPropertyChanged for that property from your ViewModel.
E.g.
Assume I need to update a TextBox's Text back into my model on OK button click.
So for that I have a EmployeeViewModel class that has EmployeeName property in it. The property is has a getter and a setter. The setter raises property changed notification. The view model also has another property of type ICommand named SaveNameCommand that return a command for me to execute.
EmployeeViewModel is the data context type of my view. Myview has a TextBox (named as x:Name="EmployeeNameTxBx") OneWay bound to the EmployeeName and a Button as OK. I bind Button.Command property to EmployeeViewModel.SaveNameCommand property and Button.CommandParameter is bound to EmployeeNameTxBx.Text property.
<StackPanel>
<TextBox x:Name="EmployeeNameTxBx"
Text="{Binding EmployeeName, Mode=OneWay}" />
<Button Content="OK"
Command="{Binding SaveNameCommand}"
CommandParameter="{Bidning Text, ElementName=EmployeeNameTxBx}" />
</StackPanel>
Inside my EmployeeViewModel I have OnSaveNameCommandExecute(object param) method to execute my SaveNameCommand.
In this perform this code...
var text = (string)param;
this.EmployeeName = text;
This way ONLY OK button click, updates the TextBox's text back into EmployeeName property of the model.
EDIT
Looking at your comments below, I see that you are trying to implement Validation on a UI. Now this changes things a little bit.
IDataErrorInfo and related validation works ONLY IF your input controls (such as TextBoxes) are TwoWay bound. Yes thats how it is intended. So now you may ask "Does this mean the whole concept of NOT ALLOWING invalid data to pass to model is futile in MVVM if we use IDataErrorInfo"?
Not actually!
See MVVM does not enforce a rule that ONLY valid data should come back. It accept invalid data and that is how IDataErrorInfo works and raises error notfications. The point is ViewModel is a mere softcopy of your View so it can be dirty. What it should make sure is that this dirtiness is not committed to your external interfaces such as services or data base.
Such invalid data flow should be restricted by the ViewModel by testing the invalid data. And that data will come if we have TwoWay binding enabled. So considering that you are implementing IDataErrorInfo then you need to have TwoWay bindings which is perfectly allowed in MVVM.
Approach 1:
What if I wan to explicitly validate certain items on the UI on button click?
For this use a delayed validation trick. In your ViewModel have a flag called isValidating. Set it false by default.
In your IDataErrorInfo.this property skip the validation by checking isValidating flag...
string IDataErrorInfo.this[string columnName]
{
get
{
if (!isValidating) return string.Empty;
string result = string.Empty;
bool value = false;
if (columnName == "EmployeeName")
{
if (string.IsNullOrEmpty(AccountType))
{
result = "EmployeeName cannot be empty!";
value = true;
}
}
return result;
}
}
Then in your OK command executed handler, check employee name and then raise property change notification events for the same property ...
private void OnSaveNameCommandExecute(object param)
{
isValidating = true;
this.NotifyPropertyChanged("EmployeeName");
isValidating = false;
}
This triggers the validation ONLY when you click OK. Remember that EmployeeName will HAVE to contain invalid data for the validation to work.
Approach 2:
What if I want to explicitly update bindings without TwoWay mode in MVVM?
Then you will have to use Attached Behavior. The behavior will attach to the OK button and will accept list of all items that need their bindings refreshed.
<Button Content="OK">
<local:SpecialBindingBehavior.DependentControls>
<MultiBinding Converter="{StaticResource ListMaker}">
<Binding ElementName="EmployeeNameTxBx" />
<Binding ElementName="EmployeeSalaryTxBx" />
....
<MultiBinding>
</local:SpecialBindingBehavior.DependentControls>
</Button>
The ListMaker is a IMultiValueConverter that simply converts values into a list...
Convert(object[] values, ...)
{
return values.ToList();
}
In your SpecialBindingBehavior have a DependentControls property changed handler...
private static void OnDependentControlsChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs e)
{
var button = sender as Button;
if (button != null && e.NewValue is IList)
{
button.Click
+= new RoutedEventHandler(
(object s, RoutedEventArgs args) =>
{
foreach(var element in (IList)e.NewValue)
{
var bndExp
= ((TextBox)element).GetBindingExpression(
((TextBox)element).Textproperty);
bndExp.UpdateSource();
}
});
}
}
But I will still suggest you use my previous pure MVVM based **Approach 1.
This is an old question but still I want to provide an alternative approach for other users who stumble upon this question...
In my viewmodels, I do not expose the model properties directly in the get/set Property methods. I use internal variables for all the properties. Then I bind all the properties two-way. So I can do all the validation as "usual" because only the internal variables are changed. In the view model constructor, I have the model object as parameter and I set the internal variables to the values of my model. Now when I click on the "Save" Button (-> Save Command fires in my view model fires) and there are no errors, I set all the properties of my model to the values of the correspondng internal variable. If I click on the "Canel/Undo"-Button (-> Cancel-Command in my view model fires), I set the internal variables to the values of my untouched model (using the setters of the view model properties so that NotifyPropertyChanged is called and the view shows the changes=old values).
Yet another approach would be to implement Memento-Support in the model, so before you start editing you call a function in the model to save the current values, and if you cancel editing you call a function to restore those values...that way you would have the undo/cancel support everywhere an not just in one view model...
I've implemented both methods in different projects and both work fine, it depends on the requirements of the project...
Related
I want to call a simple Command which adds values from my GUI into the database.
My Command:
private ICommand addSpeechCommand;
public ICommand AddSpeechCommand
{
get
{
if (addSpeechCommand == null)
{
addSpeechCommand = new RelayCommand(param => AddSpeech());
}
return addSpeechCommand;
}
}
public void AddSpeech()
{
// TODO add
OrganizerBL.InsertSpeech(new Speech(maxSpeechId, currentSpeech.Title, currentSpeech.Summary, currentSpeech.Start, currentSpeech.End, currentSpeech.TrackId, currentSpeech.SpeakerId, currentSpeech.ConferenceId, currentSpeech.Room));
this.LoadSpeeches();
}
-- this commented out row shows how i dealt with it when a row of my datagrid was selected. but i want it to work without a currentSpeech
My XAML:
<Label x:Name ="lblTitle" Content="Title"/>
<TextBox x:Name="txtTitle" Text="{Binding CurrentSpeech.Title, Mode=TwoWay}" Margin="2,144,0,0" Height="20" VerticalAlignment="Top"/>
and other textboxes...
I really don't know how to access the values of the textboxes from the command to call my insertSpeech method...
Sorry for my english :)
UPDATE:
I'm getting a nullreference exception because my currentSpeech is null.
Is there a way to solve this without the currentSpeech?
The reason you get the NullReferenceException is probably because it's instanced in the property itself. When you cerate a binding, it's created to the property as it is at that stage. And you bind to the property when it's NULL. IT's actually created inside the property, but the Binding will never know that.
First of all, I would remove all logic from properties.
I would also implement the INotifyPropertyChanged to the class, and call the PropertyChanged in the property's "set". This means that the UI will know of any changes to the porperty.
Then I would create a depencency property for the property, if it's used in any Binding ot XAML.
Last, I would instance the command in the class's constructor.
Logic don't (in my book) belong to properties.
How to do
1. Bind TextBox.Text to View model property
2. Use View model property in Command Handler.
In your case, You have binded TextBox.Text to CurrentSpeech.Title, but using this.Title.
In you command, change this.Title to currentSpeech.Title
In my WPF application I have a CheckBox whose IsChecked value is bound to a property in my viewmodel. Notice that I have commented out the actual line which sets the value in my viewmodel. It's the standard pattern:
View.xaml
<CheckBox IsChecked="{Binding Path=SomeProperty}" />
ViewModel.cs
public bool SomeProperty
{
get { return this.mSomeProperty; }
set
{
if (value != this.mSomeProperty)
{
//this.mSomeProperty = value;
NotifyPropertyChanged(new PropertyChangedEventArgs("SomeProperty"));
}
}
}
When I click the CheckBox I expect nothing to happen, since the value of this.mSomeProperty does not get set. However the observed behavior is that the CheckBox is being checked and unchecked regardless of the value of this.mSomeProperty.
What is going on? Why isn't my binding forcing the CheckBox to show what the underlying data model is set to?
Because WPF does not automatically reload from the binding source after updating the source. This is probably partly for performance reasons, but mostly to handle binding failures. For example, consider a TextBox bound to an integer property. Suppose the user types 123A. WPF wants to continue showing what the user typed so that they can correct it, rather than suddenly resetting the TextBox contents to the old value of the property.
So when you click the CheckBox, WPF assumes that it should continue to display the control state, not to re-check the bound property.
The only way I've found around this, which is not very elegant, is to raise PropertyChanged after WPF has returned from calling the property setter. This can be done using Dispatcher.BeginInvoke:
set
{
// ...actual real setter logic...
Action notify = () => NotifyPropertyChanged(...);
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, notify);
}
This could be made a bit less horrible by consolidating it into the NotifyPropertyChanged implementation so that you wouldn't have to pollute individual properties with this implementation concern. You might also be able to use NotifyOnSourceUpdated and the SourceUpdated attached event, but I haven't explored this possibility.
I've got a button strip usercontrol that I want to have on most of my forms.
I've added the commands as follows ...
public ICommand Create
{
get
{
return buttonCreate.Command;
}
set
{
buttonCreate.Command = value;
}
}
I've set these as dependency properties so I can bind to them ...
public static readonly DependencyProperty CreateCommandProperty =
DependencyProperty.Register(
"Create",
typeof(ICommand),
typeof(StandardButtonStrip),
new PropertyMetadata((ICommand)null));
I'm then binding my usercontrol to a command ...
<commoncontrols:StandardButtonStrip HorizontalAlignment="Stretch" Create="{Binding CreateCommand}" />
I'm setting up the command as follows ...
_viewModel.CreateCommand = new DelegateCommand<object>(OnCreateCommand, CanCreate);
but despite the fact that i'm always returning true on my CanCreate method the button is disabled ... if I put a break point on return true it never fires!
public bool CanCreate(object parm)
{
return true;
}
I've tried this to see if it will refresh the binding, but no joy!
_viewModel.CreateCommand.RaiseCanExecuteChanged();
I think the problem is down to the user control and how I'm passing the Command up as a property, but not sure ...
The way this looks, you have a dependency property on a view model. If you are really using MVVM, that is definetly not the way to go about it (not because of religious adherence to a pattern, but because it's not the optimal way).
First of all, is your view model a DependencyObject?
If it is, you should downgrade it to a class which implements INotifyPropertyChanged. Why? Because the Button's Command property is a DependencyProperty itself (inherited from ButtonBase) and supports databinding already.
If it isn't, then a dependency property on it will not work, which is fine, because you shouldn't have dependency properties on your view model in the first place.
What you should do, is have the view model as the DataContext for your control (I'm guessing you already have that set up). Then change your view model's CreateCommand to a plain ICommand, and bind the createButton's Command property like this (in your default StandardButtonStrip style)
<Button Name="createButton" HorizontalAlignment="Stretch" Command="{Binding CreateCommand}" />
This way, it will still be reusable, you just need to ensure that any view model you associate with your user control has a property CreateCommand of type ICommand (and that view model will be inherited down to the button control by default - one of the nicest things the wpf guys thought up).
So to recap, you should do it the other way around, more or less.
Hope this was helpful, cheers.
One caveat to the accepted answer -
Using a delegate command I could only get this to work if I created a new
Command<T> : DelegateCommand<T> and hooked up the Command Manager.
event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Have you overridden any of the user control's functionality?
One common problem is overriding a method without calling the base's implementation. For example, if you've overridden OnContentChanged() without calling base.OnContentChanged(), you may have accidentally supressed the firing of the ContentChanged event.
So I am learning WPF right now, and want to do a simple databind between a bool value, and whether or not a MenuItem is enabled or not.
I have coded it like this:
<MenuItem Name="miSaveFile" Header="Save" Click="miSaveFile_Click"
IsEnabled="{Binding}" />
And in the .cs file I set:
miSaveFile.DataContext = dataChanged;
For some reason the MenuItem doesn't seem to be properly reflecting the state of dataChanged.
What am I missing?
You are better off binding to an object than to a primitive type. This object is often referred to as the "model" for your view.
WPF uses the INotifyPropertyChanged interface for the model (or often view-model) to notify the view that the model has changed states.
So you will first want to define a data class as the model that implements the INotifyPropertyChanged interface and fires the PropertyChanged event whenever a property is changed.
When you set a binding, you have 5 main elements on the binding to worry about. The binding has a source object, a source path on the source object, a target object, a target property on the target object, and an optional converter.
If you do not specify the source, it defaults to the DataContext of the control the binding is set on. There are other options for setting the source. Here is a Microsoft article on setting the source. You can then set the path of a property to pull out of the source for the binding. In your case, the source is a boolean and there is no path because the binding is using the whole source object.
The target is always the control that you set the binding on, and the target property is the property on this control that you are binding to. In this case, MenuItem and IsEnabled.
A converter can optionally convert the source value into a value that is compatible with the target property. You can use any object for a converter that implements IValueConverter or IMultiValueConverter (for MutliBindings).
In your case, I would first create a model that implements INotifyPropertyChanged. Next, I would assign the DataContext of the menu to an instance of the model. Then I would set the binding to:
IsEnabled="{Binding Path=EnableFlag}"
(Where EnableFlag is a boolean property in the model that you want to menu to bind to)
If you set up the INotifyPropertyChanged interface correctly, the menu item will be enabled/disabled whenever you change this property on the model.
For a MenuItem, would it not be a better approach to use the Command model rather than Click and IsEnabled properties?
After InitialiseComponent():
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, fileSaveExecuted, fileSaveCanExecute));
Additional methods:
/* here is where you set e.CanExecute true for enabled: */
private void fileSaveCanExecute(object x, CanExecuteRoutedCommandEventArgs e)) { e.CanExecute = ...; e.Handled = true; }
/* here is where you act on the command: */
private void fileSaveExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
XAML:
<MenuItem Header="_Save" Command="Save"/>
How does the UI know when the dataChanged variable has actually changed?
I normally bind to a property on an object, and let that class implement INotifyPropertyChanged. The UI is then "automagically" updated whenever the PropertyChanged event is invoked.
So I would have
<MenuItem Name="miSaveFile" Header="Save" Click="miSaveFile_Click"
IsEnabled="{Binding DataChanged}"</MenuItem>
and then set miSaveFile.DataContext = myObject.DataChanged
(myObject can be this if you are using the codebehind)
Edit: I just made a quick test. If you set the data context directly to the DataChanged property, an subscription to the PropertyChanged event on the owner object is not added. But the solution I suggest works.
I cannot get a two-way bind in WPF to work.
I have a string property in my app's main window that is bound to a TextBox (I set the mode to "TwoWay").
The only time that the value of the TextBox will update is when the window initializes.
When I type into the TextBox, the underlying string properties value does not change.
When the string property's value is changed by an external source (an event on Click, for example, that just resets the TextBox's value), the change doesn't propagate up to the TextBox.
What are the steps that I must implement to get two-way binding to work properly in even this almost trivial example?
Most probably you're trying to bind to a .net CLR property instead of a WPF dependencyProperty (which provides Change Notification in addition to some other things).
For normal CLR property, you'd need to implement INotifyPropertyChanged and force update on the textbox in the event handler for PropertyChanged.
So make your object with the property implement this interface, raise the event in the property setter. (So now we have property change notification)
Make sure the object is set as the DataContext property of the UI element/control
This threw me off too when I started learning about WPF data binding.
Update: Well OP, it would have been a waste of time if i was barking up the wrong tree.. anyways now since you had to dig a bit.. you'll remember it for a long time. Here's the code snippet to round off this answer. Also found that updating the textbox happens automatically as soon as I tab-out.. You only need to manually subscribe to the event and update the UI if your datacontext object is not the one implementing INotifyPropertyChanged.
MyWindow.xaml
<Window x:Class="DataBinding.MyWindow" ...
Title="MyWindow" Height="300" Width="300">
<StackPanel x:Name="TopLevelContainer">
<TextBox x:Name="txtValue" Background="AliceBlue" Text="{Binding Path=MyDotNetProperty}" />
<TextBlock TextWrapping="Wrap">We're twin blue boxes bound to the same property.</TextBlock>
<TextBox x:Name="txtValue2" Background="AliceBlue" Text="{Binding Path=MyDotNetProperty}" />
</StackPanel>
</Window>
MyWindow.xaml.cs
public partial class MyWindow : Window, INotifyPropertyChanged
{
public MyWindow()
{
InitializeComponent();
this.MyDotNetProperty = "Go ahead. Change my value.";
TopLevelContainer.DataContext = this;
}
private string m_sValue;
public string MyDotNetProperty
{
get { return m_sValue; }
set
{
m_sValue = value;
if (null != this.PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("MyDotNetProperty"));
}
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I feel the need to add some precision:
"Two ways" data binding is more than "One way" data binding.
"One way" data binding is a binding from a source to a dependency property. The source must implement INotifyPropertyChanged, in order to get change propagation from source to target.
To get the " 2 way" , so to get a propagation from Target to Source, it depends on the binding mode which you set on the Binding . If you don't set any BindingMode for your binding, the default Binding mode will be used, and this default mode is a characteristics for your target Dependency Property.
Example:
A Textbox bound to a string property, called "MyTextProperty".
In the code, you bind Textbox.Text DependencyProperty to "MyTextProperty" on object "MyObject"
--> "one way" binding : the setter of "My TextProperty" must raise an event Property Changed,and "MyObject" must implement INotifyPropertyChanged.
--> "2 ways data binding": in addition to what is needed for "One way", bindingMode must be set to "2 ways". In this special case, the Text DependencyProperty for Textbox does have "2 ways" as default mode, so there is nothing else to do !
We might need to see the code. Does your string property raise a PropertyChanged event? Or (even better) is it implemented as a DependencyProperty? If not, the bound TextBox won't know when the value changes.
As for typing into the TextBox and not seeing the property's value change, that may be because your TextBox isn't losing focus. By default, bound TextBoxes don't write their values back to the source property until focus leaves the control. Try tabbing out of it and seeing if the property value changes.
Make sure that the binding specifies two way and when the property has a change, it is immediately transmitted to the holding property.
<TextBox Text="{Binding TextBuffer,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}"/>
The above assures that the TextBox input control Text property binds to, then sends the changes back to the string property named TextBuffer in an immediate, PropertyChanged, and TwoWay fashion.