UI IsDirty versus Entity IsDirty - wpf

I am well aware of the INotifyPropertyChanged and INotifyCollectionChanged for WPF binding. This has been implemented and is working as expected. Therefore, changes to a property on our entities will set their "IsDirty" flag. Which in turn, turns on the Save functionality, all via binding, beautiful you may say.
However, we recently had a change request raised to request that the Save button should become enabled as soon as the field changes (i.e. use presses the first keystroke), much like the undo application command does when you type in a textbox. Therefore the save button must know when every field is changed on the form instead of the property being changed in the entity via binding.
The main reason for this request for help, is one of our forms only has one field that changes (everything else normally remains default), but the user still has to tab out of the field to enable the Save button, instead of just being able to save straight away.

You need to change the UpdateSourceTrigger to PropertyChanged
This will update the binding everytime the text changes rather than LostFocus - which is the default for a textbox.
<TextBlock Name="txtName" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

Related

Data binding: Different triggers for different purposes

I have a WPF MVVM data form window with data validation. A lot of the controls are text boxes. Currently, the data binding trigger is set to the default, i. e. loss of focus. This means that a field is only validated when it is likely to be filled out completely. So when deleting a number and typing another number, the transient empty value will not be displayed as input error.
But a drawback is that the Save button can only be enabled when the focus moves out of the text box. (No matter where, just out of the edited control. Assuming there is anything else focusable.) If this is the only change, the user waits for the Save button to be available and nothing happens. For the Save button, I'd like to use an immediate binding trigger. How can that be done?
Edit: Forgot to mention that my Save button (which uses ICommand) is only enabled when the input is determined modified and valid. So the data will remain unmodified until data binding updates it, and that won't happen until the focus moves to another control.
I actually had a similar question a while back and the solution I ended using was a custom DependencyProperty that kicked off a timer when a key was pressed, and only actually processed the PropertyChange notification if a specific time had passed.
This means the bound property doesn't get updated (and validated) unless the user pauses in typing for a set period of times.
The code can be found here (may need a bit of cleanup), and it is used like this:
<TextBox
local:DelayedUpdateBehavior.TargetProperty="{x:Static TextBox.TextProperty}"
local:DelayedUpdateBehavior.Milliseconds="1000"
Text="{Binding MyTextProperty, UpdateSourceTrigger=Explicit}" />
Edit: Actually this link might be better. It's a markup extension so you can use it directly from your binding. I can't remember which of these two methods I used in the past, but I know it was one of them :)
<TextBox Text="{local:DelayBinding Path=MyTextProperty, Delay='00:00:01'}" />
Assuming you're using an ICommand type interface for the button click event:
You can...Implement string properties with INotifyPropertyChanged and bind them to your textbox controls. Now in your Command canexecute method you can check to see if the property is !nullorempty.
e/ grammar
Set your Binding's UpdateSourceTrigger property to PropertyChanged. The default for TextBoxes is LostFocus.
Update: So you want to have data binding working on your TextBox and only allow numbers? Have a look at this question: Create WPF TextBox that accepts only numbers
Or use a converter and bind the Save button's IsEnabled property to your TextBox (maybe using a MultiBinding if there's more than one), and use a converter which determines if the text is a valid number and returns true or false.

Momentarily disable buttons enabled through dependency properties

I have the following:
<Button Content="Do XXX" Height="23" Name="btnXXX"
IsEnabled="{Binding Path=(Model:INameOfInterface.CanDoXXX)}" />
<Button Content="Do YYY" Height="23" Name="btnYYY"
IsEnabled="{Binding Path=(Model:INameOfInterface.CanDoYYY)}" />
Working very nicely, btnXXX is enabled or disabled according to whether the interface can do XXX, btnYYY similarly for YYY.
However, to force one action per click, I want to disable all the buttons once one of them is clicked, i.e. momentarily override the dependencies and disable the buttons and then, once action has been executed, re-establish the dependency conditions.
So my question is: How do I momentarily disable all buttons and then resume the dependencies
Typically you don't "override and resume dependency properties", but design Model/ViewModel so that a property you bind to encapsulates all required states. In your case this would mean that your Model:INameOfInterface.CanDoXXX should be true initially, and be false whenever any button action is being executed (xxx, yyy, ...). So your CanDoXXX would have to know about execution of YYY. It might be a bad idea to mix it all up in your Model and this is where ViewModel comes into play.
Assume your Model instances are only self-aware: CanDoXXX is false only if XXX is being executed. You can have a ViewModel class containing a collection of models, each with its individual state. Now, all you need is to put a property in VM, e.g. CanExecuteAny that would iterate over all models and check whether any of them is doing work (CanDoXXX, CanDoYYY indicate that). Finally bind
<Button IsEnabled={Binding CanExecuteAny} />
This is just one of potential solutions and the best one depends on exact structure of your data. Here are some other thoughts:
You could put all your buttons in a panel and disable the panel instead of individual buttons.
Implement ICommand interface and bind button's Command property to it. See RelayCommand concept. Personally I'd go for it.
Since your buttons need info whether any other button has been clicked, you might find it useful to have sort of a global state accessible to all models (perfectly dependency injection; less perfectly a singleton without DI).
Having said that, the basic idea is to have all needed info in a single property you can bind to. Swaping DPs is not really a way it was designed to work. The Binding is already there to update things, so setting it more than once is repeating the same job twice.
You can create the bool property and bind all buttons IsEnable with it. Whenever u do some action , make it false and after completing the action make it true which in turn will affect all the buttons. OR u can use spinner/Busy indicator.

WPF DataGrid with textbox in template column not firing property changed

I have a datagrid with a template column, and in that template is a textbox. It's bound to the 'Quantity' property of the object in the collection that makes up itemssource. Quantity is an integer. When I add an item to the datagrid, I am adding an event handler to the PropertyChanged event of the item:
EnteredPart.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(p_PropertyChanged);
This works fine when the user enters an integer in the textbox... the p_PropertyChanged handler fires and I can disable buttons and alter quantities as necessary. When the user enters a non-integer, the handler doesn't get called, I just get a red outline around the textbox. Because of this, I can't disable the necessary buttons when I need to (they should be disabled with the quantity is not legit.) Is there any way that I can do something about this?
EDIT: I tried changing the Quantity property to a string, and this caused the property changed handler to be called when non-integral values are entered. However, I then added validation to the textbox to check for this, and if the Validate method returns false, the property changed handler once again ceases to be hit. Is there any way at all to get both validation and property changed notifications??
EDIT 2: Here another instance of this problem I'm having, in another location. I have a form for adding/editing phone numbers. The phone number textbox looks like this:
<TextBox >
<TextBox.Text>
<Binding Path="Phone.Number">
<Binding.ValidationRules>
<local:PhoneValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Upon clicking a save button, I need to check in my viewmodel if the number is valid, because if it's not, I don't want to run the save command. However, it doesn't seem as if there's any way to do that, because if the validation fails, then the Phone.Number property has a null value, and I have no way to check to see if I should run the save command. I either need access to the error state (which I thought would work by Validation.GetErrors, but doesn't), or to the actual text within the textbox, which isn't available in the viewmodel.
Just for the record, validation in the View part has many drawbacks if you are in an MVVM architecture.
You'd try here to check your model's content, in the view: you'd therefore break MVVM's architecture by calling your model in the view.
Using IDataErrorInfo will help you to fulfill MVVM's main objective (ie. clearly separate the three parts).
Just an example:
I think here you are just performing a very small validation (just check if it is an int or not).
But in a different environment, assume that your model is much more complicated and needs a deeper validation. Using IDataErrorInfo will help you to check deeply in your model without calling it from the view.
As a matter of fact, in my personal experience, as I regularly work with large and highly correlated datasets, I cannot even picture using validation without IDataErrorInfo because it'd cost me too much to investigate in all the data presented and find potential errors

Which event should be used to update a Model from TextBox (LostFocus, LostKeyboardFocus, etc) in WPF? How to set precedence of events in WPF?

I have an application in which there are lot of TextBoxes and some Buttons like Save, SaveAs,etc.
When the user edits a TextBox, I have to check the DataBase for some range, validate the range and update the DataBase.
If there is any error in value entered by user,then I should not allow the TextBox to lose focus.
I was using LostFocus event for this and it was working fine until lately I discovered a bug in my application.
Bug : The user edits a value in TextBox and then clicks on Save button; the LostFocus event is not called and so Database is not getting updated :(
Now my question is which event should I use in TextBox to update the DataBase. I tried TextChanged event but it validates for every character and making my application slow. I am confused in chosing the right event for this kind of application!
Note :** The Buttons are in different UserControl !
EDIT 1 : I have some Commands attached to click of Buttons, these Commands are getting executed before LostFocus !! Can I set precedence or something like attached behaviours or commands should get executed after LostFocus !!
EDIT 2 : I was just debugging the application by disabling some commands, what I found was in some cases, the DelegateCommand gets executed before LostFocus, so I want to avoid that. How can I go about it ? I felt during development its impossible to developa pure MVVM application so I am kind of using a bit of codebehind !
Trapping the keyboard focus within a control is usually a sign of bad UI design - it's pretty user-hostile to force the user to fix data in a control before he can type anywhere else in the UI.
That said, you shouldn't be using events at all here. You're trying to write a Windows Forms application in WPF. You should write a WPF application.
Create a class that is a logical model of your view - i.e., there's a string property for the text box and a Command property (or, more likely, a RelayCommand) for the Save button. Bind the text box to the string property, e.g.:
<TextBox Text="{Binding MyTextProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Because the UpdateSourceTrigger is PropertyChanged, the source object will get updated every time the user types a character.
Bind the button to the command property, e.g.:
<Button CommandBinding="{Binding SaveCommand}">Save</Button>
Implement the appropriate CanSave and Save methods that the RelayCommand (as described in Josh Smith's essential article on the MVVM pattern) requires, so that the button is enabled when the string property is valid and disabled when it's not.
I think the best approach is preventing a user to proceed until all valid information has been gathered.
Just like an installation wizard with Terms & Conditions Dialog and Next button. Until you check the I Agree checkbox, Next button is disabled.
This way, you don't have to worry about user proceeding without providing valid information. This way, you can use any event on TextBox to validate your data.

How can I stop Silverlight DataForm immediately saving changes back to underlying object?

I have a Silverlight master-details DataForm where the DataForm represents a street address.
When I edit the Address1 textbox, the value gets automatically committed to the bound Address object once focus leaves the textbox.
If I hit the Cancel button, then any changes are undone because Address implements IEditableObject and saves its state.
The problem is that since any change is immediately propagated to the underlying object it will be shown in the master grid before the user has actually hit Save. I also have other locations where this data is shown. This is not a very good user experience.
I've tried OneWay binding but then I can't commit back without manually copying all the fields over.
The only thing I can think of doing is to create a copy of the data first or using OneWay binding, but they both seem a little clumsy.
Does DataForm support this way of working?
The copy of the object feels a little clumsy, but I would use that: it's coming back into style with systems like ASP.NET MVC.
You then also have a good opportunity to do any level of validation you want before commiting it to what will be propagated to other bound controls.

Resources