Reputation: 595
I have a ViewModel that is a DependencyObject
for which the DependencyProperty
s are not updating the View with the new values.
A sample property (the get/set wrapper is called as expected)
public static readonly DependencyProperty WeaponNameProperty = DependencyProperty.Register(
"WeaponName",
typeof(string),
typeof(WeaponSystemVM),
new PropertyMetadata(null, new PropertyChangedCallback(OnWeaponNameChanged)));
public string WeaponName
{
get { return (string)GetValue(WeaponNameProperty); }
set { SetValue(WeaponNameProperty, value); }
}
The Callback
(called when WeaponName
is changed)
private static void OnWeaponNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WeaponSystemVM vm = d as WeaponSystemVM;
if (vm != null)
{ vm.CommandAddWeaponSystem.RaiseCanExecuteChanged(); }
}
The CanExecute Delegate
(gets run as expected and updates the relevant Button
)
private bool CanAddSystem()
{
if (string.IsNullOrWhiteSpace(WeaponName)) return false;
if (string.IsNullOrWhiteSpace(WeaponLock)) return false;
if (string.IsNullOrWhiteSpace(WeaponDamage)) return false;
if (string.IsNullOrWhiteSpace(WeaponAttack)) return false;
return true;
}
The input TextBox
<TextBox x:Name="NameInput" Text="{Binding WeaponName, Mode=TwoWay}" Margin="12,4" RelativePanel.Below="NameAdorner" RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"/>
The output TextBlock
(is NOT updated with the new value and the DataContext
is the same as the input TextBox
)
<TextBlock Text="{Binding WeaponName}"/>
Frustratingly, it seems to be just this implementation that isn't working.
In an attempt to reproduce the issue, I created a seperate project without all the extra info associated with my app, and the View is being updated exactly as expected.
What I don't understand is what is not being done correctly in this implementation. The ViewModel is updating exactly as expected. The Bindings are valid according to the LiveVisualTree
.
Can anyone point me to the issue?
Upvotes: 0
Views: 98
Reputation: 2108
You shouldn't use DependencyProperty
s in your ViewModel: it is a markup class, used for binding on the View side. Overkill and out of scope for it being used that way.
You should implement INotifyPropertyChanged
, and fire the INotifyPropertyChanged.PropertyChanged
event in every single property you want to notify the UI about.
Something like:
your ViewModel inherits from
public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void SetAndRaiseIfChanged<T>(
ref T backingField,
T newValue,
[CallerMemberName] string propertyName = null)
{
if (!object.Equals(backingField, newValue))
return;
backingField = newValue;
this.RaisePropertyChanged(propertyName);
}
}
and in your ViewModel you define your property like
private string _weaponName;
public string WeaponName
{
get { return this._weaponName; }
set { SetAndRaiseIfChanged(ref this._weaponName, value); }
}
a more concise CanAddSystem
private bool CanAddSystem()
{
return
!string.IsNullOrWhiteSpace(WeaponName)
&& !string.IsNullOrWhiteSpace(WeaponLock)
&& !string.IsNullOrWhiteSpace(WeaponDamage)
&& !string.IsNullOrWhiteSpace(WeaponAttack);
}
build your ViewModel's command with something that implements ICommand
interface (something like a RelayCommand)
the View piece would be
<TextBlock Text="{Binding WeaponName}"/>
and you're done: when you bind an ICommand to the UI, the system automatically updates the CanExecute reading it from the ViewModel.
Upvotes: 2