I'm working on a legacy application that's written using Silverlight 5, The application contains lot's of anti-patterns and bad practices. I'm responsible for adding real-time interactions (such as notification) using SingalR.
By the way, They're using these WCF RIA Services for interacting with authentication.
They have a Main page, this page is the place where I'm getting user's notification and show them for logged in users:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
//...
}
}
So as you can see I didn't set DataContext property as long as user is logged in, I need to set MainPage's DataContext after a user logs in to application, So I have to do that in LoginOperation_Completed inside LoginForm page:
public partial class LoginForm : StackPanel
{
private LoginRegistrationWindow parentWindow;
private LoginInfo loginInfo = new LoginInfo();
public LoginForm()
{
InitializeComponent();
//...
}
private void LoginOperation_Completed(LoginOperation loginOperation)
{
if (loginOperation.LoginSuccess)
{
// Here I need to access MainPages's DataContext property and set it with my ViewModel
}
}
}
Now my question is that, how can I set MainPage's DataContext property inside another class (in this case LoginFrom)?
I have also tried to give an ID to my MainPage user control and access it like this:
mainPage.DataContext = new NotificationItemViewModel();
But the compiler gives me this error:
The name 'mainPage' does not exist in the current context
I finally figured out How to solve my question, There is a simple way to achieve this, I should have created the static instance of the MainPage class in the class itself:
public partial class MainPage : UserControl
{
public static MainPage Instance { get; private set; }
public MainPage()
{
InitializeComponent();
Instance = this;
}
}
Now I can access to the MainPage's DataContext this way:
MainPage.Instance.DataContext = new NotificationItemViewModel();
You need to name your MainPage UserControl where you pasted it in LoginForm XAML. Not in the definition of MainPage.
Related
I create a project with infragistic which generates the view and views models folders, what I want to do now is create a binding context to the view model as it is normally done, but this view model has INavigationService parameters and I don't know how to configure it. Those parameters, if someone helps me, I would really appreciate it, I attach images so that they understand me more.
enter image description here
enter image description here
In the mainPage background code you can use following code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext= new PersonsViewModel(Navigation);
}
}
This Navigation comes from NavigableElement you can use Navigation directly in ContentPage.
NavigableElement is under the Xamarin.Forms namespace like following code.
============Prism==============
If you use Prism. you should registe it the App.xaml.cs.
public partial class App
{
/*
* The Xamarin Forms XAML Previewer in Visual Studio uses System.Activator.CreateInstance.
* This imposes a limitation in which the App class must have a default constructor.
* App(IPlatformInitializer initializer = null) cannot be handled by the Activator.
*/
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
await NavigationService.NavigateAsync("NavigationPage/MainPage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();
}
}
In your MainPage.xaml.cs you do not need other binding code.
Here is a demo about it.
https://github.com/manacespereira/xamarin-prism-navigation
I'm in the process of learning WPF, so in my case I have an xbap application and the code below is the code behind MainView.xaml.cs. Also, it was initially of a Page Type and I just changed it to UserControl recently because I need to deploy this app using ClickOnce.
List of errors:
The name 'InitializeComponent' does not exist in the current context.
The name 'FrameViews' does not exist in the current context.
public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
this.DataContext = MainViewModel.UniqueInstance;
MainViewModel.UniqueInstance.FrameNavigationService = _FrameViews.NavigationService;
_FrameViews.Unloaded +=new RoutedEventHandler(_FrameViews_Unloaded);
}
private void _FrameViews_Unloaded(object sender, RoutedEventArgs e)
{
_FrameViews.Content = null;
}
}
}
The InitializeComponent() is only available in MainWindow not in MainView
you need to write like that:
public partial class MainWindow : Window
and the _FrameView doesent exist you need to declare it
I really made a search for this topic and did not find anything, and because of that, I am asking the question here.
I have a WPF application with Prism installed.
I have wired the view-model with the view automatically by name convention
<UserControl x:Class="Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
and the model in the 'Model' like this
public class ViewAViewModel {
public ViewAViewModel () {
// time-resource consuming operations
}
}
the automatic binding work perfectly without a problem and the view and its corresponding view-model is matching, but the problem here.
I have a lot of those views say (50) and for every one of them, the view-model will be created with constructor exhausting the processes. This will make the startup of the application longer and also it will create a lot of view-models objects and put them in the RAM without being sure that they will be used at all.
What I need is to create the view-model class when the view is activated (I mean when the view is navigated to). Is this possible and if yes how?
Update
here is how I register the view with the Module, this is causing all the views to be created when the startup of the module.
public class Module1 : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("region1", typeof(View1));
regionManager.RegisterViewWithRegion("region1", typeof(View2));
is there any way to delay the creating of the views, until the navigation request come?
You could use navigation, for each view.
Or you must create an interfaces for your view and view model.
An example:
public interface IMyView
{
IMyViewModel ViewModel { get; set; }
}
public interface IMyViewModel
{
}
In the module or app.cs, in the method RegisterTypes you should register these.
containerRegistry.Register<IMyView, MyView>();
containerRegistry.Register<IMyViewModel, MyViewModel>();
You must implement IMyView interface in your MyView.cs class.
public partial class MyView : UserControl, IMyView
{
public MyView(IMyViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
}
public IMyViewModel ViewModel
{
get => DataContext as IMyViewModel;
set => DataContext = value;
}
}
After you could use it:
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
var firstView = containerProvider.Resolve<IMyView>();
regionManager.AddToRegion(RegionNames.MainRegion, firstView);
}
In such case you shouldn't use ViewModelLocator.AutoWireViewModel in your view.
I am using SignalR to implement a notification system to exchange information between my application instances. I have the following hub class:
[HubName("OpenHub")]
public class OpenHub:Hub
{
public void DetermineLength(string message)
{
Clients.All.RecieveNewInfo(newMessage);
//How to use something like the following line?
//concerning that Form1 is loaded at application startup
//and I should not create a new instance
//Form1.lstMessages.Add(newMessage);
}
}
Yet, I have to update some UI controls including a label and a listbox to log whatever new information that has arrived. Besides defining my class in my form's code, how can I update Form object to show these new information when I have my hub defined in a different class?
You can use public static property in you Program class or you Form1 class to hold the reference for it.
For example in you Program before using Application.Run(new Form1());
You can do this:
public static Form1 MainForm { get; set; }
and then in the Main()
MainForm = new Form1();
Application.Run(MainForm);
From the hub you can access you form now:
[HubName("OpenHub")]
public class OpenHub:Hub
{
public void DetermineLength(string message)
{
Clients.All.RecieveNewInfo(newMessage);
Program.MainForm.lstMessages.Add(newMessage);
}
}
Of course, that lstMessages should be public. or better you expose in your form some public method to communicate with it.
If your app is winform you can register to the hub with something like this :
var Connection = new HubConnection("yourSignalRServerUrl");
var HubProxy = Connection.CreateHubProxy("OpenHub");
HubProxy.On<string>("RecieveNewInfo", (message) =>
this.Invoke((Action)(() =>
Form1.lstMessages.Add(message);
);
await Connection.Start();
The namespace needed to use HubProxy is Microsoft.AspNet.SignalR.Client
This example comes from : https://code.msdn.microsoft.com/windowsdesktop/Using-SignalR-in-WinForms-f1ec847b
[Export]
public sealed class MainViewModel : NotificationObject
{
[Import]
public ISomeService MyService { get; private set; }
...
}
In order to INJECT this class as the DataContext to my View, I have to mark it as Export so MEF creates an instance of it in the Catalog. The problem is that the main window needs to create other windows and pass in orders, I'm not sure how to go about that without breaking the MVVM approach.
I figure that an ICommand will trigger something on my MainViewModel to generate a new ViewModel, but then after that happens I can't really force a new Window (view) to open up from the ViewModel. Plus, I can't even really create a new ViewModel from my MainViewModel because then MEF won't really work, right?
[Export]
public sealed class MainViewModel : NotificationObject
{
[Import]
public ISomeService MyService { get; private set; }
private ObservableCollection<IOrderViewModel> Orders { get; set; }
public void OpenOrder(int id)
{
//Pseudo-code to ensure that duplicate orders are not opened)
//Else create/open the new order
var order = new OrderViewModel(id);
OpenOrders.Add(order);
}
}
2 problems here:
Since I "newed" the OrderViewModel services are not autoloaded via MEF.
How does this code on my ViewModel layer (appropriate layer) create the necessary view as a NEW WINDOW (child of the main window), and then link this new OrderViewModel as the DataContext?
The way to avoid 'new-ing' the OrderViewModel is to use a factory:
[Export]
public class OrderViewModelFactory
{
[Import]
public ISomeDependency ImportedDependency { get; set; }
public OrderViewModel Create(int id)
{
return new OrderViewModel(id, this.ImportedDependency);
}
}
Then import the factory into your MainViewModel as a dependency and MEF will take care of filling everything in as required.
To get around the problem of instantiating windows, we have created a DialogService that does something like:
[Export]
public class DialogService
{
public bool? ShowDialog(string regionName, object data = null)
{
var window = new Window();
var presenter = new ContentControl();
presenter.SetProperty(RegionManager.RegionName, regionName);
window.Content = presenter;
if (data != null) window.DataContext = data;
return window.ShowDialog();
}
}
One technique I use is what I call the Navigation service. Note this is different from WPF's built in navigation framework. Your viewmodel could have an instance injected directly or you can use the EventAggregator pattern to fire a request to navigate that then gets handled by the navigation service. Having the navigation service injected directly means that it can be injected with other objects like ViewModelFactories. Regardless how you do it, at some point you're going to have to have an object that knows how to create the viewmodel properly resolved by your container.