I tried to use MVVMLight in our Windows 10 Universal app, but it seems like that it totally can't work. I've seen this blog
Nuget downloaded and added a reference to the MVVM Light assemblies
Nuget also added the ViewModelLocator in the Application.Resources.
Can't see the Locator in Application.Resources
You need to create the ViewModelLocator manually, please follow these steps:
Create a new Windows 10 Universal app, for example: MVVMLightUWPApp1
Add reference to MVVMLight using NuGet Package Manager
Add a folder for your UWP app, for example: ViewModel
Under the ViewModel folder, add two classes: MainViewModel and ViewModelLocator
In MainViewModel.cs:
namespace MVVMLightUWPApp1.ViewModel
{
public class MainViewModel
{
public string MSG { get; set; }
public MainViewModel()
{
MSG = "Test Message";
}
}
}
In ViewModelLocator.cs:
namespace MVVMLightUWPApp1.ViewModel
{
public class ViewModelLocator
{/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
////if (ViewModelBase.IsInDesignModeStatic)
////{
//// // Create design time view services and models
//// SimpleIoc.Default.Register<IDataService, DesignDataService>();
////}
////else
////{
//// // Create run time view services and models
//// SimpleIoc.Default.Register<IDataService, DataService>();
////}
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
}
In App.xaml:
<Application.Resources>
<vm:ViewModelLocator xmlns:vm="using:MVVMLightUWPApp1.ViewModel"
x:Key="Locator" />
</Application.Resources>
In the View, set DataContext as below:
DataContext="{Binding Main, Source={StaticResource Locator}}"
Now, you can set binding to VM, for example:
<TextBlock Text="{Binding MSG}" FontSize="50" />
Enjoy it:)
Related
I was using CastleWindsor in my ASP.NETCore2.2 WebAPI project and was working fine. I'm migrating to ASP.NETCore3.1 now and it doesn't look like CastleWindor has offical support for that so I decided to move to Autofac with minimal changes but having some issues resolving the dependencies.
In my project, I've maintained very loose coupling between different layers in the application namely, business layer, data layer, and translation layer. All of those layers are in their own assemblies. And then in my main project, I've a folder say "dependencies" which will hold all the DLLs of differnet layers. Additionally, I've a separate project that lists all the interfaces that are implemented by the different layers and which needs to be resolved by the IoC container.
The project having all the interfaces looks like this:
namespace Shared.Interfaces
{
public interface IBusinessLayer<T>
{
....
}
public interface IDataLayer<T>
{
....
}
public interface ITranslationLayer<T>
{
....
}
}
The implementing projects looks like this:
namespace POC.Person.BusinessLayer
{
public class BusinessLayer<T> : IBusinessLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.DataLayer
{
public class DataLayer<T> : IDataLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.TranslationLayer
{
public class TranslationLayer<T> : ITranslationLayer<T> where T : Models.Person
{
...
}
}
Using Autofac in my migrated .netcore3.1 project, Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//and other codes
}
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new DependencyResolver());
}
DependencyResolver is a class that inherits from Autofac.Module, which is again in a separate assembly in different project which looks like this:
namespace IOC.Autofac
{
public class DependencyResolver: Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
// get our path to dependencies folder in the main project
var path = Directory.GetCurrentDirectory() + "\\dependencies\\";
//get all the assemblies inside that folder
List<Assembly> assemblies = new List<Assembly>();
foreach (string assemblyPath in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories))
{
var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
assemblies.Add(assembly);
}
// Register and resolve the types with the container
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsClosedTypesOf(typeof(IBusinessLayer<>))
.AsClosedTypesOf(typeof(IDataLayer<>))
.AsClosedTypesOf(typeof(ITranslationLayer<>))
.AsImplementedInterfaces()
.InstancePerRequest();
}
}
}
I'm getting this error and I've not been able to fix it:
":"Unable to resolve service for type 'Shared.Interfaces.IBusinessLayer`1[Models.Person]' while attempting to activate 'POC.Person.Controllers.PersonController'.","
Inside my controller I've injection which looks like this:
namespace POC.Person.Controllers
{
public class PersonController : ControllerBase
{
private readonly IBusinessLayer<Models.Person> _bl;
public PersonController(IBusinessLayer<Models.Person> bl)
{
_bl = bl;
}
//other codes
}
}
Program.cs looks like this:
namespace POC.Person
{
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
host.Build().Run();
}
public static IHostBuilder BuildWebHost(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel()
.UseStartup<Startup>()
.UseIIS()
.UseIISIntegration();
;
}).ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
});
}
}
}
It looks like with autofac involving generics, registering and resolving the type is not that straight forward?
Autofac does not currently support registering open generics whilst assembly scanning. It's a long-running known issue. You can do assembly scanning, you can register open generics, you can't do both at the same time. There are some ideas in that linked issue on ways some folks have solved it.
Out of the box, the scanning logic would, thus, be reduced to:
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsImplementedInterfaces()
.InstancePerRequest();
You need to register generics separately, like:
builder
.RegisterGeneric(typeof(TranslationLayer<>))
.As(typeof(ITranslationLayer<>));
I am new to Xarmain Forms programming and try to follow the MVVM model. However, I am not sure if I am doing it correctly. The program I write works, but
I would like some experts' opinions on whether I am doing it right or not in terms of MVVM.
I see a lot of example having the OnPropertyChanged somewhere in the programs. Do I need it somewhere in my program?
Any way to simplify?
My program read the text file in the system pathe and the text file contains some URLs and my XAML will display what the URL points to.
+++++++++++++++++++++++++++++++++++++++++++++++++
Folder name: Model
Class name: ListViewNewsItem.cs
namespace SCAC.Models
{
public class ListViewNewsItem
{
public string NewsLink { get; internal set; }
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++
Folder Name: ViewModels
Class Name: ListViewNewsViewModel.cs
namespace SCAC.ViewModels
{
public class ListViewNewsViewModel : ViewModel
{
private ObservableCollection<ListViewNewsItem> newsItem;
private static string documentPath = ReturnDocumentPath(); // Get the system path
public ListViewNewsViewModel()
{
GenerateNews(); // code below
}
public ObservableCollection<ListViewNewsItem> NewsItem
{
get { return newsItem; }
set
{
this.newsItem = value;
}
}
public void GenerateNews()
{
// File
string line;
string toReadFile = Path.Combine(documentPath + "Images.txt");
NewsItem = new ObservableCollection<ListViewNewsItem>();
StreamReader toRead = new StreamReader(toReadFile);
while ((line = toRead.ReadLine()) != null)
{
Console.WriteLine("This is the url: " + line);
var newsDetail = new ListViewNewsItem()
{
NewsLink = line
};
NewsItem.Add(newsDetail);
}
toRead.Close();
}
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Folder Name: Views
XAML file Name: NewsView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:localvm="clr-namespace:SCAC.ViewModels"
mc:Ignorable="d"
x:Class="SCAC.Views.NewsView" >
<!-- ontentPage.BindingContext>
<localvm:ListViewNewsViewModel />
</ -->
<ContentPage.Content>
<ScrollView>
<StackLayout BindableLayout.ItemsSource="{Binding NewsItem}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Frame Padding="10, 10, 10, 10" HeightRequest="200">
<Image Source="{Binding ImageLink}" Aspect="Fill"></Image>
</Frame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Code behind: NewsView.xaml.cs
namespace SCAC.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NewsView : ContentPage
{
public NewsView()
{
InitializeComponent();
BindingContext = new ListViewNewsViewModel();
}
}
}
I think you did a quite good job in MVVM structure. Get the data in the ViewModel, set the binding in the Xaml, set the right bindingContext to contect View and ViewModel. Everything looks well so far.
The Model-View-ViewModel (MVVM) architectural pattern was invented
with XAML in mind. The pattern enforces a separation between three
software layers — the XAML user interface, called the View; the
underlying data, called the Model; and an intermediary between the
View and the Model, called the ViewModel. The View and the ViewModel
are often connected through data bindings defined in the XAML file.
The BindingContext for the View is usually an instance of the
ViewModel.
ViewModels generally implement the INotifyPropertyChanged interface, which means that the class fires a PropertyChanged event whenever one of its properties changes.
You does not need to implement the INotifyPropertyChanged interface because there is only one property in your ViewModel: ObservableCollection<ListViewNewsItem> NewsItem.
ObservableCollection implements the INotifyCollectionChanged interface by default so you don't have to implement the INotifyPropertyChanged interface in ViewModel to notify the data changes.
If you have a simple property like public DateTime DateTime, then you usually need to write it like:
class ClockViewModel : INotifyPropertyChanged
{
DateTime dateTime;
public event PropertyChangedEventHandler PropertyChanged;
public DateTime DateTime
{
set
{
if (dateTime != value)
{
dateTime = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DateTime"));
}
}
}
get
{
return dateTime;
}
}
}
You can read the document to learn more about MVVM with data-binding.
I am trying to create an application on the basis of the WAF framework following the MVVM pattern. Currently, my solution consists of two projects (each equipped with MEF and MAF references):
*.Application (holding controllers and viewmodels)
*.Presentation (holding the actual view files)
I am creating the binding between view and viewmodel via the ViewModel interface - see code fragments below. Further, all classes are made available via the MEF framework inside the App.xaml.cs file. Here, the controller is also initialized. In the easiest case, I want to show a string value in a label of the main window.
Here is the problem: If I start the application, the value of the second label only shows the fallback value, but the get method of the property is being called properly (checked via debugging mode). The binding between View and ViewModel seems to be correct - if I change the binding path in the xaml to a non existent property, I get an output that the property can not be found in the ViewModel. My impression is that there could be a problem with the events for view updating? Any suggestions on this strange behaviour?
Here is the expert of the ViewModel:
[Export]
public class MainWindowViewModel : ViewModel<IMainWindowView>
{
private string _labelContent;
public string LabelContent
{
get { return _labelContent; }
set { SetProperty(ref _labelContent, value); }
}
[ImportingConstructor]
public MainWindowViewModel(IMainWindowView view) : base(view)
{
}
}
Here is the exerpt of the controller:
[Export(typeof(IMainWindowController))]
public class MainWindowController : IMainWindowController
{
private MainWindowViewModel _mainWindowViewModel;
public MainWindowViewModel MainWindowViewModel
{
get { return _mainWindowViewModel; }
set { _mainWindowViewModel = value; }
}
[ImportingConstructor]
public MainWindowController(MainWindowViewModel mainWindowViewModel)
{
_mainWindowViewModel = mainWindowViewModel;
}
public void Initialize()
{
_mainWindowViewModel.LabelContent = "stfu";
}
}
The view interface:
public interface IMainWindowView : IView
{
}
And the view itself:
[Export(typeof(IMainWindowView))]
public partial class MainWindow : Window, IMainWindowView
{
public MainWindow()
{
InitializeComponent();
}
}
<Window x:Class="MyCompany.Product.Redesign.Presentation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Label Content="Test" />
<Label Name="MyLabel" Content="{Binding Path=LabelContent, FallbackValue=Fallback}" />
</StackPanel>
</Window>
Are you sure, that the view that is displayed really is the instance with the ViewModel-Instance you are setting the property on?
First, make sure that you don't have a view set as the Application's StartupUri-Property in the App.xaml. Then make sure, that you call View.Show() through your ViewModel. You are then certain that you really set the property on the instance that is being displayed:
App.xaml
<Application <!-- note: no StartupUri Property -->
x:Name="App" x:Class="YourProject.Presentation.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShutdownMode="OnMainWindowClose">
</Application>
MainViewController.cs (with method declaration in IMainViewController.cs)
public void Run()
{
_mainWindowViewModel.Show();
}
App.xaml.cs
_controller = mainExportProvider.GetExportedValue<IMainViewController>();
_controller.Initialize();
_controller.Run();
MainViewModel.cs (with method declaration in IMainViewModel.cs)
public void Show()
{
ViewCore.Show();
}
This should do the trick. Otherwise, you might be seeing a view instance that you don't have a reference to. Thus you are setting a property on a ViewModel whos view isn't being displayed.
I am trying to receive shared text in a windows 8 store application via the share contract. I can receive the text, but if the text makes its way into an observable collection I get a com exception. How do I receive shared text and pass it off correctly to a viewmodel in a portable class library?
Portable class library:
ViewModel.cs:
public class ViewModel
{
public ObservableCollection<string> Strings { get; private set; }
public ViewModel()
{
Strings = new ObservableCollection<string>
{
"one",
"two",
"three"
};
}
}
Locator.cs:
public class Locator
{
private static ViewModel vm;
public static ViewModel VM
{
get
{
if (vm == null)
{
vm = new ViewModel();
}
return vm;
}
}
}
Store App Project:
MainPage.xaml
<Page...>
<Page.Resources>
<vm:Locator x:Key="Locator"/>
</Page.Resources>
<Grid ... DataContext="{Binding VM, Source={StaticResource Locator}}">
<ListBox ItemsSource="{Binding Strings}" .../>
</Grid>
</Page>
App.xaml.cs (abbreviated):
protected override async void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
base.OnShareTargetActivated(args);
if (args.ShareOperation.Data.AvailableFormats.Contains("Text"))
{
var text = await args.ShareOperation.Data.GetTextAsync();
Locator.VM.Strings.Add(text);
}
}
The last line above ( Locator.VM.Strings.Add(text) ) throws the following exception:
An unhandled exception of type 'System.InvalidCastException' occurred
in mscorlib.dll
Additional information: Unable to cast COM object of type
'System.Collections.Specialized.NotifyCollectionChangedEventHandler'
to class type
'System.Collections.Specialized.NotifyCollectionChangedEventHandler'.
Instances of types that represent COM components cannot be cast to
types that do not represent COM components; however they can be cast
to interfaces as long as the underlying COM component supports
QueryInterface calls for the IID of the interface.
I am writing an application, WPF, using PRISM. I'm new so apologies if this question is poor form:
I have a module that up to now has a user control for displaying a list of inspections. My module has entities written and a DbContext class to access DB. My question is where should this get initialsed and passed into my ViewModel???????
Shell XAML
<Window x:Class="ChargeMgm.Desktop.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://www.codeplex.com/prism"
Title="EMS" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition MinHeight="100"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Calibri"
FontSize="16"
Foreground="SteelBlue"
Margin="5">Street Works Modules</TextBlock>
<Border BorderThickness="1" BorderBrush="SteelBlue" CornerRadius="3" Grid.Row="1" Margin="5">
<ItemsControl prism:RegionManager.RegionName="MainRegion"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"/>
</Border>
</Grid>
</Window>
Bootstrapper class
class Bootstrapper : UnityBootstrapper
{
protected override System.Windows.DependencyObject CreateShell()
{
return new Shell();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(DefectModule.DefectModule));
}
}
Module
public class DefectModule : IModule
{
private readonly IRegionManager regionManager;
private IUnityContainer container;
public DefectModule(IUnityContainer container, IRegionManager regionManager)
{
this.regionManager = regionManager;
this.container = container;
}
public void Initialize()
{
container.RegisterType<IDefectsView, DefectsView>();
container.RegisterType<IDefectsViewModel, DefectsViewModel>();
container.RegisterType<IDefectContext, DefectContext>();
var view = container.Resolve<IDefectsView>();
if(regionManager.Regions.ContainsRegionWithName("MainRegion"))
{
regionManager.Regions["MainRegion"].Add(view);
//regionManager.RegisterViewWithRegion("MainRegion", typeof(IDefectsView));
}
}
}
If you're using Unity then you're in luck. If you need it initialise you DB context then you can do something like this:
IModule implementation code (for your module)
// Create Module http://msdn.microsoft.com/en-us/library/ff648781.aspx
public class Module:IModule
{
private IUnityContainer _container;
public Module(IUnityContainer container,IRegionManager regionManager)
{
_regionManager=regionManager;
_container=container;
}
public Initialize()
{
_container.RegisterType<IView,View>();
_container.RegisterType<IViewModel,ViewModel>();
_container.RegisterType<IDBContext,DbContext>();
var view=_container.Resolve<IView>();
//Create Region http://msdn.microsoft.com/en-us/library/ff648829.aspx
_regionManager.Regions["MainRegion"].Add(view);
}
}
The above will register all of your view, viewmodel and dbcontext, resolve them and add them into a region. For the above to work I'm expecting the following:
public class View:IView
{
public View(IViewModel viewModel)
{
}
}
public class ViewModel:IViewModel
{
public ViewModel(IDbContext context)
{
}
}
Basically, I'm expecting your viewmodel to be injected into your View and your DB Context to be injected into your ViewModel using Constructor Injection.
BTW - the links in the code go to MS sites that will provide more background on Module creation and Regions. I've got one final link: This is a "Hello World" Prism app. It's for Silverlight but this is basically the same thing as a WPF app in terms of code structure so should be useful:Prism Hello World