View is not being updated. ComboBox does not represent the actual value - c#

There are many related questions on the net, addressing the apparently same issue; however, none of the answers was helpful to me.
The basic scenario in short:
I have a dialog containing a ComboBox and a Reset-Button. The ComboBox has a Binding to the ViewModel. When the button is clicked, I'd like to update the item in the ComboBox. This does not work.
<ComboBox x:Name="EditorFonts" SelectedValue="{Binding FontValue, Mode=TwoWay}" />
The obvious answer or counter-question is: do I used OnNotifyPropertyChanged? Well, yes, basically I do, the following method is being called...
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
However, PropertyChanged is NULL; thus, the if-block is not raised and nothing happens.
I also found on the net that there might be a problem with SelectedValue, and I should try using SelectedItem instead. This doesn't help either.
And even if I update the ComboBox value directly (and do not use Source-to-Target-Binding), the view isn't updated.
At this point I need to add some further details about the implementation that I, unfortunately, cannot provide as code. For one it's way to complex and for another I do not all code parts. Here's a brief explanation:
The dialog box itself is provided by another team. They own the Reset-Button. And they provide an interface. When implementing this interface and using MEF Exports, my subpage will be shown in the dialog and I get notified when the Reset-Button is clicked. This all is implemented in another assembly and I do not know a lot about their implementation.
So, when I get the notification about the Reset-Button, I try to refresh the value as described above. As this didn't work, I put an own Reset-Button (let's call it Reset2) next to my ComboBox. When Reset2 is triggered, I raise the very same code and now the ComboBox is being updated. Also, the PropertyChanged is not NULL.
My conclusion is, that the implementation of my DataBinding cannot be wrong. Actually, when closing the dialog and opening it again (after using Reset1), the ComboBox represents the correct value.
Furthermore, it doesn't matter to which property I bind.
I tried any of the following mechanisms to refresh the ComboBox but to no success.
BindingExpression be = Value.GetBindingExpression(ComboBox.SelectedValueProperty);
be.UpdateTarget();
EditorFonts.Dispatcher.Invoke(emptyDelegate, DispatcherPriority.Render);
EditorFonts.InvalidateVisual();
EditorFonts.InvalidateProperty(ComboBox.SelectedValueProperty);
EditorFonts.InvalidateMeasure();
EditorFonts.InvalidateArrange();
EditorFonts.UpdateLayout();
I'm pretty sure that once I found out why the PropertyChanged value is NULL, the ComboBox will be updated properly. However, I've got absolutely no idea how to fix this issue.
So, does anyone have any guesses on what's might be going wrong? What else I can look for, as the trivial issues regarding DataBinding (like not calling OnPropertyChanged) are not the cause.
I tried a dirty "hack" which is whenever Reset1 is clicked, I raise an event on Reset2.Clicked:
Reset2.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
Then PropertyChanged does indeed not equal NULL, but–funny enough–the ComboBox is still not updated.
I don't not for what purpose I tried that, but I was sure that it would succeed, so I'm more confused now about why the ComboBox refuses to show what I'd like to see.

When using the INotifyPropertyChanged interface, it is customary to see code like this:
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
What this means in plain English is this:
If there are any event handlers attached to the PropertyChanged event, then call them with this PropertyChangedEventArgs object
Therefore, if handler is null, then no handlers have been attached to that event.

Related

Detecting dirty using winforms data binding

I am using 2 way binding with winforms text boxes.
I need to work out if the user has changed my data
Looking at the help for
the CurrentItemChanged Event
It seems that this event does fire if a property has changed, however it also fires if current has changed.
Is there a way to tell whether the data has changed?
a similar question is also asked here
but not answered in my opinion
Oliver mentions "if your object within the List support the INotifyPropertyChanged event and you replace the List by a BindingList you can subscribe to the ListChanged event of the BindingList to get informed about any changes made by the user."
My application meets these conditions but I cant get this working. The ListChangedType.ItemChanged property looked hopeful, but it changes when I navigate to the next record without changing the data
I found a link at Microsoft here but surely it cant be that hard!
This seems to work
void bindingSource_BindingComplete(object sender, BindingCompleteEventArgs e)
{
if (e.BindingCompleteContext == BindingCompleteContext.DataSourceUpdate)
{
var person = (Person)bindingSource.Current;
if ( person.State == State.Unchanged && (e.BindingCompleteState == BindingCompleteState.Success)
&& e.Binding.Control.Focused)
{
person.State = State.Modified; // using Julie Lerman's repositories technique
}
}
}

How can I avoid events firing during InitializeComponent?

I have a simple WPF page with a couple of RadioButtons, each RadioButton is registered with a Checked event handler so that when the selection is changed something can happen. By default I want to have one of these RadioButtons selected, so I have set the IsChecked property to True in the xaml. Something like this:
<RadioButton Checked="Radio_Checked" IsChecked="True">One</RadioButton>
<RadioButton Checked="Radio_Checked">Two</RadioButton>
The problem with this is that during InitializeComponent the IsChecked property causes the event to fire, this causes a null reference exception because my event handler attempt to use elements that have not been initialized yet.
Currently I have gotten around the issue by checking if the page IsInitialized within my handler as follows:
private void Radio_Checked(object sender, RoutedEventArgs e)
{
if (this.IsInitialized)
{
if(MyRadioButton.IsChecked.GetValueOrDefault())
{
//SomeOtherElement is not initialized yet so it is null
SomeOtherElement.Visibility = Visibility.Visible;
}
}
}
I would like to avoid having to use if (this.IsInitialized) in all my event handlers, as this is something I never had to do in WinForms.
So my question is, can I handle this a different way without having to add extra code to all my event handers?
To be honest, I'm surprised that you aren't checking for null in your handlers anyway... checking for IsInitialised is just a slight variation on checking for null. Handling null values is just part of good programming and let's face it, it's not really adding a lot of code.
So to answer your question, I would say 'No, there is no way around checking for null (or IsInitialised) in your event handlers if you don't want NulReferenceExceptions to occur'.
However, when using the MVVM methodology, we don't use many events, preferring instead to use data binding and ICommand instances where possible. When we do need to use events, we generally use them in Attached Properties, but there you will still need to check for null values.
You can remove the event handler from the xaml and add it after InitializeComponent();
radioButton1.Checked+=Radio_Checked;
Every element is created in that order it is in your XAML.
<RadioButton x:Name="MyRadioButton" ...>
<YourElement x:Name="SomeOtherElement" ...>
I assume in your XAML the RadioButton is placed before the other element you are referencing. On creation of an element in InitializeComponent all properties are set and also all events are fired. So SomeOtherElement does not exist in that moment.
The solution is quiet simple:
<YourElement x:Name="SomeOtherElement" ...>
<RadioButton x:Name="MyRadioButton"...>
Set SomeOtherElement before your RadioButtons.
If there are reasons to not switch the order of your elements in your XAML, then use the already mentioned null check:
if (SomeOtherElement != null)
{
SomeOtherElement.Visibility = Visibility.Visible;
}

RejectChanges() with ViewModel and MVVM design pattern and UI update

I am having a problem with DomainContext.RejectChanges() and reflecting the rollback in the UI. Here is my scenario.
I have a Model (Entity) generated for use with RIA services (I'll call it Foo)
I have a ViewModel that wraps Foo and extends it (I'll call it FooViewModel)
I have a View that is using Binding to display and update data using the FooViewModel
I have an "outer" ViewModel that holds an ObservableCollection of FooViewModels
The "outer" View has a list box bound to the ObservableCollection
So essentially there is a listbox of FooViewModels on one screen...when you select an item a childwindow is displayed to edit that particular FooViewModel. The FooViewModel is serving both the listbox and the childwindow.
Editing works just fine. A change in the childwindow reflects in the listbox immediately because I am calling RaisePropertyChanged() when the viewmodel properties are updated.
However, If I perform a DomainContext.RejectChanges()...the underlying entity gets rolled back (all changes reverted as expected)...however the FooViewModel isn't aware that this change has occurred and thus the UI isn't updated. If I reselect the item in the listbox on the first screen, the childwindow is displayed with the rolled back changes (which is what I want). The listbox still isn't updated though.
When I reject changes, if I kludge a RaiseProperyChanged() for the field that I changed...the UI listbox does update.
How do I get the UI to update when the underlying entity is rejected?? And how do I do it without tracking what properties of the viewmodel were rolledback? There has to be an easy way to accomplish this that I am just missing.
Something you could try is use the PropertyChanged event on the underlying entity Foo to trigger a RaisePropertyChanged pass on the FooViewModel properties.
so making some assumptions (so this code make sense):
You have a private variables in your FooViewModel
private Foo _foo;
private DomainContext _context;
You have a method on your FooViewModel that is calling RejectChanges() on your domain context.
Like so:
public void RejectChanges()
{
_context.RejectChanges();
}
We have a method that raises the PropertyChanged event on our FooViewModel
Like so:
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(propertyName);
}
Ok, now we have that established, lets have a look at exactly what happens when you call RejectChanges() on a domain context.
When you call RejectChanges() this bubbles down through the DomainContext to its EntityContainer, then to each EntitySet in that container and then to each Entity in the set.
Once there (and in the EntitySet), it reapplies the original values if there was any, removes the entity if it was added, or adds it if it was deleted. If there was changes to the values, then it applies them back to the properties.
So theoretically, all the RaisePropertyChanged(), that are generated in the entity properties, should be triggered.
NOTE: I haven't actually tested this. If this isn't the case, then none of this works :P
So we can hook into PropertyChanged event of the Foo entity, and raise the PropertyChanged event on our FooViewModel.
so our RejectChanges() method might look like this:
public void RejectChanges()
{
Func<object, PropertyChangedEventArgs> handler = (sender, e) =>
{
RaisePropertyChanged(e.PropertyName);
};
_foo.PropertyChanged += handler;
_context.RejectChanges();
_foo.PropertyChanged -= handler;
}
So we hook up an event handler to our Foo entity, which calls the FooViewModel.RaisePropertyChanged method with the property name that is changing on the Foo entity.
Then we reject changes (which triggers the property changes),
then we unhook the event handler.
Pretty long winded, but I hope this helps :)
I presume that the call to DomainContext.RejectChanges() is happening within the ViewModel as you probably bound that to some command or method called from the parent ViewModel. Since all your bindings to the data is done on the ViewModel properties you will have to raise the property change event on them when you directly manipulate the model outside of those properties. Which you probably doing already.
public void RejectChanges()
{
DomainContext.RejectChanges();
RaisePropertyChangeOnAll();
}
How you implement RaisePropertyChangeOnAll() can be done simply with a list of RaisePropertyChange("...") for each property or you could do it through reflection (if Silverlight permissions allow, not too sure about it) by adding an Attribute on each property you want to raise. Find all the properties that are tagged with it and call RaisePropertyChanged on the MemberInfo.Name value.
[Raiseable]
public string SomeValue
{
...
}
Just an idea but may not be a perfect solution.

How not to lose binding source updates?

Suppose I have a modal dialog with a textbox and OK/Cancel buttons. And it is built on MVVM - i.e. it has a ViewModel object with a string property that the textbox is bound to.
Say, I enter some text in the textbox and then grab my mouse and click "OK". Everything works fine: at the moment of click, the textbox loses focus, which causes the binding engine to update the ViewModel's property. I get my data, everybody's happy.
Now suppose I don't use my mouse. Instead, I just hit Enter on the keyboard. This also causes the "OK" button to "click", since it is marked as IsDefault="True". But guess what? The textbox doesn not lose focus in this case, and therefore, the binding engine remains innocently ignorant, and I don't get my data. Dang!
Another variation of the same scenario: suppose I have a data entry form right in the main window, enter some data into it, and then hit Ctrl+S for "Save". Guess what? My latest entry doesn't get saved!
This may be somewhat remedied by using UpdateSourceTrigger=PropertyChanged, but that is not always possible.
One obvious case would be the use of StringFormat with binding - the text keeps jumping back into "formatted" state as I'm trying to enter it.
And another case, which I have encountered myself, is when I have some time-consuming processing in the viewmodel's property setter, and I only want to perform it when the user is "done" entering text.
This seems like an eternal problem: I remember trying to solve it systematically from ages ago, ever since I've started working with interactive interfaces, but I've never quite succeeded. In the past, I always ended up using some sort of hacks - like, say, adding an "EnsureDataSaved" method to every "presenter" (as in "MVP") and calling it at "critical" points, or something like that...
But with all the cool technologies, as well as empty hype, of WPF, I expected they'd come up with some good solution.
At critical points, you can force the binding to push through to your view model:
var textBox = Keyboard.FocusedElement as TextBox;
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
Edit:
OK, since you don't want hacks we have to face the ugly truth:
In order to implement a clean view, the properties exposed by your view model should be friendly to frequent binding updates.
An analogy we can use is a text editor. If the application was a giant text box bound to a file on disk, every keystroke would result in writing the whole file. Even the concept of saving is not needed. That's perversely correct but terribly inefficient. We all immediately see that the view model needs to expose a buffer for the view to bind to and this re-introduces the concept of save and forces state handling in our view model.
Yet, we see this is still not efficient enough. For even medium-sized files the overhead of updating the whole-file buffer on every keystroke becomes unbearable. Next we expose commands in our view model to efficiently manipulate the buffer, never actually exchanging the whole buffer with the view.
So we conclude that in order to achieve efficiency and responsiveness with pure MVVM, we need to expose an efficient view model. That means that all text boxes can be bound through to properties with no ill effects. But, it also means that you have to push state down into the view model to handle that. And that's OK because a view model is not the model; it's job is it to handle the needs of the view.
It's true that we can rapidly prototype user interfaces by taking advantage of shortcuts like binding on focus changes. But binding on focus changes can have negative consequences in real applications and if so then we should simply not use it.
What is the alternative? Expose a property friendly to frequent updates. Call it the same thing as the old inefficient property was called. Implement your fast property using the slow property with logic that depends on the state of the view model. The view model gets the save command. It knows whether the fast property has been pushed through to the slow property. It can decide if when and where the slow property will be synched to the model.
But you say, haven't we just moved the hack from the view to the view model? No, we have lost some elegance and simplicity, but go back to the text editor analogy. We have to solve the problem, and it is the view model's job to solve it.
If we want to use pure MVVM and we want efficiency and responsiveness, then lame heuristics like let's avoid updating the binding source until the element loses focus won't help. They introduce as many problems as they solve. In that case, we should let the view model do its job, even if means adding complexity.
Assuming we accept it, how can we manage the complexity? We can implement a generic wrapper utility class to buffer the slow property and allow the view model to hook its get and set methods. Our utility class can auto-register for save command events to reduce the amount of boilerplate code in our view model.
If we do it right, then all the parts of the view model that were fast enough to be used with property changed binding will all still be the same, and the others that were worthy of asking the question "Is this property too slow?" will have a small amount of code to address the issue, and the view is none the wiser.
This is a tricky one and I agree a non-hack and more-or-less code free solution should be found. Here are my thoughts:
The view is responsible because it sets IsDefault to true and allows for this 'problem'
The ViewModel should not be responsible in any way to fix this it might introduce dependencies from VM to V and thus breaking the pattern.
Without adding (C#) code to the View all you can do is either change bindings (e.g. to UpdateSourceTrigger=PropertyChanged) or add code to a base class of the Button. In the base class of the button you might be able to shift focus to the button before executing the command. Still hack-ish but cleaner than adding code to the VM.
So at the moment the only 'nice' solutions I see require the view developers to stick to a rule; set the binding in a specific way or use a special button.
I would add a Click event handler for the default button. The button's event handler is executed prior the command will be called, so the data bindings can be updated by changing the focus in the event handler.
private void Button_Click(object sender, RoutedEventArgs e) {
((Control)sender).Focus();
}
However, I don't know if similar approach can be used with other shorcut keys.
Yes, I have quite some experience. WPF and Silverlight still have their pain areas. MVVM doesn't solve it all; it is not a magic bullet and the support in the frameworks is getting better but still lacks. E.g., I still find editing deep child collections a problem.
At the moment I handle these situations case by case because a lot depends on the way the individual view have to work. This is what I spend most of my time on because I generate a lot of plumbing using T4 so I have time left for these quirks.
The problem is that the TextBox's text has a default source trigger of LostFocus instead of PropertyChanged. IMHO this was a wrong default choice since it is quite unexpected and can cause all sorts of problems (such as the ones you describe).
The simplest solution would be to always explicitly use UpdateSourceTrigger=PropertyChanged (as the others suggested).
If this isn't feasible (for whatever reason), I would handle the Unloaded, Closing or Closed events and manually update the binding (as shown by Rick).
Unfortunately it seems that certain scenarios are still a bit problematic with a TextBox, so some workarounds are necessary. For example, see my question. You might want to open a Connect bug (or two) with your specific problems.
EDIT:
Pressing Ctrl+S with focus on the TextBox, I would say the behavior is correct. After all, you are executing a command. This has nothing to do with the current (keyboard) focus. The command may even depend on the focused element! You are not clicking on a button or similar, which would cause the focus to change (however, depending on the button, it may fire the same command as before).
So if you want to only update the bound Text when you lose focus from the TextBox, but at the same time you want to fire a command with the newest contents of TextBox (i.e. the changes without it having lost focus), this does not match up. So either you have to change your binding to PropertyChanged, or manually update the binding.
EDIT #2:
As for your two cases why you cannot always use PropertyChanged:
What precisely are you doing with StringFormat? In all my UI work so far I use StringFormat to reformat data I am getting from the ViewModel. However, I am not sure how using StringFormat with data which is then again edited by the user should work. I am guessing you want to format the text for display, and then "unformat" the text the user enters for further processing in your ViewModel. From your description, it seems it isn't "unformatted" correctly all the time.
Open a Connect bug where it isn't working as it should.
Write your own ValueConverter which you use in the binding.
Have a separate property with the last "valid" value and use that value in your ViewModel; only update it once you get another "valid" value from the property you use in databinding.
If you have a long-running property setter (ex. a "validate" step), I would do the long-running part in a separate method (getters and setters should normally be relatively "fast"). Then run that method in a worker thread/threadpool/BackgroundWorker (make it abortable so you can restart it with a new value once the user enters more data) or similar.
What you think about proxy command and KeyBinding to ENTER key?
EDITED:
There we have one utility command (like converter), which requires knowledge about concrete view. This command can be reused for any dialog with same bug. And you add this functionality/hack only in view where this bug exists, and VM will be clear.
VM creates to adapt business to view and must provide some specific functionality like data conversion, UI commands, additional/helper fields, notifications and hacks/workarounds. And if we have leaks between levels in MVVM we have problems with: high connectivity, code reuse, unit testing for VM, pain code.
Usage in xaml (no IsDefault on Button):
<Window.Resources>
<model:ButtonProxyCommand x:Key="proxyCommand"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding Source={StaticResource proxyCommand}, Path=Instance}"
CommandParameter="{Binding ElementName=_okBtn}"/>
</Window.InputBindings>
<StackPanel>
<TextBox>
<TextBox.Text>
<Binding Path="Text"></Binding>
</TextBox.Text>
</TextBox>
<Button Name="_okBtn" Command="{Binding Command}">Ok</Button>
</StackPanel>
There used special proxy command, which receive element (CommandParameter) to move focus and execute. But this class requires ButtonBase for CommandParameter:
public class ButtonProxyCommand : ICommand
{
public bool CanExecute(object parameter)
{
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return false;
return btn.Command.CanExecute(btn.CommandParameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter == null)
return;
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return;
Action a = () => btn.Focus();
var op = Dispatcher.CurrentDispatcher.BeginInvoke(a);
op.Wait();
btn.Command.Execute(btn.CommandParameter);
}
private static ButtonProxyCommand _instance = null;
public static ButtonProxyCommand Instance
{
get
{
if (_instance == null)
_instance = new ButtonProxyCommand();
return _instance;
}
}
}
This is just idea, not complete solution.

Better Practice: CheckBox DataBindings vs CheckedChanged event

I have a CheckBox that, when checked/unchecked will toggle the Enabled property of some other controls. I did have my code looking something like this:
checkBox.CheckedChanged += new EventHandler((o, e) =>
{
control1.Enabled = checkBox.Checked;
control2.Enabled = checkBox.Checked;
});
But today I started playing with DataBindings and discovered I could do this:
control1.DataBindings.Add("Enabled", checkBox, "Checked");
control2.DataBindings.Add("Enabled", checkBox, "Checked");
They seem to behave the same, but I suspect one is preferred over the other. Or perhaps one has some unexpected behavior that may trip me up later.
Is one way better than the other?
The first one is checked at compiled time, so I'd go with that one. I assume that if the "Enabled" property in the second example was not valid you would get a runtime error.
You should notice that there is another difference:
with data binding (method 2), if the object implements INotifyPropertyChanged, and if the object.Enabled is changed outside the UI layer, the checkbox.checked state will get changed automatically.

Categories

Resources