I have a TextBox on WPF that I want to validate. I'm using Binding to validate it:
<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=Explicit}" TabIndex="0" LostFocus="TextBox_OnLostFocus">
</TextBox>
The LostFocus event:
private void TextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
((Control) sender).GetBindingExpression(TextBox.TextProperty);
}
Code behind the validation:
public string Name
{
get { return _name; }
set
{
_name = value;
this.OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
public string Error { get { return this[null]; } }
public string this[string columnName]
{
get
{
string result = string.Empty;
columnName = columnName ?? string.Empty;
if (columnName == string.Empty || columnName == "Name")
{
if (string.IsNullOrEmpty(this.Name))
{
result += Properties.Resources.ValidationName + Environment.NewLine;
}
}
return result.TrimEnd();
}
}
I have some questions:
1. When I first load my Window, my control is surrounded by a red square (the validation one), but I want it to appear only when I fire it (on the Explicit side).
2. How can I know if all my fields have been validated? I mean, when I press a button I only need to know how to know if all controls have been validated.
NOTE: I do have this context on the Constructor:
User u = new User();
DataContext = u;
Your first question may be answered here Did you try setting the binding mode to Default?
The Validation.HasError Attached Property will tell you if any binding on a particular UI Element has any binding validation errors. Use that on every control you need to have validated. Try that first. If you are using a pattern like MVVM, you could create properties on your VM to bind to the Validation.HasError properties.
Actually, my problem had something to do with the class I used to validate. The class does this:
public ErrorProvider()
{
this.DataContextChanged += new DependencyPropertyChangedEventHandler(ErrorProvider_DataContextChanged);
this.Loaded += new RoutedEventHandler(ErrorProvider_Loaded);
}
So whenever it first loads, it suscribes to the Load event and then it launches this:
private void ErrorProvider_Loaded(object sender, RoutedEventArgs e)
{
Validate();
}
so I commented it, and launched the Validate() method when needed....
Related
I find myself quite often in the following situation:
I have a user control which is bound to some data. Whenever the control is updated, the underlying data is updated. Whenever the underlying data is updated, the control is updated. So it's quite easy to get stuck in a never ending loop of updates (control updates data, data updates control, control updates data, etc.).
Usually I get around this by having a bool (e.g. updatedByUser) so I know whether a control has been updated programmatically or by the user, then I can decide whether or not to fire off the event to update the underlying data. This doesn't seem very neat.
Are there some best practices for dealing with such scenarios?
EDIT: I've added the following code example, but I think I have answered my own question...?
public partial class View : UserControl
{
private Model model = new Model();
public View()
{
InitializeComponent();
}
public event EventHandler<Model> DataUpdated;
public Model Model
{
get
{
return model;
}
set
{
if (value != null)
{
model = value;
UpdateTextBoxes();
}
}
}
private void UpdateTextBoxes()
{
if (InvokeRequired)
{
Invoke(new Action(() => UpdateTextBoxes()));
}
else
{
textBox1.Text = model.Text1;
textBox2.Text = model.Text2;
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
model.Text1 = ((TextBox)sender).Text;
OnModelUpdated();
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
model.Text2 = ((TextBox)sender).Text;
OnModelUpdated();
}
private void OnModelUpdated()
{
DataUpdated?.Invoke(this, model);
}
}
public class Model
{
public string Text1 { get; set; }
public string Text2 { get; set; }
}
public class Presenter
{
private Model model;
private View view;
public Presenter(Model model, View view)
{
this.model = model;
this.view = view;
view.DataUpdated += View_DataUpdated;
}
public Model Model
{
get
{
return model;
}
set
{
model = value;
view.Model = model;
}
}
private void View_DataUpdated(object sender, Model e)
{
//This is fine.
model = e;
//This causes the circular dependency.
Model = e;
}
}
One option would be to stop the update in case the data didn't change since the last time. For example if the data were in form of a class, you could check if the data is the same instance as the last time the event was triggered and if that is the case, stop the propagation.
This is what many MVVM frameworks do to prevent raising PropertyChanged event in case the property didn't actually change:
private string _someProperty = "";
public string SomeProperty
{
get
{
return _someProperty;
}
set
{
if ( _someProperty != value )
{
_someProperty = value;
RaisePropertyChanged();
}
}
}
You can implement this concept similarly for Windows Forms.
What you're looking for is called Data Binding. It allows you to connect two or more properties, so that when one property changes others will be updated auto-magically.
In WinForms it's a little bit ugly, but works like a charm in cases such as yours. First you need a class which represents your data and implements INotifyPropertyChanged to notify the controls when data changes.
public class ViewModel : INotifyPropertyChanged
{
private string _textFieldValue;
public string TextFieldValue {
get
{
return _textFieldValue;
}
set
{
_textFieldValue = value;
NotifyChanged();
}
}
public void NotifyChanged()
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(null));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Than in your Form/Control you bind the value of ViewModel.TextFieldValue to textBox.Text. This means whenever value of TextFieldValue changes the Text property will be updated and whenever Text property changes TextFieldValue will be updated. In other words the values of those two properties will be the same. That solves the circular loops issue you're encountering.
public partial class Form1 : Form
{
public ViewModel ViewModel = new ViewModel();
public Form1()
{
InitializeComponent();
// Connect: textBox1.Text <-> viewModel.TextFieldValue
textBox1.DataBindings.Add("Text", ViewModel , "TextFieldValue");
}
}
If you need to modify the values from outside of the Form/Control, simply set values of the ViewModel
form.ViewModel.TextFieldValue = "new value";
The control will be updated automatically.
You should look into MVP - it is the preferred design pattern for Winforms UI.
http://www.codeproject.com/Articles/14660/WinForms-Model-View-Presenter
using that design pattern gives you a more readable code in addition to allowing you to avoid circular events.
in order to actually avoid circular events, your view should only export a property which once it is set it would make sure the txtChanged_Event would not be called.
something like this:
public string UserName
{
get
{
return txtUserName.Text;
}
set
{
txtUserName.TextChanged -= txtUserName_TextChanged;
txtUserName.Text = value;
txtUserName.TextChanged += txtUserName_TextChanged;
}
}
or you can use a MZetko's answer with a private property
I'm trying to set up a ComboBox with its options binded from a list of strings, its default selected value binded from a setting, and with an event handler for its selection changed.
I want to configure it all using XAML like so:
<ComboBox Name="RoutesComboBox"
ItemsSource="{Binding Routes}"
SelectedItem="{Binding DefaultRoute}"
SelectionChanged="RouteFilter_SelectionChanged" />
But when I do that on startup it throws the error:
An unhandled exception of type
'System.Reflection.TargetInvocationException' occurred in
PresentationFramework.dll
If I only do some of it in XAML, then either set the SelectionChanged event or the ItemsSource programatically in C# like below it works fine. But I have a lot of these ComboBoxes so I would rather do it straight in the XAML.
<ComboBox Name="RoutesComboBox"
ItemsSource="{Binding Routes}"
SelectedItem="{Binding DefaultRoute}" />
With this C#:
public IEnumerable<string> Routes
{
get { return LubricationDatabase.GetRoutes(); }
}
public string DefaultRoute
{
get { return MySettings.Default.DefaultRoute; }
set { } /* side question: without this, it throws a parse exception. Any idea why? */
}
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
RoutesComboBox.SelectionChanged += RouteFilter_SelectionChanged;
}
I've also tried the solution found here:
private string _defaultRoute;
public string DefaultRoute
{
get { return MySettings.Default.DefaultRoute; }
set
{
if (_defaultRoute != value)
{
_defaultRoute = value;
// this fires before `SelectedValue` has been
// updated, and the handler function uses that,
// so I manually set it here.
RoutesComboBox.SelectedValue = value;
SelectionChangedHandler();
}
}
}
Which is okay, but is pretty bulky and probably more work than is worth it when I can just programatically assign the SelectionChanged event.
Again if possible I'd like to do it all using XAML because I have a lot of these ComboBoxes and initializing them all like this in the C# will look awful.
Any ideas?
Why are you binding with SelectedItem when you're not going to update the item when a user changes their selection? Not sure what your event handler is doing, but I have a working solution just the way you wanted it.
In short, you need to keep track of the DefaultRoute using a backing field. Also, you need to notify the UI when the selected item changes in your view model; which by the way is something you don't seem to be doing, MVVM. You should only be hooking into the selection changed event if you plan on updating the view in some way. All other changes should be handled in your view models DefaultRoute setter
XAML
<ComboBox Name="RoutesComboBox"
ItemsSource="{Binding Routes}"
SelectedItem="{Binding DefaultRoute}"
SelectionChanged="RouteFilter_SelectionChanged" />
Code
public partial class MainWindow : Window, INotifyPropertyChanged
{
public IEnumerable<string> Routes
{
get
{
return new string[] { "a", "b", "c", "d" };
}
}
public string DefaultRoute
{
get
{
return _defaultRoute;
}
set
{
_defaultRoute = value;
// Handle saving/storing setting here, when selection has changed
//MySettings.Default.DefaultRoute = value;
NotifyPropertyChanged();
}
}
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
DefaultRoute = MySettings.Default.DefaultRoute;
}
private string _defaultRoute;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void RouteFilter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
public static class MySettings
{
public static class Default
{
public static string DefaultRoute = "a";
}
}
I have a OneWayToSource binding that is not behaving as I expected when I set the DataContext of the target control. The property of the source is being set to default instead of the value of the target control's property.
I've created a very simple program in a standard WPF window that illustrates my problem:
XAML
<StackPanel>
<TextBox x:Name="tb"
Text="{Binding Path=Text,Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"
TextChanged="TextBox_TextChanged"/>
<Button Content="Set DataContext" Click="Button1_Click"/>
</StackPanel>
MainWindow.cs
public partial class MainWindow : Window
{
private ViewModel _vm = new ViewModel();
private void Button1_Click(object sender, RoutedEventArgs e)
{
Debug.Print("'Set DataContext' button clicked");
tb.DataContext = _vm;
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
Debug.Print("TextBox changed to " + tb.Text);
}
}
ViewModel.cs
public class ViewModel
{
private string _Text;
public string Text
{
get { return _Text; }
set
{
Debug.Print(
"ViewModel.Text (old value=" + (_Text ?? "<null>") +
", new value=" + (value ?? "<null>") + ")");
_Text = value;
}
}
}
The TextBox tb starts out with a null DataContext and therefore the binding is not expected to do anything. So if I type something in the text box, say "X", the ViewModel.Text property remains null.
If I then click the Set DataContext button I would have expected the ViewModel.Text property to be set to the "X" of the TextBox.Text property. Instead it is set to "". Certainly the binding is working because if I then type "Y" in the text box, after the "X", it sets the ViewModel.Text property to "XY".
Here is an example of the output (the last two lines are counter-intuitive because of the order of evaluation, but they definitely both occur immediately after typing the "Y"):
TextBox changed to X
'Set DataContext' button clicked
ViewModel.Text (old value=<null>, new value=)
ViewModel.Text (old value=, new value=XY)
TextBox changed to XY
Why is the ViewModel.Text property being set to "" instead of "X" when the DataContext is set?
What am I doing wrong? Am I missing something? Have I misunderstood something about binding?
Edit: I would have expected the output to be:
TextBox changed to X
'Set DataContext' button clicked
ViewModel.Text (old value=<null>, new value=X)
ViewModel.Text (old value=X, new value=XY)
TextBox changed to XY
Its a bug or perhabs not. Microsoft claims its by design. You first type x and then you kill DataContext by clicking on Button hence why the TextBox holds x and your viewModel.Text property gets newly initialized (its empty). When on datacontext changed getter will still be called. In the end you have no chance to fix this.
You can however use two way and let it be.
Here you will have to UpdateSource like below:
private void Button1_Click(object sender, RoutedEventArgs e)
{
Debug.Print("'Set DataContext' button clicked");
tb.DataContext = _vm;
var bindingExp = tb.GetBindingExpression(TextBox.TextProperty);
bingExp.UpdateSource();
}
TextBox has a Binding in it's TextProperty and when you set TextBox's DataContext, TextBox will update it's source (viewmodel.Text) , no matter which type of the UpdateSourceTrigger.
It's said that the first output in viewmodel
"ViewModel.Text (old value=<null>, new value=)"
is not triggered by UpdateSourceTrigger=PropertyChanged.
It's just a process of init:
private string _Text;
public string Text
{
get { return _Text; }
set
{
Debug.Print(
"ViewModel.Text (old value=" + (_Text ?? "<null>") +
", new value=" + (value ?? "<null>") + ")");
_Text = value;
}
}
Because it's not triggered by UpdateSourceTrigger=PropertyChanged, the viewmodel will not know the value of TextBox.Text.
When you type "Y",the trigger of PropertyChanged will working,so the viewmodel read text of TextBox.
There is a bug in .NET 4 with one way to source bindings that it calls getter for OneWayToSource bindings thats why you are having this problem.You can verify it by putting breakpoint on tb.DataContext = _vm; you will find setter is called and just after that getter is called on Text property.You can resolve your problem by manually feeding the viewmodel values from view before assigning the datacontext..NET 4.5 resolves this issue.
see here and here too
private void Button1_Click(object sender, RoutedEventArgs e)
{
Debug.Print("'Set DataContext' button clicked");
_vm.Text=tb.Text;
tb.DataContext = _vm;
}
You need Attached property:
public static readonly DependencyProperty OneWaySourceRaiseProperty = DependencyProperty.RegisterAttached("OneWaySourceRaise", typeof(object), typeof(FrameworkElementExtended), new FrameworkPropertyMetadata(OneWaySourceRaiseChanged));
public static object GetOneWaySourceRaise(DependencyObject o)
{
return o.GetValue(OneWaySourceRaiseProperty);
}
public static void SetOneWaySourceRaise(DependencyObject o, object value)
{
o.SetValue(OneWaySourceRaiseProperty, value);
}
private static void OneWaySourceRaiseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
return;
var target = (FrameworkElement)d;
target.Dispatcher.InvokeAsync(() =>
{
var bindings = target.GetBindings().Where(i => i.ParentBinding?.Mode == BindingMode.OneWayToSource).ToArray();
foreach (var i in bindings)
{
i.DataItem.SetProperty(i.ParentBinding.Path.Path, d.GetValue(i.TargetProperty));
}
});
And set binding in XAML:
extendends:FrameworkElementExtended.OneWaySourceRaise="{Binding}"
where {Binding} - is binding to DataContext.
You need:
public static IEnumerable<BindingExpression> GetBindings<T>(this T element, Func<DependencyProperty, bool> func = null) where T : DependencyObject
{
var properties = element.GetType().GetDependencyProperties();
foreach (var i in properties)
{
var binding = BindingOperations.GetBindingExpression(element, i);
if (binding == null)
continue;
yield return binding;
}
}
private static readonly ConcurrentDictionary<Type, DependencyProperty[]> DependencyProperties = new ConcurrentDictionary<Type, DependencyProperty[]>();
public static DependencyProperty[] GetDependencyProperties(this Type type)
{
return DependencyProperties.GetOrAdd(type, t =>
{
var properties = GetDependencyProperties(TypeDescriptor.GetProperties(type, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.All) }));
return properties.ToArray();
});
}
private static IEnumerable<DependencyProperty> GetDependencyProperties(PropertyDescriptorCollection collection)
{
if (collection == null)
yield break;
foreach (PropertyDescriptor i in collection)
{
var dpd = DependencyPropertyDescriptor.FromProperty(i);
if (dpd == null)
continue;
yield return dpd.DependencyProperty;
}
}
I have a Windows Forms ListBox data-bound to a BindingList of business objects. The ListBox's displayed property is a string representing the name of the business object. I have a TextBox that is not data-bound to the name property but instead is populated when the ListBox's selected index changes, and the TextBox, upon validation, sets the business object's name property and then uses BindingList.ResetItem to notify the BindingList's bound control (the ListBox) to update itself when the TextBox's text value is changed by the user.
This works great unless the name change is only a change in case (i.e. "name" to "Name"), in which case the ListBox doesn't get updated (it still says "name", even though the value of the underlying business object's name property is "Name").
Can anyone explain why this is happening and what I should do instead? My current workaround is to use BindingList.ResetBindings, which could work for me but may not be acceptable for larger datasets.
Update 9/27/2011: Added a simple code example that reproduces the issue for me. This is using INotifyPropertyChanged and binding the textbox to the binding list. Based on How do I make a ListBox refresh its item text?
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WinformsDataBindingListBoxTextBoxTest
{
public partial class Form1 : Form
{
private BindingList<Employee> _employees;
private ListBox lstEmployees;
private TextBox txtId;
private TextBox txtName;
private Button btnRemove;
public Form1()
{
InitializeComponent();
FlowLayoutPanel layout = new FlowLayoutPanel();
layout.Dock = DockStyle.Fill;
Controls.Add(layout);
lstEmployees = new ListBox();
layout.Controls.Add(lstEmployees);
txtId = new TextBox();
layout.Controls.Add(txtId);
txtName = new TextBox();
layout.Controls.Add(txtName);
btnRemove = new Button();
btnRemove.Click += btnRemove_Click;
btnRemove.Text = "Remove";
layout.Controls.Add(btnRemove);
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(object sender, EventArgs e)
{
_employees = new BindingList<Employee>();
for (int i = 0; i < 10; i++)
{
_employees.Add(new Employee() { Id = i, Name = "Employee " + i.ToString() });
}
lstEmployees.DisplayMember = "Name";
lstEmployees.DataSource = _employees;
txtId.DataBindings.Add("Text", _employees, "Id");
txtName.DataBindings.Add("Text", _employees, "Name");
}
private void btnRemove_Click(object sender, EventArgs e)
{
Employee selectedEmployee = (Employee)lstEmployees.SelectedItem;
if (selectedEmployee != null)
{
_employees.Remove(selectedEmployee);
}
}
}
public class Employee : INotifyPropertyChanged
{
private string name;
private int id;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
public int Id
{
get { return id; }
set
{
id = value;
OnPropertyChanged("Id");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
}
Update 9/28/2011: The problem seems to be internal to the ListBox control, specifically the way it decides (not) to update an item if its string representation is equivalent to the new value, ignoring case differences. As far as I can tell this is hard coded into the control with no way to override it.
Think I found the problem:
I just hope this concept will be helpfull to solve your problem
I have a TextBox, the user input is compared to string "Name". My button click event is:
private void btn_Click(object sender, EventArgs e)
{
if(txt.Text == "Name")
MessageBox.Show("Value is Same");
}
If you write "name" in textbox, condition will be false. If you type type "Name" in textbox, condition will be true.
Now try changing the btn click:
using System.Globalization;
private void btn_Click(object sender, EventArgs e)
{
TextInfo ChangeCase = new CultureInfo("en-US", false).TextInfo;
string newText = ChangeCase.ToTitleCase(txt.Text);
if (newText == "Name")
MessageBox.Show("Value is Same");
}
now you type "name" or "Name" condition is true.
Remember it will just capaitalize the first letter of the string suplied. "my name" will outputted as "My Name".
And if your condition says:
if(txt.Text == "name")
MessageBox.Show(Value is Same);
Then you can try something like
string newText = (txt.Text).ToLower();
if(newText == "name")
MessageBox.Show(Value is Same);
Here the supplied string will outputted in the lower case always.
Hope it helps.
This really is the same problem as when renaming files or directories while only case is different. I suggest the same work-around that I found earlier:
if (oldValue.ToUpper() == newValue.ToUpper()){
ListBox1.Items[i] = newValue + "_tmp"; // only adding stuff to force an update
ListBox1.Items[i] = newValue; // now the new value is displayed, even only case has changed
}
Now for your question, I suggest you try to check if the setter is changing a value only in lower/upper case (a.ToUpper() == b.ToUpper()). If true, then first give a extra change, before the intended change, something like:
name = value + "_tmp";
OnPropertyChanged("Name");
name = value;
OnPropertyChanged("Name");
Hope this helps.
In my usercontrol.xaml.cs. I have this dependency proprerty as bleow.
public static readonly DependencyProperty MessageKeyProperty =
DependencyProperty.Register("MessageKey", typeof(String),
typeof(UC_MessageEntry),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(MessageKeyPropertyChangedCallback)));
private static void MessageKeyPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UC_MessageEntry)
{
UC_MessageEntryucMessageEntryAccessDenied = (UC_MessageEntry)d;
if (e.NewValue != null)
{
ResourceBundle resourceBundle = App.ResourceBundle;
if (e.NewValue.ToString().Equals("enable"))
{
ucMessageEntryAccessDenied.txtAceessDeniedMsg.Text = "";
return;
}
String actualMessage = resourceBundle.GetString("Resources", e.NewValue.ToString());
if (actualMessage == null)
{
ucMessageEntryAccessDenied.txtAceessDeniedMsg.Text = resourceBundle.GetString("Resources", "ContractSetup.ExchangeAccessDeniedMessage.OTHERS");
}
else
{
ucMessageEntryAccessDenied.txtAceessDeniedMsg.Text = actualMessage;
}
}
else
{
ucMessageEntryAccessDenied.txtAceessDeniedMsg = null;
}
}
}
public String MessageKey
{
get
{
return (String)this.GetValue(MessageKeyProperty);
}
set
{
this.SetValue(MessageKeyProperty,value);
}
}
In mainwindow.xaml , i bind this MessageKey as below.
<view_MessageEntry:UC_MessageEntry
x:Uid="local:UC_MessageEntry_1" x:Name="UC_OrderEntry" MessageKey="{Binding MsgAccessDenied}"
Style="{DynamicResource contentControlStyle}" SnapsToDevicePixels="True" Margin="0" />
And Behind MessageViewModle.cs,
private static readonly PropertyChangedEventArgs MsgAccessDeniedPropertyChangedEventArgs
= new PropertyChangedEventArgs("MsgAccessDenied");
private string _msgAccessDenied;
public string MsgAccessDenied
{
get
{
if (_selectedExchange != null)
{
return _msgAccessDenied;
}
else
{
return "enable";
}
}
set
{
_msgAccessDenied = value;
this.RaisePropertyChanged(this, MsgAccessDeniedPropertyChangedEventArgs);
}
}
public void FireMsg()
{
this.MsgAccessDenied="value";
}
When the combo box selection is changed, I called FireMsg() and it will update the MessageKeyPropertyChangedCallback function in usercontrol.xaml.cs. It's working fine. But If I call this FireMsg() from Other ViewModels, value of _msgAccessDenied is updated. But MessageKeyPropertyChangedCallback function is not firing. Any solution for this issue? Thanks.
Your code looks generally sound. But if your dependency property change callback is not being called, it is almost certainly because the value of the dependency property isn't changing. And if the dependency property is bound to a source, then it is probably because the source isn't changing.
From my quick review of the code, the only way that I can see that happening is:
the value of MsgAccessDenied is set
this causes the field _msgAccessDenied to be set
the RaisePropertyChanged method is called to trigger PropertyChanged
the dependency subsystem does its thing which forces a get of MsgAccessDenied
the MsgAccessDenied getter is called
the getter checks _selectedExchange and it is null
the getter returns the value "enable" instead of the new value of _msgAccessDenied
the previous value was also "enable"
the dependency subsystem says, OK, no change
the property change callback is not called
In summary, _selectedExchange can hide value changes in _msgAccessDenied, thereby preventing the upstream change callback from firing.
This is just a theory.