I have a class as follows:
public class UserData : INotifyPropertyChnaged
{
public string strUserName;
public string UserName
{
get { return strUserName; }
set { SetProperty(ref strUserName, value); }
}
private string strPhoneNumber;
public string UserPhoneNumber
{
get { return strPhoneNumber; }
set { SetProperty(ref strPhoneNumber, value); }
}
private List<UserMailID> listUserMailID;
public List<UserMailID> ListOfUserMailID
{
get { return listUserMailID; }
set { SetProperty(ref listUserMailID, value); }
}
}
I'm accessing this class at some location and this class object I am binding to my view:
private UserData cActiverUser;
public UserData ActiverUser
{
get { return cActiverUser; }
set { SetProperty(ref cActiverUser , value); }
}
Suppose there is UserName field in my view; my data binding goes like this:
TextBox.Text="{Binding ActiverUser.StrUserName, UpdateSourceTrigger=PropertyChanged}
Binding works well, but I am unable to raise the property changed event when I change this user name field on my view. I have tried setting Mode="TwoWay" also.
In your XAML binding, try setting NotifyOnTargetUpdated=True, or if that doesn't work NotifyOnSourceUpdated=True.
I had the same issue with a project I did a while ago, and it was one of those that got it working.
Also on this line:
public string strUserName;
Should this be private? and you should be specifying the binding as UserName, not strUserName?
I believe the bindings are also case sensitive.
If you're using Prism, derive UserData and whatever class has UserData as its property from BindableBase, which implements INotifyPropertyChanged, but also lets you use SetProperty(...). I'm not even sure how you were able to get it to work before, but I suspect you're not showing us your original code, since you misspelled INotifyPropertyChanged -- INotifyPropertyChnaged.
Additionally, are you actually instantiating the object? Don't forget to do that.
Lastly, you're binding to ActiverUser.StrUserName, but UserData does not have a property StrUserName. It has UserName, which is what you want to bind to.
Related
I have a simple Person model:
public class Person
{
public string FirstName { get; set; }
public DateTime LastUpdated { get; set; }
}
Lets say that I have a View that has a TextBox, which is binded to LastUpdated field:
<TextBox Grid.Column="1" Margin="5" Text="{Binding Person.FirstName}"/>
Now I need to implement PropertyChanged somehow. Let's use Prism Snippet.
What I need is to perform SetProperty on a Person class:
private Person person;
public Person Person
{
get { return person; }
set { SetProperty(ref person, value); }
}
and NOT on the field LastUpdated in Person class:
private DateTime? lastUpdated;
public DateTime? LastUpdated
{
get { return lastUpdated; }
set { SetProperty(ref lastUpdated, value); }
}
This is not the matter of dependencies in the model. I got the model through DataContract ( WCF service ) so I cannot changed it. So is there a way to observe a class for changes and bind class field to some UI control.
So is there a way to observe a class for changes and bind class field to some UI control.
No. You need to raise the PropertyChanged event for the object of the property that you are actually binding to.
If you get the Person object from some third-party service for which you cannot modify the code to raise the PropertyChanged event in the setter of the FirstName property, you should not bind to these objects.
Instead you should create your own view model class and bind to this one. The view model can simply wrap the WCF class, e.g.:
public class PersonViewModel
{
private readonly Person _person;
public PersonViewModel(Person person)
{
_person = person;
}
public string FirstName
{
get { return _person.FirstName; }
set { _person.FirstName = value; RaisePropertyChanged(); }
}
}
If you're using Prism, then you likely are using the MVVM pattern. If so, then the one approach is using the view model for binding. Instead of exposing Person as a property, expose the individual properties you want to bind against - FirstName and LastUpdated:
Create a property on the view model that forwards calls to your model.
Bind your view to the view model property.
You can freely implement your change notifications in the view model.
My Main window has a sidebar menu. When an item on the menu is clicked, I will render that item's page (UserControl) on a ContentControl. Here is what it looks like.
My MainViewModel
public MainViewModel()
{
SystemMenu = new List<SystemMenuViewModel>();
SystemMenu.Add(new SystemMenuViewModel("Dashboard", new Dashboard()));
SystemMenu.Add(new SystemMenuViewModel("Appointments", new Dashboard()));
SystemMenu.Add(new SystemMenuViewModel("Reports", new Reports()));
SystemMenu.Add(new SystemMenuViewModel("Configuration", new Configuration()));
}
private string _windowTitle = GlobalVariables.WindowTitleDefault;
private string _currentPage = "Dashboard";
public string WindowTitle
{
get { return _windowTitle; }
set
{
_windowTitle = value;
NotifyOfPropertyChange(() => WindowTitle);
}
}
public string CurrentPage
{
get { return _currentPage; }
set
{
_currentPage = value;
NotifyOfPropertyChange(() => CurrentPage);
}
}
public List<SystemMenuViewModel> SystemMenu { get; set; }
My SystemMenuViewModel
private string _name;
private object _content;
public SystemMenuViewModel(string name, object content)
{
_name = name;
Content = content;
}
public string Name
{
get { return _name; }
set { this.MutateVerbose(ref _name, value, RaisePropertyChanged()); }
}
public object Content
{
get { return _content; }
set { this.MutateVerbose(ref _content, value, RaisePropertyChanged()); }
}
public event PropertyChangedEventHandler PropertyChanged;
private Action<PropertyChangedEventArgs> RaisePropertyChanged()
{
return args => PropertyChanged?.Invoke(this, args);
}
My MainView on the rendering part
<ContentControl Content="{Binding ElementName=lstSystemMenu, Path=SelectedItem.Content}" />
My main problem is that I am just rendering the Content on my MainView without actually invoking or binding its ViewModel.
I am sure that there is something wrong on my implementation of the MVVM framework. Kindly enlighten me on what part did I go wrong and what's the best way to implement this one.
Have a look at this : https://msdn.microsoft.com/en-us/magazine/dd419663.aspx
Can't find the source zip, but the article has plenty of code samples.
What you need is to bind your ContentControl's Content property to a ViewModel/Model object, and use DataTemplates to create the correct page depending on the datacontext. The datatemplates just need to be stored in a ResourceDictionary, either the ContentControl's or some upper level control (or even the app). The DataTemplates must have a DataType set for this to work.
Also, as suggested in the comments on your question, the viewmodels shouldn't have a "content" property of type object. It looks like your "content" property is a view object or something. Can't know without you showing us more code.
The ViewModel should not reference any View object. But the View can reference ViewModel classes in the code-behind or in the XAML.
There are two ways to bind to the "current selection".
Either use the "current selected" info from the view list (SelectedItem for example), or add a property in the MainViewModel (ex: SelectedViewModel, and then bind the ContentControl to this property.
I have TaskViewModel class with a lot of different properties. The simplified piece of code is below:
internal class TaskViewModel : ViewModelBase
{
private TaskModel _model;
public long Id
{
get { return _model.Id; }
set
{
_model.Id = value;
RaisePropertyChanged("Id");
}
}
public string Title
{
get { return _model.Title; }
set
{
_model.Title = value;
RaisePropertyChanged("Title");
}
}
public DateTime? Date
{
get { return _model.Date; }
set
{
_model.Date = value;
RaisePropertyChanged("Date";);
}
}
private RelayCommand _updateCommand;
public RelayCommand UpdateCommand
{
get
{
return _updateCommand
?? (_updateCommand = new RelayCommand(
() =>
{
// somehow update _model
}));
}
}
}
And I have TaskView where I could edit the instance of TaskViewModel. Also I have a few validation rules, for example, if Titleis empty I can't update model and have to reestablish previous Title. That's why I cannot use "{Binding Mode=TwoWay}.
The question is what is the best way to update view model.
I have two ways to do it:
Add property of TaskViewModel type to the instance and bind all properties of this to the view and than using ICommand for updating properties in main instance if all validations rules are performing. But in this case I need to keep whole copy of object.
Using "{Binding Mode=TwoWay, UpdateSourceTrigger=Explicit}" for necessary properties and than in code-behind using event handlers call binding.UpdateSource(). But in that case I have to implement validation logic in code-behind too, which looks like a bad way in mvvm-approach.
May be you should recommend the best way for this task.
Thanks in advance.
UPDATE:
For example of the typical validation case, Title mustn't be empty. If I changed the Title property from "Buy milk" to "Buy mi" it would be valid, but I don't want to update my model after every change of every property and save it to a storage. So I have to implement SaveCommand which will update the model. But also I need to have a possibility to rollback all the changes, so I can't change current view model properties directly by using Mode=TwoWay binding.
So the problem is how to update all changed properties on demand if they are valid?
I have a class, derived from BindableBase, which contains two properties:
private string sourceCallNumber;
public string SourceCallNumber
{
get { return sourceCallNumber; }
set { SetProperty(ref sourceCallNumber, value); }
}
private string sourceMediaType;
public string SourceMediaType
{
get { return sourceMediaType; }
set { SetProperty(ref sourceMediaType, value); }
}
I have an ObservableCollection that contains a number of items using that class.
I have a GridView for which I set the ItemsSource to point to the ObservableCollection.
My problem is that if I change the value of, say, SourceMediaType on one of the items, the display does not update. I have put debugging in and can confirm that changing the value causes OnPropertyChanged to fire for that property.
I've read quite a few SO questions and answers around similar problems and I'm getting quite confused as to what I need to do in order to get this to work.
My understanding was that although ObservableCollection itself doesn't do anything when a property is changed, if the item itself triggers an OnPropertyChanged, that should get the display to update.
(There was one answer I read that proposed the use of code provided called TrulyObservableCollection but the problem I've got there is that everything refreshes rather than just the one item that has been updated).
What am I missing or misunderstanding, please?
Thanks.
C# apps should implement INotifyCollectionChanged and System.Collections.IList (not IList Of T).
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("Willa", "Cather"));
Add(new PersonName("Isak", "Dinesen"));
Add(new PersonName("Victor", "Hugo"));
Add(new PersonName("Jules", "Verne"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
firstName = first;
lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
Look at GridView.
#RodrigoSilva put me on the correct path ... the XAML that references the values was this:
<StackPanel>
<TextBlock Text="{Binding DisplayCallNumber}" Style="{StaticResource TitleTextBlockStyle}" Visibility="{Binding GotCallNumber, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
<TextBlock Text="{Binding DisplayMediaType}" Style="{StaticResource ItemTextStyle}" Visibility="{Binding GotMediaType, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
</StackPanel>
which doesn't directly reference the underlying properties SourceCallNumber and SourceMediaType. As a result, although OnPropertyChanged is correctly firing for SourceCallNumber and SourceMediaType, that isn't causing the display to update because that isn't what the XAML is pointing at.
Explicitly changing the call to SetProperty to this:
SetProperty(ref sourceCallNumber, value, "DisplayCallNumber");
fixes the problem but is not a GOOD fix because some other part of the app may actually be binding to SourceCallNumber and won't get a property update after this change. The GOOD fix is to use a converter as explained in http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464965.aspx.
For my question, let's assume I have a viewmodel, model and view (MVVM). The viewmodel contains a couple of properties that change (and change the view via INotifyPropertyChanged event where they are binded).
Now I want to do something like a poly-property (i.e. take together a couple of string properties and put it into one property). How can I do this properly?
For example, I have three properties: Prop1, Prop2, Prop3 and want to make one property out of these: PropNew = String.Format("{0}, {1} {2}", Prop1, Prop2, Prop3);.
A possibility is to create a new property in the viewmodel (and update this if another property changes) which is then bound to the view. But I fear this is not clean... Are there other possibilities?
THank you!
If I understand the question correctly, then you want to update the UI binding to a property on the view model that is derived from the current state of the class.
Notifying the UI in WPF is done by raising the PropertyChanged event on the interface INotifyPropertyChanged. In the code snippet below I am using the NotificationObject from the PRISM library:
public class MyViewModel: NotificationObject
{
private string _lastName;
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged(() => Greeting);
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
RaisePropertyChanged(() => Greeting);
}
}
public string Greeting
{
get { return string.Format("Hello {0} {1}!", _firstName, _lastName); }
}
}
Updating either of the FirstName or LastName properties will cause the UI to be notified that the Greeting property has changed, and that the data should be rebound.
Have you considered using MutiBinding and interface IMultiValueConverter?