I'm trying to expose the ViewModel as a static resource on the page so that it can be easily accessible by the binding.
TestViewModel.cs
namespace Test.WPFUI.Home
{
public class TestViewModel....
HelloWorldView.Xaml
xmlns:local="clr-namespace:Test.WPFUI.Home"
<UserControl.Resources>
<local:TestViewModel x:Key="mainPageViewModel" />
</UserControl.Resources>
TestViewModel Can't be found. May I ask for some tips or suggestions Please.
Getting help from http://www.telerik.com/help/silverlight/gridview-troubleshooting-blank-cells.html
public class LoanViewModel : ScreenViewModelBase<LoanViewModel>, IRecord, INotifyPropertyChanged
{
public LoanViewModel(IEventAggregator events) .............
It sounds like your initial problem was not having the full xmlns definition. You usually need both the namespace and assembly.
The easiest way to get it right, in my experience, is to let intellisense do it for you. Just start typing the namespace you want, and as long as its in a referenced project, there will be an autocomplete option.
Your second problem is due to not having a default constructor. You wrote this:
<local:TestViewModel x:Key="mainPageViewModel" />
Which will invoke the default constructor. However, you define a constructor here:
public LoanViewModel(IEventAggregator events) .............
Which removes the provided (paramaterless) default constructor. I'm going to take a wild guess and say that creating the correct IEventAggregator is not simple or desired from XAML, so I see two choices:
You didn't really need that parameter in the constructor. Simply add a default constructor to your view model and you are good to go!
You really need that parameter, so instantiating from XAML just isn't a good idea. Pass in your view model from somewhere else on the view's constructor.
If you feel like you can instantiate the correct object from XAML, use this post to invoke the paramaterized constructor: Calling a parameterized constructor from XAML
In my opinion, putting truly regular classes into XAML is not a good pattern to follow, so I wouldn't. By regular, I mean not related at all to the view.
Related
I'm trying to connect multiple Viewmodels to a single View using caliburn micro. I found some solution but all seem quite heavy for something that seems to me a quite standard way of doing things.
The reason for being that is the following :
I'm implementing some views and their default ViewModels in an assembly. lets call the assembly HMI.Base with two classes
HMI.Base.ViewModels.BaseViewModel
HMI.Base.Views.BaseViewModel.
I'm then using this view from another assembly. lets call the assembly HMI.ConcreteApp
Usage is quite straightforward and overriding SelectAssemblies() does the trick so the view can be easily located.
Issues are rising when a ViewModel needs to get its data from a specific source. The solution that come to my mind is to extend my ViewModel and Add an element in its constructor being the source of my data and then do the necessary to link those data to the base viewmodel Properties.
So I create a class
HMI.Concrete.ViewModels.CustomViewModel
The implementation looks like this:
using HMI.Base.ViewModels;
public class CustomViewModel : BaseViewModel
{
public CustomViewModel (IConfiguration config, ILoggerFactory loggerFactory, IEventAggregator eventAggregator, DataSourceXYZ data) : base(config, loggerFactory, eventAggregator)
{
Logger.LogInformation("Constructing custom viewmodel that will display as HMI.Base.Views.BaseViewModel");
}
However the name has now changed, so the view cannot be located automatically. Is there a way to simply and clearly say "Use the base class view if no specific view exist with the same name" in the CustomViewModel ?
Without having to do some more fiddling in the bootstrapper, which seems bad to me (except if it isn't ) because all needed extra information are there (we know the base viewmodel why not use the base view to display the data)
I will provide already an answer for the best solution I found even if I would like better:
The solution is at this page
In short :
A custom attribute can be created to indicate the baseViewModel.
The Viewlocator can be changed in the Bootstraper to take it into consideration
And an extra step not in this article to avoid writing it in every application:
create A CustomBaseBoostrapper to write it only once and use it instead of BootstrapperBase.
It is not too bad, but I'm mainly wondering if there is not better and without changing the viewlocator.
I'm looking for the right approach to declare design-time ViewModel for an Avalonia window.
Some samples suggest
d:DataContext="{d:DesignInstance viewModels:LoginViewModel, IsDesignTimeCreatable=True}"
This throws
XamlParseException at 5:5: Unable to resolve type DesignInstance from namespace http://schemas.microsoft.com/expression/blend/2008
Default Avalonia MVVM template suggests
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
If the ViewModel takes parameters, it throws
XamlLoadException at 16:10: Unable to find public constructor for type Demo.CloseNonModalDialog:Demo.CloseNonModalDialog.CurrentTimeDialogViewModel()
I guess adding a default parameter-less constructor is an option.
With MvvmLight/WPF, I used to reference the ViewLocator as a static resource
DataContext="{Binding Source={StaticResource Locator}, Path=MainWindow}"
That's an option, although I haven't yet found the right way to declare and reference the resource.
What is the recommended approach here? If I want to show design-time data, I'd say only the 3rd option would work. Which is not the option shown in samples.
Unable to find public constructor for type Demo.CloseNonModalDialog:Demo.CloseNonModalDialog.CurrentTimeDialogViewModel()
You can specify arguments via x:Arguments XAML directive, see https://learn.microsoft.com/en-us/dotnet/desktop/xaml-services/xarguments-directive
That's an option, although I haven't yet found the right way to declare and reference the resource.
I'd suggest to declare DesignData class and use x:Static, it will give you way more flexibility. e. g.
class DesignData
{
public MyViewModel MyViewModel => new MyViewModel(...);
}
d:DataContext="{x:Static local:DesignData.MyViewModel}"
View model creation would also not happen during the normal app execution unlike the StaticResource approach.
I have a MainViewModel, which features PersonViewModel and a HouseViewModel as properties. HouseViewModel has the property GetRooms. What is the best way to access this property from the PersonViewModel?
My solution at the minute is to pass through an instance of MainViewModel to PersonViewModel, then I can call MainViewModel.HouseViewModel.GetRooms. However, this seems a little wasteful.
I am happy to pass a function as a delegate, but I can't seem to do this with a Property. I have searched for an example of this and only come up with overly complicated techniques. I'm assuming there must be a simple way of doing this, as it seems like a common problem. Can anyone point out a strong example?
Or is there another, alternative method that I haven't considered?
If a method has to be shared across two viewmodel, it should be defined in base viewmodel or a service. The best way is a common Service class should hold all common methods like GetRooms, CheckIn, CheckOut, etc. And this service should be provided to every viewmodel using Dependency Injection.
public class HomeViewModel
{
public HomeViewModel(IRoomService roomservice)
{
}
}
public class PersonViewModel
{
public PersonViewModel(IRoomService roomservice)
{
}
}
I have a UserControl I've created which imports several parts using the [Import] attribute.
public class MyUserControl : UserControl, IPartImportsSatisfiedNotification
{
[Import]
public IService Service { get; set; }
public MyUserControl()
{
}
public void OnImportsSatisfied()
{
// Do something with Service.
}
}
This UserControl is instantiated from XAML, so its imports aren't being satisfied and OnImportsSatisfied isn't being called.
<local:MyUserControl />
My question is how can I satisfy my class's imports when it's being created in XAML.
From MSDN:
To be instantiated as an object element in XAML, a custom class must
meet the following requirements:
The custom class must be public and must expose a default (parameterless) public constructor. (See following section for notes
regarding structures.)
The custom class must not be a nested class. The extra "dot" in the full-name path makes the class-namespace division ambiguous, and
interferes with other XAML features such as attached properties.
If an object can be instantiated as an object element, the created object
can fill the property element form of any properties that take the
object as their underlying type.
You can still provide object values
for types that do not meet these criteria, if you enable a value
converter. For more information, see Type Converters and Markup
Extensions for XAML.
From there, you have two choices:
1) Using a TypeConverter:
Using a type converter will allow you to instantiate an object without a parameterless constructor, but you will have to provide a TypeConverter that will do the instantiation.
Now, I never had to use it, I cannot help you further with that.
2) Retrieve IService using the ServiceLocator:
public class MyUserControl : UserControl
{
public IService Service { get; set; }
public MyUserControl()
{
Service = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IService>();
// You can do something with Service here already.
}
}
I realize it is a change in the design of your class, but hopefully you can cope with it.
Hope this helps,
Bab.
if you did not want mef to create your usercontrol, you have to use the compositioncontainer in your usercontrol and call GetExport direct. but then you have the problem to get the instance of your compositioncontainer :)
ps: i let mef create my wpf views in my applications.
(I'm resurrecting this in case anyone comes across it. As a disclaimer, I'm no expert and these are just solutions I found to be working.)
I found that calling CompositionContainer.ComposeParts(myUserControl) works. I call this on the control's constructor. You'll need to get a reference to the CompositionContainer somehow:
public MyUserControl()
{
compositionContainer.ComposeParts(this);
}
Additional solution:
This is probably unnecessary, but here's another way. This is far more convoluted but it does allow you to "Import" your usercontrol in XAML.
To have your imports satisfied, MyUserControl needs to be exported and then instantiated by MEF. My solution was to have static field in a class that holds a "Locator" object. This Locator object is responsible for importing and returning exported objects. Then I could refer to this static field in XAML, like so:
<ContentControl Content="{Binding MyUserControl, Source={x:Static v:SomeClass.Locator}}">
SomeClass has a static property called Locator which gets assigned early in the application's life cycle. The Locator could then have a MyUserControl property that gets Imported.
(Disclaimer: the links below are to my own framework and the solution, being as crude as it is, if used should be used with care.)
To provide an example of the above, I'll explain how I implemented it in my framework:
In my case, SomeClass is a subclass of System.Windows.Application that replaces App.xaml, and ViewLocator is assigned on its OnStartup, as can be seen here.
The ViewLocator class is a System.Dynamic.DynamicObject that imports views, which have a custom ViewExport attribute. Views are identified using the ViewExportAttribute.Alias property.
This is an example of a view being exported and being assigned an alias.
Finally, the MEF instantiated instance of the view can be used in XAML as follows:
<ContentControl Content="{Binding HomeView, Source={x:Static v:FrameworkApp.ViewLocator}}">
When writing a MVVM WPF app, there's always a point where the view model has to be set to as the data context of the view. For me, usually that's in code. But I realized that if I declare the view model as a static resource inside the xaml and set the binding there, I don't need to do it in code anymore. This means I don't have to coordinate the view and the viewmodel in a third class somewhere, like in the App.
Is it acceptable to do this?
Thanks!
I'd say so. It sort of implies specific knowledge of the ViewModel from the View, but you have to set it somehow and I like the codebehindless approach here.
If you are using dependency injection this would not be appropriate, but if you aren't I'd stick with this approach.
Acceptable, yes, but if you are using PRISM, or DI of any sort, then it would make more sense to resolve it from the container and then set the datacontext either in code, or using a markup extension, depending on your exact solution.
If you want to use Dependency Injection (DI) in the View-First approach try ViewModel locator pattern:
public static class ViewModelLocator
{
public static MainWindowViewModel MainWindowViewModel
{
get
{
return ObjectFactory.GetInstance<MainWindowViewModel>();
}
}
};
and WPF code:
<Window
...
DataContext="{x:Static Services:ViewModelLocator.MainWindowViewModel}"
>