I am trying to instantiate an object of my model class, from my MVVM test project, in my viewmodel class.
Person Model Class:
using System.ComponentModel;
namespace WPFAppTest.Models
{
public class Person : INotifyPropertyChanged
{
private string _FirstName;
public string FirstName
{
get
{
return _FirstName;
}
set
{
_FirstName = value;
RaisePropertyChange("FirstName");
RaisePropertyChange("FullName");
}
}
private string _LastName;
public string LastName
{
get
{
return _LastName;
}
set
{
_LastName = value;
RaisePropertyChange("LastName");
RaisePropertyChange("FullName");
}
}
public string FullName
{
get
{
return _FirstName + " " + _LastName;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
PersonViewModel Class:
using WPFAppTest.Models;
namespace WPFAppTest.ViewModels
{
public class PersonViewModel
{
public Person person = new Person();
person.FirstName = "Iain";
}
}
It seems I get an error saying "The name 'person.FirstName' does not exist in the current context".
How do I create the object and then access its properties to set the values? Which in turn I can then use for data bindings in my View?
The statement person.FirstName = "Iain"; must be inside a method or in the constructor
public class PersonViewModel
{
public Person person = new Person();
public PersonViewModel() // constructor
{
person.FirstName = "Iain";
}
public void Test() // method
{
person.FirstName = "Pete";
}
}
Note: the constructor which is called automatically when you create an object with new PersonViewModel() has the same name as the class and does not have a return type (there is no void keyword).
The method Test must be called explicitly
var p = new PersonViewModel(); // calls constructor and assigns "Iain".
p.Test(); // Assigns "Pete"
The constructor can have parameters like a method.
public PersonViewModel(string personName) // constructor with parameter
{
person.FirstName = personName;
}
You must pass an argument to this parameter when calling new:
var p = new PersonViewModel("Sue"); // Creates a VM with a person named "Sue".
For a View Model it would probably make sense to pass a Person instead.
public class PersonViewModel
{
private readonly Person _person;
public string FirstName => _person.FirstName;
public PersonViewModel(Person person) // constructor
{
_person = person;
}
}
Related
Instead of directly exposing entire Model to the View, I want to have ViewModel properties which are just proxies for each Model properties. For example;
private Product _product;
public string ProductName
{
get { return _product.ProductName; }
set
{
SetProperty(ref _product.ProductName, value);
}
}
But the above example causes the error of A property, indexer or dynamic member access may not be passed as an out or ref parameter.
How should I solve this problem?
P.S. My Models are not implemented by INPC interface. They are just simple POCO classes.
What you want is a façade or decorator object that will act as your model in your VM, not wrapping every model property with ViewModel properties. This allows you to not only reuse your models (facades/decorators), but it also keeps the concerns where they belong. You define your properties just like chipples provided, but call OnPropertyChanged() in the setter. You can't use the SetProperty method when wrapping other properties.
Something similar to this:
class Person
{
public string Name { get; set; }
}
class PersonFacade : BindableBase
{
Person _person;
public string Name
{
get { return _person.Name; }
set
{
_person.Name = value;
OnPropertyChanged();
}
}
}
class ViewModel : BindableBase
{
private PersonFacade _person;
public PersonFacade Person
{
get { return _person; }
set { SetProperty(ref _person, value); }
}
}
Maybe this is an old thread but C# MVVM property makes me stressed. Imagine You need to write 200 properties a day.
I have another approach to creating BaseClass.
public abstract class NotifyPropertiesBase : INotifyPropertyChanged, INotifyPropertyChanging
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
readonly Dictionary<string, object> _propertyStore = new Dictionary<string, object>();
public PropertyChangingEventArgs NotifyChanging([CallerMemberName] string propertyName = null)
{
var arg = new PropertyChangingEventArgs(propertyName);
PropertyChanging?.Invoke(this, arg);
return arg;
}
public PropertyChangedEventArgs NotifyChanged([CallerMemberName] string propertyName = null)
{
var arg = new PropertyChangedEventArgs(propertyName);
PropertyChanged?.Invoke(this, arg);
return arg;
}
public void SetPropValue(object newValue, [CallerMemberName] string propertyName = null)
{
if (GetType().GetMember(propertyName).Count() != 1)
throw new NotSupportedException($"\"{propertyName}\" Not Supported or maybe its not a Property");
var member = GetType().GetMember(propertyName).FirstOrDefault();
if (member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotSupportedException($"Not Support Member Type {member.MemberType}");
var pInfo = member.DeclaringType.GetProperties().First();
NotifyChanging(propertyName);
if (!_propertyStore.ContainsKey(propertyName))
_propertyStore.Add(propertyName, newValue);
else
_propertyStore[propertyName] = newValue;
NotifyChanged(propertyName);
}
public T GetPropertyValue<T>([CallerMemberName] string propertyName = null)
{
return (T)GetPropertyValue(propertyName);
}
public object GetPropertyValue([CallerMemberName] string propertyName = null)
{
if (GetType().GetMember(propertyName).Count() != 1)
throw new NotSupportedException($"\"{propertyName}\" Not Supported or maybe its not a Property");
var member = GetType().GetMember(propertyName).FirstOrDefault();
if (member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotSupportedException($"Not Support Member Type {member.MemberType}");
var pInfo = member.DeclaringType.GetProperties().First();
if (!_propertyStore.ContainsKey(propertyName))
{
_propertyStore.Add(propertyName, GetDefault(pInfo.PropertyType));
}
return _propertyStore[propertyName];
}
object GetDefault(Type t)
{
if (t.IsValueType)
{
return Activator.CreateInstance(t);
}
return null;
}
}
Class Usages:
class Program
{
static void Main(string[] args)
{
var t = new Test();
t.PropertyChanged += T_PropertyChanged;
t.ValueTest = "Hello World!";
var data = t.GetPropertyValue(nameof(t.ValueTest));
Console.Write(data);
}
private static void T_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}
}
public class Test : NotifyPropertiesBase
{
public string ValueTest
{
get => GetPropertyValue<string>();
set => SetPropValue(value);
}
}
It is simple make with helper class like ViewModelBase, which simplifies raise PropertyChanged event:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
PropertyChanged(this, new PropertyChangedEventArgs(ExtractPropertyName(propertyExpression)));
}
private static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
throw new ArgumentNullException("propertyExpression");
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentException("memberExpression");
var property = memberExpression.Member as PropertyInfo;
if (property == null)
throw new ArgumentException("property");
var getMethod = property.GetGetMethod(true);
if (getMethod.IsStatic)
throw new ArgumentException("static method");
return memberExpression.Member.Name;
}
}
then, simple POCO class Person:
class Person
{
public string Name { get; set; }
public double Age { get; set; }
}
we can wrap to ViewModel like:
public class PersonViewModel : ViewModelBase
{
private readonly Person person = new Person();
public string Name
{
get { return person.Name; }
set
{
person.Name = value;
OnPropertyChanged(() => Name);
}
}
public double Age
{
get { return person.Age; }
set
{
person.Age = value;
OnPropertyChanged(() => Age);
}
}
}
SetProperty is not needed here, you can just do this:
private Product _product;
public string ProductName
{
get { return _product.ProductName; }
set
{
_product.ProductName = value;
}
}
Using Castle.DynamicProxy, what is the best way to create a proxy from an existing class instance?
// The actual object
Person steve = new Person() { Name = "Steve" };
// Create a proxy of the object
Person fakeSteve = _proxyGenerator.CreateClassProxyWithTarget<Person>(steve, interceptor)
// fakeSteve now has steve as target, but its properties are still null...
Here's the Person class:
public class Person
{
public virtual string Name { get; set; }
}
Here's the interceptor class:
public class PersonInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Person p = invocation.InvocationTarget as Person;
if (invocation.Method.Name.Equals(get_Name)
{
LoadName(p);
}
}
private void LoadName(Person p)
{
if (string.IsNullOrEmpty(p.Name))
{
p.Name = "FakeSteve";
}
}
}
If your Person class has only non-virtual properties the proxy can't access them. Try to make the properties virtual.
http://kozmic.net/2009/02/23/castle-dynamic-proxy-tutorial-part-vi-handling-non-virtual-methods/
I am trying to add entries in dictionary array list but i don't know which arguments to set in the People Class in the main function.
public class People : DictionaryBase
{
public void Add(Person newPerson)
{
Dictionary.Add(newPerson.Name, newPerson);
}
public void Remove(string name)
{
Dictionary.Remove(name);
}
public Person this[string name]
{
get
{
return (Person)Dictionary[name];
}
set
{
Dictionary[name] = value;
}
}
}
public class Person
{
private string name;
private int age;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
}
using this seem to give me error
static void Main(string[] args)
{
People peop = new People();
peop.Add("Josh", new Person("Josh"));
}
Error 2 No overload for method 'Add' takes 2 arguments
This peop.Add("Josh", new Person("Josh"));
should be this
var josh = new Person() // parameterless constructor.
{
Name = "Josh" //Setter for name.
};
peop.Add(josh);//adds person to dictionary.
The class People has the method Add which only takes one argument: a Person object. The Add on the people class method will take care of adding the it to the dictionary for you and supplying both the name (string) argument and the Person argument.
Your Person class only has a parameterless constructor, which means that you need to set your Name in the setter. You can do this when you instantiate the object like above.
For your design this would solve the problem:
public class People : DictionaryBase
{
public void Add(string key, Person newPerson)
{
Dictionary.Add(key , newPerson);
}
public void Remove(string name)
{
Dictionary.Remove(name);
}
public Person this[string name]
{
get
{
return (Person)Dictionary[name];
}
set
{
Dictionary[name] = value;
}
}
}
public class Person
{
private string name;
private int age;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
}
And in Main:
People peop = new People();
peop.Add("Josh", new Person() { Name = "Josh" });
I'm trying to call an overriden property from a base constructor but I'm receiving a System.Reflection.TargetInvovationException("Object reference not set to an instance of an object."). Why is this error being thrown and can anything be done to avoid it?
I would have expected the constructor to have just called the overriden property.
Here is a stripped down example:
// Call that generates exception
var foo = new Foo();
public class Foo : Bah {
public Foo() : base("Foo!") {}
public override string Name {
get { return _name + "123"; }
set { _name = value; }
}
}
public class Bah {
protected string _name;
public Bah(string name) {
Name = name; // << -- Exception here
}
public virtual string Name {
get { return _name; }
set { _name = value; }
}
}
You have some other issue going on. The code, as typed, works. Try this fully functional program to see, which prints (as expected) "Foo!123":
using System;
namespace Test
{
public class Program
{
private static void Main()
{
var foo = new Foo();
Console.WriteLine(foo.Name);
Console.ReadKey();
}
}
public class Foo : Bah
{
public Foo() : base("Foo!") { }
public override string Name
{
get { return _name + "123"; }
set { _name = value; }
}
}
public class Bah
{
protected string _name;
public Bah(string name)
{
Name = name; // << -- No Exception here (or anywhere!)
}
public virtual string Name
{
get { return _name; }
set { _name = value; }
}
}
}
That being said, calling virtual methods (including Property accessor methods) in a constructor is a very bad idea. It can lead to very odd behavior, which is likely the culprit in your real code.
I have a simple custom class (Person), which I want to bind to a label as a whole (not to separate properties of this class). The label should just present whatever the Person.ToString() returns (in this case FirstName + LastName).
How do I properly bind it using the person as a Source.
How do I make sure that any change in one of the properties of the Person will be reflected in the label?
public class Person : INotifyPropertyChanged {
private string firstName;
public string FirstName {
get {
return firstName;
}
set {
firstName = value;
OnPropertyChanged("FirstName");
}
}
private string lastName;
public string LastName {
get {
return lastName;
}
set {
lastName = value;
OnPropertyChanged("LastName");
}
}
public override string ToString() {
return FirstName + " " + LastName;
}
private void OnPropertyChanged(string name) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public Window1() {
myPerson = new Person() {
FirstName = "AAA",
LastName = "BBB"};
InitializeComponent();
}
public Person MyPerson {
get {
return myPerson;
}
set {
myPerson = value;
}
}
Label Content="{Binding Source=MyPerson}"
Create a new property FullName which returns the full name and raise PropertyChanged for FullName in the setters of FirstName and LastName as well. You should never bind to the object itself.