I'm currently converting the SideBarDemo to C# and MonoMac in combination with ReactiveUI.
I have two subclasses of NSTextField and NSTableCellView that are views for a common view model class.
My problem is that I do not know how to implement these sub classes so that data binding works.
How does a good implementation of such subclasses looks like?
In the following you can see my current state. I know that the binding, that is created in the constructor won't work, because ViewModel is an ordinary property. However, I could not figure out which interfaces I should implement best.
[Register("MainCellView")]
public class MainCellView : NSTableCellView, IViewFor<TreeItemViewModel>
{
public MainCellView ()
{
this.OneWayBind (ViewModel, x => x.Name, x => x.TextField.StringValue);
}
public MainCellView(IntPtr ptr) : base(ptr) { }
public TreeItemViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get { return this.ViewModel; }
set { this.ViewModel = (TreeItemViewModel)value; }
}
}
[Register("HeaderCellView")]
public class HeaderCellView : NSTextField, IViewFor<TreeItemViewModel>
{
public HeaderCellView ()
{
this.OneWayBind (ViewModel, x => x.Name, x => x.StringValue);
}
public HeaderCellView(IntPtr ptr) : base(ptr) { }
TreeItemViewModel _vm;
public TreeItemViewModel ViewModel { get; set }
object IViewFor.ViewModel
{
get { return this.ViewModel; }
set { this.ViewModel = (TreeItemViewModel)value; }
}
}
Thx a lot in advance,
Jens
However, I could not figure out which interfaces I should implement best.
If ReactiveUI doesn't have a built-in subclass that helps you out for a class, you should implement INotifyPropertyChanged for your class, and signal when ViewModel changes. That should be enough to get bindings working!
Related
I was given this sample code when creating a new MVVM light(WPF451) project and it made me confusing.
DataItem.cs:
public class DataItem
{
public string Title { get; private set; }
public DataItem(string title)
{
Title = title;
}
}
This class declares a set of properties that is needed in the ViewModel. It's used in the Model layer DataService, which provides data to the VM in its constructor.
DataService.cs
public class DataService : IDataService
{
public void GetData(Action<DataItem, Exception> callback)
{
// Use this to connect to the actual data service
var item = new DataItem("Welcome to MVVM Light");
callback(item, null);
}
}
I thought it would be used in the VM as well to hold properties, like this:
public DataItem Data { get; set; }
but instead, the MVVM light developer decided to re-declare the properties in the VM.
MainViewModel.cs:
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private string _welcomeTitle = string.Empty;
public string WelcomeTitle
{
get{ return _welcomeTitle; }
set{ Set(ref _welcomeTitle, value); }
}
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
WelcomeTitle = item.Title;
});
}
}
I couldn't understand why they implemented like this. Yes, it reduces redundant INotifyPropertyChanged implemented object so it uses less resource. However, if I had to implement tons of properties to the VM, I'll have to write properties on both VM and DataItem, and also when I want to add or delete properties I'll have to edit both of them.
Couldn't I just hold a DataItem property in the VM? Or am I missing something?
DataItem simply represents the Model in this case. If the Model is an entity that cannot be modified (database auto-generated POCO), this scenario would work.
Yes, you will have to have each applicable Model property in your ViewModel so that it can RaisePropertyChanged, and yes, this is more 'work', but it provides an abstraction between the two.
Some people are okay with modifying the Model to have it implement INotiftyPropertyChanged, others believe the Model shouldn't and all the work should be done in the ViewModel (which is what is being done in this case).
Question: I have two viewmodels that share a service with a list. My question is how to setup the notification so that both viewmodels know when this list is changed. Description below and code of where i'm at.
I found this example HERE that looks right for what I'm trying to do, but I have a question regarding how to be notified in my viewmodels of a change in my service. I'll add some code I've mocked up to see if I'm on the right track. I'm using WPF/MVVM Light.
First part is a service with a interface that will have a list of data, in this example I'm using a string list. I want a property in both viewmodels to have access to this list of data, and be notified when it's changed. I think what's throwing me is the interface IOC into my viewmodels. I'm understanding more and more why this is good, but I'm still wrapping my mind around it and I'm not sure how to setup the notification when the list is changed in the service. If my service was not injected I might have setup a event or property that my viewmodel property would access get/set but injecting my service does not expose my public fields, just methods. This is new to me so it's very likely i'm not understanding this correctly or missing something.
I used a List in my service instead of a ObservableCollection based on some reading I've done warning against using the ObservableCollection here. Thanks you for any help.
public class MyService : IMyService
{
private List<string> myList = new List<string>();
public List<string> getMyList()
{
return this.myList;
}
public void setMyList(List<string> value)
{
this.myList = value;
}
public void addValue(string value)
{
this.myList.Add(value);
}
public void insertValue(int index, string value)
{
this.myList.Insert(index, value);
}
}
public class MyViewModelOne : ViewModelBase
{
private readonly IMyService myService;
public MyViewModelOne(IMyService myService)
{
this.myService = myService;
}
public List<string> MyProperty // control item source will bind to this
{
get
{
return this.myService.getSource();
}
}
public void setSomeValue(value)
{
this.myService.addValue(value);
}
}
public class MyViewModelTwo : ViewModelBase
{
private readonly IMyService myService;
public MyViewModelTwo(IMyService myService)
{
this.myService = myService;
}
public List<string> MyProperty // control item source will bind to this
{
get
{
return this.myService.getSource();
}
}
public void setSomeValue(value)
{
this.myService.addValue(value);
}
}
From what I understood about your problem, what you need essentially is that your INotifyPropertyChanged implementation at your service level and the list my list to be an ObservableCollection being injected from the service.
Now if there is a notification change it would be directly on the service and hence no explicit need of handling.
Your binding could look like "{Binding MyService.MyProperty}"
I got this working two different ways, I went with the first example because I think it's easier to follow in the code.
This came up because I had a control in my mainview with related code that was growing and I realized I wanted the same control/behavior in a separate view that would use the same data/control for a different purpose.
I did not want to duplicate this control/template/code in two places so I made it into a User Control. I then nest the user control in my views. The user control has it's own VM. The main view updates the service with new data, and the nested control listens on a event to know when there is new data.
Still very new to MVVM thinking so please feel free to point out in issues with either of these examples.
Example using a service with eventhandler.
public interface IMyInterface
{
event EventHandler OnSomeEvent;
void addSomeData(string value);
void getSomeData();
}
public class MyInterface: IMyInterface
{
public event EventHandler OnSomeEvent = delegate { };
public void addSomeData(string value)
{
// do stuff
OnSomeEvent();
}
public string getSomeData()
{
return "some data";
}
}
// Main ViewModel
public class ViewModelOne : ViewModelBase
{
IMyInterface myInterface;
public NotifyViewModel(IMyInterface myInterface)
{
this.myInterface = myInterface;
this.myInterface.OnItemSourceChanged += myInterface_OnSomeEvent;
}
void testEvent()
{
this.myInterface.addSomeData("test data");
}
}
// My nested user control
public class ViewModelTwo : ViewModelBase
{
IMyInterface myInterface;
public NotifyViewModel(IMyInterface myInterface)
{
this.myInterface = myInterface;
this.myInterface.OnItemSourceChanged += myInterface_OnSomeEvent;
}
void myInterface_OnSomeEvent(object sender, System.EventArgs e)
{
// do stuff
}
}
Example using MVVM Light Messaging
public class EventDataSource
{
public string MyItemSource { get; set; }
public EventDataSource()
{
MyItemSource = string.Empty;
}
}
// Message class
public class MyDataSourceMessage : MessageBase
{
public EventDataSource MyItemSource { get; set; }
public MyDataSourceMessage(EventDataSource myItemSource)
{
MyItemSource = myItemSource;
}
}
// Main ViewModel
public class ViewModelOne : ViewModelBase
{
public NotifyViewModel() {}
void testMessage()
{
EventDataSource msg = new EventDataSource() { MyItemSource = "magic message!"};
Messenger.Default.Send(new MyDataSourceMessage(msg as EventDataSource));
}
}
// My nested user control
public class ViewModelTwo : ViewModelBase
{
public NotifyViewModel()
{
Messenger.Default.Register<MyDataSourceMessage>(this, (action) => ReceiveMessage(action));
}
private ObservableCollection<string> myProperty = new ObservableCollection<string>();
public ObservableCollection<string> MyProperty
{
get { return myProperty; }
set
{
myProperty: = value;
RaisePropertyChanged(() => MyProperty);
}
}
void ReceiveMessage(MyDataSourceMessage action)
{
// do something with the data
MyProperty.Add(action.DGItemSource.ItemSource);
}
}
Ok let me try to shed some light on this. First of all, change notification is not meant to pass information between view models, it is meant to notify the view itself that the a property of the view model has changed.
There are a few methods for view models to issue change notifications to views:
INotifyPropertyChanged interface
INotifyCollectionChanged interface
A custom event with the name of the property suffixed with Changed (e.g. an event called MyPropChanged for a property called MyProp)
Having said all that, it is still possible for one view model to subscribe to the events generated by the above methods, and if you really need to, you may of course.
EDIT:
Check this link for a description on item number 3 above.
My goal is to do TwoWay binding off a generated EntityFramework model.
What is the best way to implement NotifyPropertyChanged on properties in a generated entity model?
For example, suppose I have this entity from a database:
public partial class Survey
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Answer { get; set; }
}
I then create a ViewModel...
public class SurveyViewModel : ViewModelBase
{
private Survey _survey = new Survey();
public Survey
{
get { return _survey; }
set
{
_survey = value;
}
}
}
How could I achieve 2 way binding other than writing dependency properties for every single property in the entity model, like so...
//below the declaration of the Survey entity in the viewmodel
public string FirstName
{
get { return Survey.FirstName; }
set
{
Survey.FirstName = value;
NotifyPropertyChanged("FirstName");
}
}
//This works but is very time consuming for large models
Let me know if I'm attempting this wrong...
PropertyChanged.Fody may be what you are looking for:
// Non-auto generated partial class declaration
[ImplementPropertyChanged]
public partial class Survey
{
}
As commented by TyCobb, this question has been asked repeatedly and the result remains the same... here is a summary.
While there are ways pollute your data models with UI accommodating
features such as INotifyPropertyChanged, the MVVM mantra teaches us
that it is the View-Model's job to interact with the UI and the
Data-Model should remain as pure as possible (POCO).
So what? How do we keep to MVVM but avoid the boiler-plate codes of exposing individual properties on the View-Model?
From experience, calling a RaisePropertyChanged is not reserved only for property setters but could be used to manually raise a property changed for a model that has had its own properties modified, thus, cause the UI to update.
Here is a code example...
public class SurveyViewModel : INotifyPropertyChanged
{
private Survey _survey;
public Survey Survey
{
get { return _survey; }
set
{
_survey = value;
RaisePropertyChanged(() => Survey);
}
}
public void ModifySurvey()
{
// Modify a property of the model.
Survey.FirstName = "Modified";
// Make other modifications here...
// Notify property changed
RaisePropertyChanged(() => Survey);
}
}
I have this class called BluetoothDeviceInfo from 32feet library to handle, obviously, Bluetooth related stuff.
I have created my program and it worked. However, I need to add some information to a "more complete", lets say, class which also carries GPS information.
So what I did was to created a new class MyDeviceInfo and derive it from BluetoothDeviceInfo as the code below.
Base* (which I don't have control on)
namespace InTheHand.Net.Sockets
{
public class BluetoothDeviceInfo : IComparable
{
public BluetoothDeviceInfo(BluetoothAddress address);
public bool Authenticated { get; }
public ClassOfDevice ClassOfDevice { get; }
public bool Connected { get; }
public BluetoothAddress DeviceAddress { get; }
public string DeviceName { get; set; }
.
.
.
Derive*
public class MyDeviceInfo : BluetoothDeviceInfo
{
private bool gpsSignal;
public MyDeviceInfo(BluetoothAddress address) : base(address)
{
gpsSignal = false;
}
#region Properties
public bool GpsSignal { get { return gpsSignal; } set { gpsSignal = value;}}
}
And I substituted all my BluetoothDeviceInfo types in my code with MyDeviceInfo types.
Everything seems working except this part.
This particular method called Client.DiscoverDevice() returns a list of Bluetooth devices available in the type BluetoothDeviceInfo. And since this is of a type base class, it doesn't make sense that I would cast it to a variable of my derive class MyDeviceInfo and I can't get past this point since I need the list of devices. Even though sounding wrong I tried to cast it using this code (which obviously didn't work)
IEnumerable<MyDeviceInfo> _discoveredCsaDevices = (IEnumerable<MyDeviceInfo>)cli.DiscoverDevices().Where(d => (d.DeviceName.StartsWith(...
Since this logic doesn't fit into inheritance I thought of composition. However, this isn't quite fit the composition line of thought such as object Bird can have flying functionality of type Airplane but not the whole lot cuz I actually need my driven class include all the functionality of BluetoothDeviceInfo.
I want to understand the big picture here and what would be approach to do such a thing.
Will be happy to hear you thoughts and guides on this.
Cheers
Try this:
IEnumerable<MyDeviceInfo> _discoveredCsaDevices = (cli.DiscoverDevices().Where(d => (d.DeviceName.StartsWith(...).Select (b => new MyDeviceInfo(b.DeviceAddress)).ToList();
You may need to provide a MyDeviceInfo constructor that takes a BluetoothDeviceInfo and copies all the properties.
Update
The code then becomes:
public class MyDeviceInfo : BluetoothDeviceInfo
{
private bool gpsSignal;
MyDeviceInfo(BluetoothDeviceInfo btInfo)
{
this.Authenticated = btInfo.Authenticated;
this.ClassOfDevice = btInfo.ClassOfDevice;
this.Connected = btInfo.Connected;
this.DeviceAddress = btInfo.DeviceAddress;
this.DeviceName = btInfo.DeviceName;
}
public MyDeviceInfo(string address) : base(address)
{
gpsSignal = false;
}
public bool GpsSignal { get { return gpsSignal; } set { gpsSignal = value;}}
}
And you use it like this:
IEnumerable<MyDeviceInfo> _discoveredCsaDevices = (cli.DiscoverDevices().Where(d => (d.DeviceName.StartsWith(...).Select (b => new MyDeviceInfo(b)).ToList();
Application note.
To the extent that inheritance works, use that. See Ned Stoyanov's answer. My answer below should only be used if the library class is sealed.
Problem #1 - Library class was sealed and wasn't dependent on an interface, therefore I cannot substitute it with my own class.
Solution: Make an interface anyway.
public interface IMyDeviceInfo : IComparable
{
BluetoothDeviceInfo(BluetoothAddress address);
bool Authenticated { get; }
ClassOfDevice ClassOfDevice { get; }
bool Connected { get; }
BluetoothAddress DeviceAddress { get; }
string DeviceName { get; set; }
...
}
Problem #2 - How can I stuff the library class under my interface?
Solution: Make an adapter.
public class DeviceInfoAdapter : IMyDeviceInfo
{
private BluetoothDeviceInfo m_theRealStuff;
// Allow yourself to bypass the abstraction to
// get down to the real object, because it may
// just be unavoidable in some cases.
// You may also mark it [Obsolete] or comment it out
// until you encounter a real need for it.
internal BluetoothDeviceInfo TheRealStuff
{
get { return m_theRealStuff; }
}
// Constructor. If the real stuff has been created by someone else.
public DeviceInfoAdapter(BluetoothDeviceInfo theRealStuff)
{
m_theRealStuff = theRealStuff;
}
// Constructor. A knock-off copy of the real stuff constructor.
public DeviceInfoAdapter(BluetoothAddress address)
{
m_theRealStuff = new BluetoothDeviceInfo(address);
}
// Imitate all properties and methods on the real stuff.
public bool Authenticated
{
get
{
return m_theRealStuff.Authenticated;
}
}
// ...
// Basically, for every publicly-accessible method or property,
// you just call the real stuff.
}
Problem #3 - How do I add extra properties to it?
Solution: Make a decorator.
public interface IMyDeviceInfoExtra : IMyDeviceInfo
{
bool GpsSignal { get; set; }
}
public class MyDeviceInfoWithGps : IMyDeviceInfoExtra
{
private IMyDeviceInfo m_theRealStuff;
private bool m_gpsSignal;
public MyDeviceInfoWithGps(IMyDeviceInfo theRealStuff)
{
m_theRealStuff = theRealStuff;
}
// the same thing again ... lots of code duplications
// The only new member here
public bool GpsSignal
{
get { return m_gpsSignal; }
set { m_gpsSignal = value; }
}
}
I recently wrote two classes and an interface as a way to implement the answer to this question of mine.
The first class is the Notifier generic class:
public interface INotifier { }
public class Notifier<T> : Observable,INotifier where T:new()
{
public Notifier()
{
V = new T();
}
private T _ValueBacker;
public T V
{
get { return _ValueBacker; }
set
{
_ValueBacker = value;
OnPropertyChanged(() => V);
}
}
}
The Observable base class here is just a class that implements INotifyPropertyChanged and defines an OnPropertyChanged method.
Thanks to that class, I can now define a Silverlight/WPF ViewModel like this:
public class Person : ViewModelBase
{
Notifier<string> Name {get;set;}
Notifier<string> Surname {get;set;}
Notifier<int> Age {get;set;}
}
instead of:
public class Person : Observable
{
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
_Name=value;
OnPropertyChanger(()=>Name);
}
}
privaate string _Surname;
public string Surname
{
get
{
return _Surname;
}
set
{
_Surname=value;
OnPropertyChanger(()=>Surname);
}
}
private int _Age;
public int Age
{
get
{
return _Age;
}
set
{
_Age=value;
OnPropertyChanger(()=>Age);
}
}
}
As you can see, the new code is much more concise and much less coding-error (or typo) prone. All I have to do in my XAML is to bind to "MyPerson.V" instead of "MyPerson". However, since there aren't any ways to implement initializers for autoproperties, I had to initialize every property in the constructor. In some cases, I skipped the initializers and that led to some runtime errors. So, to take care of that, in the constructor of the ViewModelBase class, I added this loop:
public ViewModelBase()
{
foreach(var notifierProperty in this.GetType().GetProperties().Where(c=>c.PropertyType.GetInterfaces().Any(d=>d==typeof(INotifier))))
{
notifierProperty.SetValue(this, notifierProperty.PropertyType.GetConstructor(System.Type.EmptyTypes).Invoke(null), null);
}
}
What this does is, whenever you instantiate a ViewModelBase derived class, the constructor loops through the properties, and invokes the constructor for each Notifier type property.
Is this evil? Will using reflection this way come back to haunt me in the future? Are there any performance hits I should be aware of?
I think that's fine. I have some bits of information to add:
You can create types with trivial constructors by calling Activator.Create(myType), which means you don't have to fetch a constructor.
I believe at least for Silverlight, all properties initialized with your hack need to be public.
There is a library called ReactiveProperty, that defines a class ReactiveProperty<T> very similar to your Notifier<T>.
You will bind against it's Value property:
public class ReactiveProperty<T> : IObservable<T>, IDisposable, INotifyPropertyChanged, IReactiveProperty, INotifyDataErrorInfo
{
public T Value
{
get { return latestValue; }
set { anotherTrigger.OnNext(value); }
}
// ...
}
The call in the setter eventually leads to the respective call to INotifyPropertyChanged.PropertyChanged.
ReactiveProperty<T> also is an observable in the sense of reactive extensions, on which the library depends. Other than that, the author basically does what you do, but without the initialization hack in the constructor.