Custom Object resets its values after initializing from constructor - c#

I create (if not exists) a new ViewModel Instance via the IMessenger (MVVM Light Toolkit) and pass a custom Object trough the constructor, which is a Property of my MainViewModel.
In this ViewModel I set it to a Property too and using it e.g. for a Command Execute Method. But when I trigger the Command via Binding, the custom Object Property looses its values.
I already debugged and saw, that the object get's passed correctly with its values, but after initializing from the constructor its empty (Not null, just empty Properties).
My custom Object
public class CustomObject : ObservableObject
{
public int Id { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
// etc...
}
I create the ViewModel like this
public CustomObject CustomObj
{
get { return _customObj; }
set { Set(ref _customObj, value); }
}
_customViewModel = new CustomViewModel(CustomObj, _dataService);
The ViewModel
public CustomViewModel(CustomObject obj, IDataService dataService)
{
_dataService = dataService;
// Here it sets correctly the Object
CustomObj = obj;
}
public CustomObject CustomObj
{
get { return _customObj; }
set { Set(ref _customObj, value); }
}
// Even before the Command is triggered, the Object is already empty
public ICommand SomeCommand => new RelayCommand<string>(async s =>
{
var someThing = await _dataService.GetSomeData(CustomObj.Id);
// Stuff...
}
They are registered in the SimpleIoC Container as well, if that matters.
What could result that?

Related

HasChanged method for c# model

I am searching for a solution where i can ask a model if a property has changed. But i want to prevent to write own setter methods for all models and all their properties.
I want to use this to automatically generate a update queries based models and there changed properties. But if my model has a boolean property Test which is by default false, then i can't differentiate if the value is from the request payload or if it is the default value.
I already saw the INotifyPropertyChanged Implementation but there i have to write a setter for all properties too.
public class Main
{
public static void main()
{
var person = new Person();
Console.WriteLine(person.HasChanged("Firstname")); // false
Console.WriteLine(person.HasChanged("Lastname")); // false
Console.WriteLine(person.HasChanged("LikesChocolate")); // false
person.Firstname = "HisFirstname";
person.LikesChocolate = true;
Console.WriteLine(person.HasChanged("Firstname")); // true
Console.WriteLine(person.HasChanged("Lastname")); // false
Console.WriteLine(person.HasChanged("LikesChocolate")); // true
}
}
public class Person : BaseModel
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public bool LikesChocolate { get; set; }
}
public class BaseModel
{
public bool HasChanged(string propertyName)
{
// ...
}
}
I'd probably reuse the idea from WPF with their INotifyPropertyChanged pattern and simplify it a bit for the current needs. However, it resolves the question only partially, as you still need to write setters. But at least, you don't need to manage each property on its own.
So, the solution will be something like this:
void Main()
{
var person = new Person();
Console.WriteLine(person.HasChanged(nameof(Person.FirstName))); // false
Console.WriteLine(person.HasChanged(nameof(Person.LastName))); // false
Console.WriteLine(person.HasChanged(nameof(Person.LikesChocolate))); // false
person.FirstName = "HisFirstname";
person.LikesChocolate = true;
Console.WriteLine(person.HasChanged(nameof(Person.FirstName))); // true
Console.WriteLine(person.HasChanged(nameof(Person.LastName))); // false
Console.WriteLine(person.HasChanged(nameof(Person.LikesChocolate))); // true
}
public class Person : ChangeTrackable
{
private string _firstName;
private string _lastName;
private bool _likesChocolate;
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
public string LastName
{
get { return _lastName; }
set { SetProperty(ref _lastName, value); }
}
public bool LikesChocolate
{
get { return _likesChocolate; }
set { SetProperty(ref _likesChocolate, value); }
}
}
public class ChangeTrackable
{
private ConcurrentDictionary<string, bool> _changes =
new ConcurrentDictionary<string, bool>();
public bool HasChanged(string propertyName)
{
return _changes.TryGetValue(propertyName, out var isChanged)
? isChanged : false;
}
public void ResetChanges()
{
_changes.Clear();
}
protected void SetProperty<T>(
ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (!Equals(storage, value))
{
_changes[propertyName] = true;
}
}
}
The ChangeTrackable tracks if property was changed and does it without any reflection that guarantees high performance. Note, that with this implementation you need to call ResetChanges if you initialize property with some actual values after constructing the object. Drawback is that you need to write each property with its backing field and call SetProperty. On the other side, you decide what to track, that could be handy in the future in your application. Also we don't need to write property as strings (thanks to compile-time CallerMemberName and nameof) that simplifies refactorings.
INotifyPropertyChanged is the established practice for this type of requirement. Part of keeping your code maintainable is by keeping it predictable and by adopting best practices and patterns.
An alternative, which I wouldn't recommend, would be to use reflection to iterate over all of your properties and dynamically add a property changed event handler. This handler could then set a boolean flag which can be returned by your HasChanges method. Please refer to this for a staring point: AddEventHandler using reflection
I would recommend avoiding unnecessary complexity though and stick with PropertyChanged notifications in your setters.
As followup for my comment a proof of concept (online):
using System.Reflection;
public class HasChangedBase
{
private class PropertyState
{
public PropertyInfo Property {get;set;}
public Object Value {get;set;}
}
private Dictionary<string, PropertyState> propertyStore;
public void SaveState()
{
propertyStore = this
.GetType()
.GetProperties()
.ToDictionary(p=>p.Name, p=>new PropertyState{Property = p, Value = p.GetValue(this)});
}
public bool HasChanged(string propertyName)
{
return propertyStore != null
&& propertyStore.ContainsKey(propertyName)
&& propertyStore[propertyName].Value != propertyStore[propertyName].Property.GetValue(this);
}
}
public class POCO : HasChangedBase
{
public string Prop1 {get;set;}
public string Prop2 {get;set;}
}
var poco = new POCO();
poco.Prop1 = "a";
poco.Prop2 = "B";
poco.SaveState();
poco.Prop2 = "b";
poco.HasChanged("Prop1");
poco.HasChanged("Prop2");
Be aware, that reflection may reduce the performance of your application when used extensively.

MvvmCross - Passing a string with IMvxNavigationService

I'm currently working on a Xamarin.iOS project that uses a web-api to gather data. However, I'm running into some problems trying to pass the user input from a textfield to the Tableview that gets the result from the api.
To do this I've followed the example on the MvvmCross documentation.
The problem is that the input from the Textfield never reaches the 'Filter' property in my TableviewController's viewmodel. I think I'm not passing the string object correctly to my IMvxNavigationService when called.
To clarify, in my UserinputViewController I'm binding the textfield's text like so:
[MvxFromStoryboard(StoryboardName = "Main")]
public partial class SearchEventView : MvxViewController
{
public SearchEventView (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
MvxFluentBindingDescriptionSet<SearchEventView, SearchEventViewModel> set = new MvxFluentBindingDescriptionSet<SearchEventView, SearchEventViewModel>(this);
set.Bind(btnSearch).To(vm => vm.SearchEventCommand);
set.Bind(txtSearchFilter).For(s => s.Text).To(vm => vm.SearchFilter);
set.Apply();
}
}
The Viewmodel linked to this ViewController looks like this:
public class SearchEventViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
private string _searchFilter;
public string SearchFilter
{
get { return _searchFilter; }
set { _searchFilter = value; RaisePropertyChanged(() => SearchFilter); }
}
public SearchEventViewModel(IMvxNavigationService mvxNavigationService)
{
this._navigationService = mvxNavigationService;
}
public IMvxCommand SearchEventCommand {
get {
return new MvxCommand<string>(SearchEvent);
}
}
private async void SearchEvent(string filter)
{
await _navigationService.Navigate<EventListViewModel, string>(filter);
}
}
And finally, TableviewController's viewmodel looks like this:
public class EventListViewModel : MvxViewModel<string>
{
private readonly ITicketMasterService _ticketMasterService;
private readonly IMvxNavigationService _navigationService;
private List<Event> _events;
public List<Event> Events
{
get { return _events; }
set { _events = value; RaisePropertyChanged(() => Events); }
}
private string _filter;
public string Filter
{
get { return _filter; }
set { _filter = value; RaisePropertyChanged(() => Filter); }
}
public EventListViewModel(ITicketMasterService ticketMasterService, IMvxNavigationService mvxNavigationService)
{
this._ticketMasterService = ticketMasterService;
this._navigationService = mvxNavigationService;
}
public IMvxCommand EventDetailCommand {
get {
return new MvxCommand<Event>(EventDetail);
}
}
private void EventDetail(Event detailEvent)
{
_navigationService.Navigate<EventDetailViewModel, Event>(detailEvent);
}
public override void Prepare(string parameter)
{
this.Filter = parameter;
}
public override async Task Initialize()
{
await base.Initialize();
//Do heavy work and data loading here
this.Events = await _ticketMasterService.GetEvents(Filter);
}
}
Whenever trying to run, the string object 'parameter' in my TableviewController's Prepare function remains 'null' and I have no idea how to fix it. Any help is greatly appreciated!
I believe the issue is with your command setup
new MvxCommand<string>(SearchEvent);
As this command is being bound to a standard UIButton. It will not pass through a parameter value of your filter but null instead. So the string parameter generic can be removed. Additionally, as you want to execute an asynchronous method I would suggest rather using MvxAsyncCommand
new MvxAsyncCommand(SearchEvent);
Then in terms of SearchEvent method you can remove the parameter. The value of filter is bound to your SearchFilter property. It is this property's value that you want to send as the navigation parameter.
private async Task SearchEvent()
{
await _navigationService.Navigate<EventListViewModel, string>(SearchFilter);
}

Passing Parameter From Main to Detail in MVVMCross

I am trying to pass the selected item from the list to the detail view, but myitem is null in the DetailViewmodel even though it is not in the MyViewModel.
MyViewModel.cs
public virtual ICommand ItemSelected
{
get
{
return new MvxCommand<MyViewModel>(item =>{SelectedItem = item;});
}
}
public MyViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
// myItem is NOT null here!!!
ShowViewModel<MyDetailViewModel>(new { date = Date, myItem = _selectedItem });
RaisePropertyChanged(() => SelectedItem);
}
}
MyDetailViewModel.cs
public class MyDetailViewModel: MvxViewModel
{
private MyViewModel _myItem;
public void Init(DateTime date, MyViewModel myItem = null)
{
// myItem is NULL here!!!
_myItem = myItem;
}
}
You can use a parameter object, because you can only pass one parameter. I usually crate a nested class Parameter for this.
public class MyDetailViewModel: MvxViewModel
{
private MyViewModel _myItem;
public class Parameter
{
public DateTime Date {get; set; }
public string Name {get; set;}
}
public void Init(Parameter param)
{
Name = param.Name;
}
}
and show the viewmodel like:
ShowViewModel<MyDetailViewModel>(new MyDetailViewModel.Parameter { Date = Date, Name = _selectedItem.Name });
But be aware!
The paramters cannot be complex due certain platform issues. You might have to pass only the Id of your Item within the Parameter object and then load MyItem in your Init function. Or you pass only a string and use serialization: https://stackoverflow.com/a/19059938/1489968
myItem is null because if you pass typed parameter to Init it should be the only parameter you pass. According to MvvmCross ViewModel Creation documentation:
Init() can come in several flavors:.
individual simply-Typed parameters
a single Typed parameter object with simply-Typed properties
as InitFromBundle() with an IMvxBundle parameter - this last flavor is always supported via the IMvxViewModel interface.

Serializing/Deserializing Command Object

I'm attempting to serialize (and later deserialize) a command object to a string (preferably using the JavaScriptSerializer). My code compiles, however when I serialize my command object it returns an empty Json string, i.e. "{}". The code is shown below.
The aim is to serialize the command object, place it in a queue, then deserialize it later so it can be executed. If the solution can be achieved with .NET 4 then all the better.
ICommand
public interface ICommand
{
void Execute();
}
Command Example
public class DispatchForumPostCommand : ICommand
{
private readonly ForumPostEntity _forumPostEntity;
public DispatchForumPostCommand(ForumPostEntity forumPostEntity)
{
_forumPostEntity = forumPostEntity;
}
public void Execute()
{
_forumPostEntity.Dispatch();
}
}
Entity
public class ForumPostEntity : TableEntity
{
public string FromEmailAddress { get; set; }
public string Message { get; set; }
public ForumPostEntity()
{
PartitionKey = System.Guid.NewGuid().ToString();
RowKey = PartitionKey;
}
public void Dispatch()
{
}
}
Empty String Example
public void Insert(ICommand command)
{
// ISSUE: This serialization returns an empty string "{}".
var commandAsString = command.Serialize();
}
Serialization Extension Method
public static string Serialize(this object obj)
{
return new JavaScriptSerializer().Serialize(obj);
}
Any help would be appreciated.
Your DispatchForumPostCommand class has no properties to serialize. Add a public property to serialize it. Like this:
public class DispatchForumPostCommand : ICommand {
private readonly ForumPostEntity _forumPostEntity;
public ForumPostEntity ForumPostEntity { get { return _forumPostEntity; } }
public DispatchForumPostCommand(ForumPostEntity forumPostEntity) {
_forumPostEntity = forumPostEntity;
}
public void Execute() {
_forumPostEntity.Dispatch();
}
}
I now get the following as the serialized object (I removed the inheritance of TableEntity for testing purposes):
{"ForumPostEntity":{"FromEmailAddress":null,"Message":null}}
If you want to deserialize the object as well, then you will need to add the public setter for the property, else the deserializer will not be able to set it.

Dependency attribute in c#

Here is my code
private MyClass _someProperty
[Dependency]
public MyClass SomeProperty
{
get{
if(_someProperty == null)
_someProperty = new MyClass()
return (MyClass)_someProperty
}
}
This works good, but I'm directed like "No need to add "get" property for [Dependency]"
I'm not clear with this [Dependency] attribute. When I looked into other code, it is written as
[Dependency]
public AnotherClass MyNewClass
{
get; set;
}
When I tried the above for MyClass, it didn't work. It threw the Property is NULL
Can anybody tell me what is the use of the [Dependency] and whether there is an alternate way for MyClass code and why it is coming as "NULL" for that?
This has nothing to do with the DependencyAttribute. The latter case is simply a auto-generated property. If you like to use this syntax you have to fill in the value within your constructor.
public class MyClass
{
public MyClass()
{
SomeProperty = new AnotherClass();
}
public AnotherClass SomeProperty { get; set; }
}
Be aware that you in this case you can't perform any checks within the getter or setter. So if you need any nullity checks or like to raise an event within the setter, you have to implement the getter and setter on yourself.
public class MyClass
{
private AnotherClass _SomeProperty;
public MyClass()
{
_SomeProperty = new AnotherClass();
}
public AnotherClass SomeProperty
{
get { return _SomeProperty; }
set
{
if(value == null)
throw new ArgumentNullException("SomeProperty");
if(value != _SomeProperty)
{
_SomeProperty = value;
// ToDo: Implement RaiseEvent() and declare event.
RaiseEvent(MyEvent);
}
}
}
}

Categories

Resources