I recently found the SimpleMVVM toolkit and am trying to create a small example program. I am trying to create a CurrentViewModel parameter like so:
private ViewModelBase<> _CurrentViewModel;
public ViewModelBase<> CurrentViewModel
{
get { return _CurrentViewModel; }
set
{
_CurrentViewModel= value;
NotifyPropertyChanged(m => m.CurrentViewModel);
}
}
Any object referenced by CurrentViewModel will extend the SimpleMVVM ViewModelBase class like so:
public class HomeViewModel : ViewModelBase<HomeViewModel>
{ }
The problem I am having is that SimpleMVVM ViewModelBase requires a type T as an argument and I don't know how to create the parameter CurrentViewModel such that it can accept any ViewModel extending ViewModelBase.
One of the issues around using Generics '<T>' is that any consumer has to still know the type. If you consider adding an ICollection to your model, you have to know what it is a collection of so that you maintain strong typing.
The only exception is if you define a class which is itself generic, which can then pass on it#s type property to a child class. i.e.
CustomCollection<T>
{
ICollection<T> _foo;
}
To do what you're trying to do will require a seperate common interface that encapsulates the functionality you want from CommonViewModel.
Related
I have WPF Project with Prism 7.1, Unity DI 5.8.11 and .NET framework 4.7
I have BaseViewModel which all the ViewModel classes will inherit from
public abstract class BaseViewModel : BindableBase
{
[Dependency]
protected IEventAggregator EventAggregator { get; set; }
// just default constructor, no other constructor is needed
}
and here is an example of one of the ViewModel class
public class TablesViewModel : BaseViewModel
{
public TablesViewModel()
{
EventAggregator.GetEvent<OperatorChangedEvent>().Subscribe(.....);
}
}
and the register of the type as the following
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IEventAggregator>(new EventAggregator());
}
Now, what happened is the following: First the constructor of the BaseViewModel is called then the constructor of the TablesViewModel then the Dependency property is set by the Unity DI, this is a logical sequence of events, but it does not fit for me.
the constructor of the TablesViewModel is giving a null reference Exception because the EventAggregator property is still null.
I do not want to use the constructor dependency injection, this will force me to create a lot of non-default constructor for all the ViewModel classes.
and at the same time, I need to subscribe to the EventAggregator at the constructor ( because there is no other good place to do that if there is please told me).
How can I solve this
I do not want to use the constructor dependency injection, this will force me to create a lot of non-default constructor for all the ViewModel classes.
You want non-default constructors for classes that have dependencies. That's what dependency injection is all about: the types tell the user through their constructor parameters what he has to give them to operate.
There are plenty of ways to create view models with non-default constructors, e.g. Prism's ViewModelLocator or Unity's automatic factories. You do not want to resort to using the ServiceLocator unless it is absolutely necessary, but an evil person technically could do something like this:
public abstract class BaseViewModel : BindableBase
{
protected IEventAggregator EventAggregator { get; } = ServiceLocator.Current.GetInstance<IEventAggregator>();
}
A property cannot be set before an instance of the class has been created, i.e. the EventAggregator property won't ever be set before the constructor has been executed.
If you need to access the event aggregator in the constructor, you should either use constructor dependency injection or retrieve the event aggregator from some static property.
I apologize for the lengthy question, but I feel like it is necessary to include all of this information.
Until now, I've been using a possibly-unorthodox way of adding UserControls to my applications. Let's say I have a UserControl called Diagnostics that has a button, that when clicked, performs a function that is specific to the application that owns it. For example, if I drop Diagnostics into AppA, I want it to display "A", and if I drop it into AppB, I want AppB to define the behavior so it displays "B".
I typically implement this via a callback interface that is passed to the UserControl's constructor, which is pretty straightforward. Here's some sample "code" that probably won't compile, but is presented just to clarify what I've basically done before, and what I am trying to do:
public interface IDiagnosticsCallback {
void DisplayDiagnostics(); // implemented by owner of Diagnostics UserControl
}
public class MyApp : IDiagnosticsCallback {
public void DisplayDiagnostics() {
MessageBox.Show("Diagnostics displayed specifically for MyApp here");
}
}
public Diagnostics : UserControl {
private IDiagnosticsCallback _callback { get; private set; }
public Diagnostics(IDiagnosticsCallback callback) {
_callback = callback;
}
public void ShowDiagnostics_Click(object sender, EventArgs e) {
_callback.DisplayDiagnostics();
}
}
The problem I had in the past was understanding how to declare a UserControl that takes a parameter in its constructor (i.e. doesn't have a default constructor) in XAML, and apparently you can't. I worked around this with a fairly-inelegant method -- I would give the main panel a name in XAML, and then from code-behind I would create Diagnostics, passing it the necessary callback, and then I would add Diagnostics to the panel's list of children. Gross and violates usage of MVVM, but it works.
This weekend, I decided to try to learn how to do it for a class and a TextBox, and it turns out that all I had to do was to create a DependencyProperty in my UserControl and use databinding. It looks something like this:
public ClassA
{
public void ShowSomethingSpecial()
{
MessageBox.Show("Watch me dance!");
}
}
public MyApp
{
public ClassA Foo { get; set; }
}
public Diagnostics : UserControl
{
public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register("Something", typeof(ClassA), typeof(Diagnostics), new PropertyMetadata());
public ClassA Something
{
get { return (MyApp)GetValue(SomethingProperty); }
set { SetValue(SomethingProperty, value); }
}
// now uses default constructor
public void ShowSomethingSpecial_Click(object sender, EventArgs e)
{
Something.ShowSomethingSpecial();
}
}
MyApp.xaml
<diags:Diagnostics Something="{Binding Foo}" />
So Foo is a property of MyApp, which is databound to the Something DependencyProperty of Diagnostics. When I click the button in the UserControl, the behavior is defined by ClassA. Much better, and works with MVVM!
What I'd like to do now is to go one step further and instead pass a callback interface to my UserControl so that it can get the states of my digital inputs and outputs. I'm looking for something like this:
public Diagnostics : UserControl
{
public interface IDioCallback
{
short ReadInputs();
short ReadOutputs();
void SetOutput( char bit);
}
public IDioCallback DioCallbackInterface {
get { return (IDioCallback)GetValue(DioCallbackInterfaceProperty); }
set { SetValue(DioCallbackInterfaceProperty,value); }
}
// Using a DependencyProperty as the backing store for DioCallbackInterface. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DioCallbackInterfaceProperty = DependencyProperty.Register("DioCallbackInterface",typeof(IDioCallback),typeof(Diagnostics),new PropertyMetadata(0)); // PropertyMetadata is the problem...
}
public class DIO : IDioCallback
{
public short ReadInputs() { return 0; }
public short ReadOutputs() { return 0; }
public void SetOutput( char bit) {}
}
public class MyApp
{
public DIO MyDIO { get; set; }
}
MyApp.xaml
<diags:Diagnostics DioCallbackInterface="{Binding MyDIO}" />
While my code (maybe not the exact code above, but my real project) does compile successfully, it appears that the PropertyMetadata passed to Register is at fault. I get an exception that says "Default value type does not match type of property 'DioCallbackInterface'."
Am I doing something really unorthodox, or is this approach to databinding interfaces actually possible? If not, what are the recommended ways of defining how a UserControl behaves based on the application it's being used in?
The exception you have mentioned because of this:
new PropertyMetadata(0)
You have passed 0 (of type Int32) instead of the null or whatever you like for your interface: IDioCallback.
I cannot say that the way you select is wrong, but you should keep in mind that every user of your UserControl must implement that interface you have defined. If you have several properties that you would like to pass to the UserControl, you can basically discard them via DependencyProperty.
In your case you also would like to inject some logic to the UserControl Button. Let me suppose that this control has only one button. MVVM-way to handle Button.Click event is done via ICommand - you can declare the command property in your ViewModel and use it as data source for data binding in your UserControl as DependencyProperty, passing it properly to the Button.
Also you can have an agreement with all of your data context, and use special name for that property. For example:
public interface IViewModelWithCommand
{
public ICommand TheCommand { get; }
}
Implement it for each data context you need, and use TheCommand property name inside your data template of your UserControl. In the code-behind you can create type validation of DataContext passed to your UserControl, and throw an exception in case the type is not implements your interface
Here several articles you probably should be interested in:
RelayCommand
Commands, RelayCommands and EventToCommand
How to use RelayCommands
Using RelayCommand will simplify your life because you don't need to re-implement interface for every command, instead, you need to pass valid action that you want.
My WPF application follows the MVVM pattern. There are three views:
MainWindow
LoginView
ProjectsView
LoginView and ProjectsView are user controls imported by the MainWindow. Both views have their view model assigned. LoginViewModel defines a property ProjectList which is set by calling a webservice. Now LoginViewModel needs access to the ProjectList property and others.
I am aware that one solution might be a redesign so that there is only one view and one view model. I would do that as a backup solution but I would favor not to do so.
How should this be done? Should I use some kind of EventAggregator like in Prism? Or are there other ways to do this?
So if i understood clearly, ProjectList property should be accessed from both 'LoginViewModel' and 'ProjectsViewModel'. I'd try to implement it in the 'MainViewModel' so child viewmodels can access it in a natural way.
An IEventAggregator is like a box in which you can add events, or find and subscribe to one, so i would say it's not what you need.
Anyway, you could register your custom interface (box type) in the UnitySingleton.Container, which would expose ProjectList for it to be accessible everywhere. This approach makes a lot of sense when modules, which are separate assemblies, need to communicate whith each other.
If this is overkill or not in your case is something you should decide, i'd personally go with the 'put it in the mainviewmodel' option.
-- Sample -- (not tested)
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
LoginVM = new LoginViewModel(this);
ProjectsVM = new ProjectsViewModel(this);
RetrieveProjectList();
}
public LoginViewModel LoginVM { get; private set; }
public ProjectsViewModel ProjectsVM { get; private set; }
public object ProjectList { get; private set; }
private void RetrieveProjectList()
{
ProjectList = ....
}
}
It's pretty simple as you see, LoginVM and ProjectsVM will hold a reference to the MainViewModel that created them, therefore giving them access to ProjectList.
How should this be done? Should I use some kind of EventAggregator
like in Prism? Or are there other ways to do this?
Here are a few ideas:
You can create a view-model class that both view-models
inherit from. This base class will contain the shared properties.
Create a static class that contains the shared properties.
Using dependency injection, create a class that contains the
properties, register it as a singleton in your container and inject
it into your view-model's ctors.
Also, I believe that the EventAggregator is best suited for communicating between modules/assemblies. In your example, it seems like everything is in the same assembly.
I have a usercontrol where I need a list of a simple class I've made, called Person:
public class Person {
public string Name { get; set; }
}
Now in the usercontrol, I need to have a ObservableCollection<Person> that I can bind to. So I figure I need to make it a dependency property. So in the usercontrol I have the following:
public static readonly DependencyProperty PersonsDependencyProperty =
DependencyProperty.Register("Persons", typeof(ObservableCollection<Person>),
typeof(PersonUserControl));
And the property is like this:
public ObservableCollection<Person> Persons
{
get
{
return (ObservableCollection<Person>)GetValue(PersonsDependencyProperty);
}
set
{
SetValue(PersonsDependencyProperty, value);
}
}
Now in my MainWindow.xaml codebehind I make an ObservableCollection<Person> called PersonList, set the mainwindow datacontext to self, and bind to it like so:
<Local:PersonUserControl Persons="{Binding PersonList}">
</Local:PersonUserControl>
And I get the error: Exception has been thrown by the target of an invocation. - No further explanation. Can anybody tell me how to react to it or what I'm doing wrong.
I hope I'm being clear enough.
PersonsDependencyProperty should be just PersonsProperty. Hard to say whether that's the underlying cause without more information, but that's certainly a problem. WPF appends "Property" onto the binding path in order to find the related dependency property. Thus, it won't find yours.
There is an interface:
public interface IFoo {
}
A Silverlight user control has a collection of IFoo instances:
public ObservableCollection<IFoo> Items { get; set; }
There is an abstract class that implements the interface:
abstract public class Foo : IFoo {}
And a class that further derives from that:
public class DerivedFoo : Foo {}
With all of that said, I'm trying to add instances of DerivedFoo into the control's collection via XAML, but I receive an error that DerivedFoo is not of type IFoo and cannot be used in the generic collection.
I did find a post in a forum that said this was a bug in Silverlight 3 but would be fixed (I am using Silverlight 4). Is this still a bug or am I going about this incorrectly?
Update:
My code is at home and I'm at work so I can't post the actual XAML, but from memory it was along the lines of:
<my:Thing>
<my:Thing.Items>
<my:DerivedFoo ... />
</my:Thing.Items>
</my:Thing>
The answer is...
The CollectionChanged event handler for the generic collection made an improper cast during the Add action.