In my view model:
public string MyProperty{ get; set; }
public MyViewModel()
{
MyProperty = "hello";
}
I have defined a string property.
Now, from my page, I want to bind to this property:
Text="{Binding MyProperty}"
but this is not working - no text is being show. What am I missing?
Edit:
My view model inherits from:
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Edit 2:
I have modified my view model:
private string _myProperty;
public string MyProperty
{
get => _myProperty;
set => Set(ref _myProperty, value);
}
public MyViewModel()
{
_myProperty = "hello";
}
and the xaml:
Text="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
but it's still not working.
Edit 3: I think the problem is that the Text property is a registered dependency property of a custom control:
public sealed partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
DataContext = this;
}
public string Text
{
get => (string)GetValue(s_textProperty);
set => SetValue(s_textProperty, value);
}
public static readonly DependencyProperty s_textProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyControl), new PropertyMetadata(null));
}
and in the control's xaml I have:
<TextBlock Text="{Binding Text}" />
This:
<MyControl Text="{Binding MyProperty}"/>
is in the page where I use the custom control.
Your class should implement INotifyPropertyChanged and haveproperty accessors like this:
public event PropertyChangedEventHandler PropertyChanged;
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
In XAML:
Text="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Remarks:
Mode = TwoWay - property will change both on UI and in code if changed by either one.
UpdateSourceTrigger - Reacts on the PropertyChanged event.
Also, read about DataContext :)
I recomment adding the PropertyChanged.Fody Nuget (https://www.nuget.org/packages/PropertyChanged.Fody/)
Its simple to implement it.
[AddINotifyPropertyChangedInterface]
public class MyViewModel
{
public string MyProperty { get; set; }
public MyViewModel()
{
MyProperty = "hello";
}
}
As #DavidHruška said, edit the binding in XAML too.
Your setter needs to explicitly call the Observable.Set() Method:
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set { this.Set<string>(ref _myProperty, value); }
}
Unfortunately, autos don't implement INPC for you, so you can't use them. Microsoft had this as a feature request, but it appears to be getting turned down.
Related
I have below WPF TextBox:
<TextBox Grid.Column="0"
Grid.Row="1"
Text="{Binding myPath, UpdateSourceTrigger=PropertyChanged}"
Margin="5,8,8,5" />
And its property in the view model is:
public string myPath
{
get => myObject.path;
set
{
// Do some comprobations before assigning the new value
if (comprobationsOk(value))
{
Uri myUri = new Uri(value);
myObject.path = myUri.LocalPath;
this.OnPropertyChanged();
}
}
}
And this is my OnPropertyChanged method in my ViewModelBase class which implements INotifyPropertyChanged:
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
As you can see, the actual view model value is transformed in the setter.
When the user starts typing in the TextBox ///my/absolute/path the property in the view model gets //my/absolute/path.
So I expect the TextBox to be updated with //my/absolute/path, but it is not and remains as ///my/absolute/path
Actually it works pretty well with a working view model.
View:
<TextBox Text="{Binding Path, UpdateSourceTrigger=PropertyChanged}" />
View model:
public class ViewModel : NotifyPropertyChangedBase
{
private string _name = string.Empty;
public string Path
{
get => _name;
set
{
// To avoid constant System.UriFormatException, we use the try method
var newValue = Uri.TryCreate(value, UriKind.Absolute, out var result) ? result.LocalPath : value;
Update(ref _name, newValue);
}
}
}
NotifyPropertyChangedBase :
public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void Update<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
{
if (Equals(field, newValue))
return;
field = newValue;
OnPropertyChanged(propertyName);
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have a ComboBox in my View:
<ComboBox Name="comboBox1" ItemsSource="{Binding MandantList}" SelectedItem="{Binding CurrentMandant, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Firma}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Here is my Model:
public class MandantListItem : INotifyPropertyChanged
{
public MandantListItem() { }
string _Firma;
bool _IsChecked;
public string Firma
{
get { return _Firma; }
set { _Firma = value; }
}
public bool IsChecked
{
get
{
return _IsChecked;
}
set
{
_IsChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And here is my ViewModel:
public class MaViewModel : INotifyPropertyChanged
{
public ObservableCollection<MandantListItem> MandantList { get { return _MandantList; } }
public ObservableCollection<MandantListItem> _MandantList = new ObservableCollection<MandantListItem>();
private MandantListItem _CurrentMandant;
public MandantListItem CurrentMandant
{
get { return _CurrentMandant; }
set
{
if (value != _CurrentMandant)
{
_CurrentMandant = value;
OnPropertyChanged("CurrentMandant");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
How to fill the ComboBox:
public zTiredV2.ViewModel.MaViewModel MAList = new zTiredV2.ViewModel.MaViewModel();
this.comboBox1.ItemsSource = MAList.MandantList;
MAList.MandantList.Add(new zTiredV2.Model.MandantListItem { Firma = "A", Homepage = "a.com", IsChecked = false });
MAList.MandantList.Add(new zTiredV2.Model.MandantListItem { Firma = "B", Homepage = "b.com", IsChecked = false });
But my item doesnt update ... tried also via IsChecked, but no success either ... when i iterate through MAList, IsChecked is always false. And how can i bind a TextBlock to the selected Firma?
Have a hard time with MVVM, but i like it.
You should set the DataContext of the ComboBox to an instance of your view model. Otherwise the bindings won't work:
this.comboBox1.DataContext = MAList;
Also note that the _MandantList backing field for your property shouldn't be public. In fact, you don't need it at all:
public ObservableCollection<MandantListItem> MandantList { get; } = new ObservableCollection<MandantListItem>();
Setting the DataContext should cause the CurrentMandant property to get set when you select an item in the ComboBox. It won't set the IsChecked property though.
I recently started learning Xamarin and I stumbled across the following problem. I have a single label in my XAML file which is bound to a ViewModel property. I am using the ICommand interface to bind a tap gesture to a method in my ViewModel which is supposed to update the label's text. However, it is not updating the "Please touch me once!". I am just wondering what I am doing wrong here?
MainPage xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App1"
x:Class="App1.MainPage">
<Label Text="{Binding MessageContent, Mode=TwoWay}"
VerticalOptions="Center"
HorizontalOptions="Center">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnLabelTouchedCmd}" />
</Label.GestureRecognizers>
</Label>
</ContentPage>
Code-behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
}
ViewModel:
class MainPageViewModel : INotifyPropertyChanged
{
private string _messageContent;
public MainPageViewModel()
{
MessageContent = "Please touch me once!";
OnLabelTouchedCmd = new Command(() => { MessageContent = "Hey, stop toutching me!"; });
}
public ICommand OnLabelTouchedCmd { get; private set; }
public string MessageContent
{
get => _messageContent;
set
{
_messageContent = value;
OnPropertyChanged(value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You're calling OnPropertyChanged with a wrong argument as seen here:
protected virtual Void OnPropertyChanged ([System.Runtime.CompilerServices.CallerMemberName] String propertyName)
It expects the name of the property instead of the value you're passing now. Try this instead:
public string MessageContent
{
get => _messageContent;
set
{
_messageContent = value;
OnPropertyChanged("MessageContent");
}
}
Explaination
The current code isn't working because it is passing the value of the property into OnPropertyChanged.
Instead, we need to pass the name of the property as a string into OnPropertyChanged.
Answer
We can take advantage of the CallerMemberName attribute to make the code more concise and to avoid hard-coding strings when calling OnPropertyChanged.
Adding [CallerMemberName] to the parameter of OnPropertyChanged allows you to call OnPropertyChanged() from the setter of the property, and the property name is automatically passed into the argument.
Updated Method
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Updated ViewModel
class MainPageViewModel : INotifyPropertyChanged
{
private string _messageContent;
...
public string MessageContent
{
get => _messageContent;
set
{
_messageContent = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also look at the ViewModelBase located here, have all your ViewModels inherit from it. You can call just OnPropertyChanged, in either of the two ways below. The first of which will just take the name of the calling member, in this case your public property.
OnPropertyChanged();
OnPropertyChanged("MyProperty");
Edit- this is in extension to Brandon's correct answer
I'm having a great trouble with understanding how button command works. I have something like this
{Binding TxtBox} gets value from model, let's say it's "aaa". I would like click the button and the value should appear in the second textbox (the one with {Binding TxtBox2}).
This is my xaml:
<TextBox Text="{Binding TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>
This is my ViewModel:
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
Do I really need this CommandHandler class? I copied the code from the net.
public string TxtBox
{
get { return Model.TxtBoxValue; }
set { Model.TxtBoxValue = value; }
}
public string TxtBox2 { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
The second textbox's binding never gets notified that it's bound property is changed. When you set this.TxtBox2 you should fire the propertychanged event for that property so the binding will be updated.
See think link for everything on bindings
I don't know if you are using prism as mvvm framework but that comes with the DelegateCommand class. I don't think there is a simple/lightweight implementation in the .net framework. See this link for the mvvm framework and the delegate command
The View reacts to binding changes through PropertyChanged events, of which you have none. Have anything that binds to the View implement INotifyPropertyChanged and then fire events when props change, and you're all set for your bindings to work (one way or two way).
Change your model to look like this and it should work for you.
public class MyViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged = null;
protected virtual void RaisePropertyChanged(string propName)
{
if(PropertyChanged != null)
{
Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
}
}
#endregion
public string TxtBox
{
get { return Model.TxtBoxValue; }
set
{
Model.TxtBoxValue = value;
RaisePropertyChanged("TxtBox");
}
}
// presuming TxtBox2Value is in Model...else use a field
public string TxtBox2
{
get { return Model.TxtBox2Value; }
set
{
Model.TxtBox2Value = value;
RaisePropertyChanged("TxtBox2");
}
}
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
}
IMO - it is better to have your Model implement INotifyPropertyChanged and then bind directly to it rather than wrap it in your ViewModel. If Model : INotifyPropertyChanged, then your ViewModel now looks like this:
public class MyViewModel
{
// fire prop changed event here if this model will be swapped out after the ctor...otherwise don't worry about it
public Model Model { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute));
}
}
private bool _canExecute = true;
public void MyAction()
{
Model = new Model();
Model.TxtBox2 = "Some new value";
}
}
...and your xaml changes to this:
<TextBox Text="{Binding Model.TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding Model.TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>
I've got a list of checkboxes bound like this.
<ListBox ItemsSource="{Binding AllThings}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}"
IsChecked="{Binding Active,Mode=TwoWay}"
Checked="ToggleButton_OnChecked"
Unchecked="ToggleButton_OnUnchecked"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The binding works one-way because I can see that the boxes are checked/unchecked based on my settings from the beginning. I was expecting checking a box to update the underlying viewmodel but it doesn't happen. The breakpoint set on the OnPropertyChanged doesn't get hit. I suspect that it's got to do with the fact that I'm changing a property inside the observed property but due to ignorance I'm not sure.
class Presenter : INotifyPropertyChanged
{
private IEnumerable<Something> _allThings;
public IEnumerable<Something> AllThings
{
get { return _allThings; }
set
{
_allThings = value;
OnPropertyChanged("AllThings");
}
}
public Presenter()
{
_allThings = DataAccessor.GetThings();
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(
[CallerMemberName] String propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
What can I be missing here?
As far I can see I'm doing precisely as this guy suggests. Obviously I'm missing something but it's beyond me what...
edit
As per request from #Clemens, I also implemented the interface in the Soomething class.
public class Something :INotifyPropertyChanged
{
public int Id { get; set; }
public String Name { get; set; }
public bool Active { get; set; }
public override String ToString()
{
return Name;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(
[CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class Something must implement the INotifyPropertyChanged interface. This means that besides writing
public class Something : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
it also has to actually raise the PropertyChanged event when property values change, e.g.:
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}