Reputation: 55
Searching didn't bring me any clues and I am sort of at a loss. WPF is self taught so far, so I might be overlooking something simple.
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<TextBlock Text={Binding BoundTextProperty}"/>
that's the simplified xml
public class MainViewModel
{
private Model Data;
public MainViewModel()
{...}
public string BoundTextProperty => Data.BoundTextProperty;
...
}
The Property that's bound referencing the Property holding the Data in the model
public class Model : INotifyPropertyChanged
{
private long number;
public long Number
{
get { return number; }
set
{
number = value;
OnPropertyChanged(nameof(BoundTextProperty));
}
}
public string BoundTextProperty => $"Some text {Number} some text again";
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I swear it worked at some point. The string has a couple other variables, but that's the basic of how it works or rather doesn't.
My Question is wether or not the Binding can actually bubble up, and if it can, why doesn't it?
Upvotes: 0
Views: 324
Reputation: 169200
You need to raise the PropertyChanged
event for the source property that you bind to in your XAML. In this case you bind to the BoundTextProperty
of the MainViewModel
which means that the MainViewModel
class should raise the PropertyChanged
event.
It doesn't matter whether the source property wraps another property of a class that does raise the PropertyChanged
event. It's the source object of the binding that notifies the view.
You could also just bind to the "model" property directly, provided that you turn Data
into a public property in your view model:
public Model Data { get; private set; }
...
<TextBlock Text="{Binding Data.BoundTextProperty}"/>
If you choose to stick with your wrapper property, the MainViewModel
must implement the INotifyPropertyChanged
event and raise the PropertyChanged
event whenever the model is updated:
public class MainViewModel : INotifyPropertyChanged
{
private readonly Model Data;
public MainViewModel()
{
Data = new Model();
Data.PropertyChanged += Data_PropertyChanged;
}
private void Data_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("BoundTextProperty");
}
public string BoundTextProperty => Data.BoundTextProperty;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Upvotes: 1
Reputation: 1107
You have to add the code for bubbling up the Model's PropertyChanged event from the ViewModel to the View. Here is an example (based on your code):
public class MainViewModel : ViewModelBase
{
private readonly Model Data;
public MainViewModel()
{
Data = new Model();
Data.PropertyChanged += ModelOnPropertyChanged;
}
private void ModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(Model.BoundTextProperty):
OnPropertyChanged(nameof(MainViewModel.BoundTextProperty));
break;
// add cases for other properties here:
}
}
public string BoundTextProperty => Data.BoundTextProperty;
}
public class Model : ModelBase
{
private long number;
public long Number
{
get { return number; }
set
{
number = value;
OnPropertyChanged(nameof(BoundTextProperty));
}
}
public string BoundTextProperty => $"Some text {Number} some text again";
}
public abstract class ViewModelBase : Base
{
// add other ViewModel related stuff here
}
public abstract class ModelBase : Base
{
// add other Model related stuff here
}
public abstract class Base : INotifyPropertyChanged
{
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Upvotes: 1