Create View from ViewModel object - wpf

I see that MvvmCross touch supports creating a View from a ViewModel object using a MvxViewModelRequest.
But in MvvmCross WPF, I can only create Views from a MvxViewModelRequest using
Mvx.Resolve<IMvxSimpleWpfViewLoader>().CreateView(viewmodelRequest)
However, I cannot find a way to create a View from a ViewModel object? Is this support in MvvmCross for WPF?

This functionality isn't included by default in Wpf - but you could easily add it.
The logic would be similar to the request-based code in https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Wpf/Views/MvxWpfViewsContainer.cs - something like:
// Use `IMvxViewFinder` to find the type of view:
var viewType = Mvx.Resolve<IMvxViewFinder>().GetViewType(myViewModel.GetType());
// create a view and set the data context
var viewObject = Activator.CreateInstance(viewType);
if (viewObject == null)
throw new MvxException("View not loaded for " + viewType);
var wpfView = viewObject as IMvxWpfView;
if (wpfView == null)
throw new MvxException("Loaded View does not have IMvxWpfView interface " + viewType);
wpfView.ViewModel = myViewModel;
You could build this into a custom views container or a custom view presenter if you wanted to.

Assume u have
public partial class LoginViewController : MvxViewController<LoginViewModel>
than, if i want to use view somewhere u can do something like
this.presentedCurrentController = Activator.CreateInstance(typeof(LoginViewController)) as LoginViewController;
(this.presentedCurrentController as LoginViewController).ViewModel = new LoginViewModel();
where
this.presentedCurrentController it's
var NSViewController presentedCurrentController;
Thanks too #cheesebaron for link and another one

Related

Generic EventAggregator?

We want to fire Events, with event names saved in SQL Server
In the SQL Server you'll find ApplicationExitRequestEvent
When we click the menu button, we'll get the string from the MenuItem
Type t = Type.GetType(SelectedMenu.View + "," + "AssemblyName");
var obj = Activator.CreateInstance(t);
if (t != null)
{
//Working firing Event with className
EventAggregator.GetEvent<ApplicationExitRequestEvent>().Publish(null);
//Generic?
EventAggregator.GetEvent<???>().Publish(null);
}
Ist it possible to do?
Working with PRISM and MVVM - WPF - .NET 4.0
if you look at the EventAggregator class, you'll see it's nothing more than a container Dictionary<Type, EventBase> and the GetEvent method. That's it, all the actual work is done in EventBase. In order to achieve what you want, you could modify the class (or make a copy and modify that), and add a method GetEvent( string typeString ) in which you convert the typeString to an actual Type (same way as in your code sample) and use that to fetch the event from the dictionary.
Got it, working fine now!
Pimped the Prism Library, to get the event by Type :-)
/// <summary>
/// Gets the single instance of the event managed by this EventAggregator.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public CompositePresentationEvent<object> GetEvent(Type type)
{
EventBase existingEvent = null;
events.TryGetValue(type, out existingEvent);
if(existingEvent != null)
return (CompositePresentationEvent<object>)existingEvent;
return null;
}
Thank you guys!

How to detect the sender(button) of dynamical created bindings

I create some RibbonButtons dynamically and add them to a group according to an xml file. The follwoing function is carried out as often as entries found in the xml file.
private void ExtAppsWalk(ExternalAppsXml p, AppsWalkEventArgs args)
{
RibbonButton rBtn = new RibbonButton();
rBtn.Name = args.Name;
Binding cmdBinding = new Binding("ExtAppCommand");
rBtn.SetBinding(RibbonButton.CommandProperty, cmdBinding);
Binding tagBinding = new Binding("UrlTag");
tagBinding.Mode = BindingMode.OneWayToSource;
rBtn.SetBinding(RibbonButton.TagProperty, tagBinding);
rBtn.Label = args.Haed;
rBtn.Tag = args.Url;
rBtn.Margin = new Thickness(15, 0, 0, 0);
MyHost.ribGrpExtern.Items.Add(rBtn);
}
I tried to use the Tag property to store the Url's to be started when the respective button is clicked. Unfortunately the binding to the Tag property gives me the last inserted Url only.
What would be the best way to figure out which button is hit or to update the Tag property.
The datacontext is by default the context of the Viewmodel. The RibbonGroup to which the Buttons are added is created in the xaml file at designtime. I use that construct:
MyHost.ribGrpExtern.Items.Add(rBtn);
to add the buttons. It maight not really be conform with the mvvm pattern. May be someone else has a better idea to carry that out.
I foud a solution for my problem here and use the RelayCommand class. So I can pass objects (my Url) to the CommandHandler.
RibbonButton rBtn = new RibbonButton();
rBtn.Name = args.Name;
Binding cmdBinding = new Binding("ExtAppCommand");
rBtn.SetBinding(RibbonButton.CommandProperty, cmdBinding);
rBtn.CommandParameter = (object)args.Url;
private void ExtAppFuncExecute(object parameter)
{
if (parameter.ToString().....//myUrl

Can I implement my own view resolution service and have RequestNavigate use it?

I 'm fairly new to Prism and I 'm currently re-writing one of our existing applications using Prism as a proof of concept project.
The application uses MVVM with a ViewModel first approach: our ViewModel is resolved by the container, and an IViewResolver service figures out what view it should be wired up to (using name conventions amongst other things).
The code (to add a view to a tab control) at the moment looks something like this:
var vm = (get ViewModel from somewhere)
IRegion reg = _regionManager.Regions["MainRegion"];
var vw = _viewResolver.FromViewModel(vm); // Spins up a view and sets its DataContext
reg.Add(vw);
reg.Activate(vw);
This all works fine, however I 'd really like to use the Prism navigation framework to do all this stuff for me so that I can do something like this:
_regionManager.RequestNavigate(
"MainRegion",
new Uri("NameOfMyViewModel", UriKind.Relative)
);
and have Prism spin up the ViewModel + View, set up the DataContext and insert the view into the region.
I 've had some success by creating DataTemplates referencing the ViewModel types, e.g.:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Module01">
<DataTemplate DataType="{x:Type local:TestViewModel}">
<local:TestView />
</DataTemplate>
</ResourceDictionary>
...and have the module add the relevant resource dictionary into the applications resources when the module is initialized, but that seems a bit rubbish.
Is there a way to effectively take over view creation from Prism, so that when RequestNavigate is called I can look at the supplied Uri and spin up the view / viewmodel based on that? There’s an overload of RegionManager.RegisterViewWithRegion that takes a delegate that allows you to supply a view yourself, and I guess I’m after something like that.
I think I might need to supply my own IRegionBehaviorFactory, but am unsure what's involved (or even if I am on the right path!).
Any help appreciated!
--
note: Originally posted over at the prism codeplex site
Sure you can do that. I 've found that Prism v4 is really extensible, if only you know where to plug in.
In this case, you want your own custom implementation of IRegionNavigationContentLoader.
Here's how to set things up in your bootstrapper (the example is from a subclass of UnityBootstrapper from one of my own projects):
protected override void ConfigureContainer()
{
// IMPORTANT: Due to the inner workings of UnityBootstrapper, accessing
// ServiceLocator.Current here will throw an exception!
// If you want access to IServiceLocator, resolve it from the container directly.
base.ConfigureContainer();
// Set up our own content loader, passing it a reference to the service locator
// (it will need this to resolve ViewModels from the container automatically)
this.Container.RegisterInstance<IRegionNavigationContentLoader>(
new ViewModelContentLoader(this.Container.Resolve<IServiceLocator>()));
}
The ViewModelContentLoader itself derives from RegionNavigationContentLoader to reuse code, and will look something like this:
public class ViewModelContentLoader : RegionNavigationContentLoader
{
private readonly IServiceLocator serviceLocator;
public ViewModelContentLoader(IServiceLocator serviceLocator)
: base(serviceLocator)
{
this.serviceLocator = serviceLocator;
}
// THIS IS CALLED WHEN A NEW VIEW NEEDS TO BE CREATED
// TO SATISFY A NAVIGATION REQUEST
protected override object CreateNewRegionItem(string candidateTargetContract)
{
// candidateTargetContract is e.g. "NameOfMyViewModel"
// Just a suggestion, plug in your own resolution code as you see fit
var viewModelType = this.GetTypeFromName(candidateTargetContract);
var viewModel = this.serviceLocator.GetInstance(viewModelType);
// get ref to viewResolver somehow -- perhaps from the container?
var view = _viewResolver.FromViewModel(vm);
return view;
}
// THIS IS CALLED TO DETERMINE IF THERE IS ANY EXISTING VIEW
// THAT CAN SATISFY A NAVIGATION REQUEST
protected override IEnumerable<object>
GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
{
if (region == null) {
throw new ArgumentNullException("region");
}
// Just a suggestion, plug in your own resolution code as you see fit
var viewModelType = this.GetTypeFromName(candidateNavigationContract);
return region.Views.Where(v =>
ViewHasDataContract((FrameworkElement)v, viewModelType) ||
string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
}
// USED IN MY IMPLEMENTATION OF GetCandidatesFromRegion
private static bool
ViewHasDataContract(FrameworkElement view, Type viewModelType)
{
var dataContextType = view.DataContext.GetType();
return viewModelType.IsInterface
? dataContextType.Implements(viewModelType)
: dataContextType == viewModelType
|| dataContextType.GetAncestors().Any(t => t == viewModelType);
}
// USED TO MAP STRINGS OF VIEWMODEL TYPE NAMES TO ACTUAL TYPES
private Type GetTypeFromName(string typeName)
{
// here you need to map the string type to a Type object, e.g.
// "NameOfMyViewModel" => typeof(NameOfMyViewModel)
return typeof(NameOfMyViewModel); // hardcoded for simplicity
}
}
To stop some confusion about "ViewModel first approach":
You use more a "controller approach", but no "ViewModel first approach". A "ViewModel first approach" is, when you inject your View in your ViewModel, but you wire up both, your ViewModel and View, through a third party component (a controller), what by the way is the (I dont want to say "best", but) most loosely coupled approach.
But to answer your Question:
A possible solution is to write an Extension for the Prism RegionManager that does exactly what you have described above:
public static class RegionManagerExtensions
{
public static void AddToRegion<TViewModel>(
this IRegionManager regionManager, string region)
{
var viewModel = ServiceLocator.Current.GetInstance<TViewModel>();
FrameworkElement view;
// Get View depending on your conventions
if (view == null) throw new NullReferenceException("View not found.");
view.DataContext = viewModel;
regionManager.AddToRegion(region, view);
regionManager.Regions[region].Activate(view);
}
}
then you can call this method like this:
regionManager.AddToRegion<IMyViewModel>("MyRegion");

Prism v4, MEF, WPF - Using proper Uri for module graphic

My solution's structure is:
CI.Frontier.Classic contains a MEF module. My application uses the RibbonWindow control, and the modules define what menu items should be created. I can successfully add a button to the ribbon control from the CI.Frontier.Classic module, however, I cannot figure out the proper Uri to ClassicFrontierToopTip.png
Heres the code in FrontierClassic.cs that creates the tab, button and attempting to set the ribbon ToolTipImage
public void CreateMenuItems()
{
TabData tabData = new TabData("Legacy");
GroupData groupData = new GroupData("Internal");
tabData.GroupDataCollection.Add(groupData);
ButtonData classicFrontierBtn = new ButtonData()
{
Label = "Classic Frontier",
ToolTipTitle = "Classic Frontier",
ToolTipDescription = "Open Classic Frontier",
ToolTipImage = new Uri("./Graphics/ClassicFrontierToolTip.png", UriKind.Relative)
};
classicFrontierBtn.Command.RegisterCommand(new DelegateCommand(LoadFrontierView));
groupData.ControlDataCollection.Add(classicFrontierBtn);
_ribbonService.AddTab(tabData);
}
This Uri doesn't work as the tooltip does not display. Can I use the UriKind.Relative or should I be using some sort of "pack uri"?
The robust approach would be to leverage the pack syntax...
new Uri("pack://application:,,,/CI.Frontier.Classic;component/Graphics/ClassicFrontierToolTip.png", UriKind.Absolute);
Include an icon for a Prism module solved it...
ToolTipImage = new Uri("/" + GetType().Assembly.ToString().Split(',')[0] + ";component/Graphics/ClassicFrontierToolTip.png", UriKind.Relative)
Not sure if this is the best solution though.

ASP MVC to Silverlight MVVM issues

I am converting an asp.net MVC application to silverlight, and due to the fact I was doing some 'non-standard' things in my mvc app, I am having a hard time trying to work out how to implement it in Silverlight MVVM.
Basically I was generating all my views from metadata, including links, buttons etc. One example of this that I can't get my head around how to do in Silverlight is that I passed in an action collection to my view, and had a html helper class that then converted these actions into links
public static string GenericLinks(this HtmlHelper htmlHelper, int location, bool inTable, int? parentRecordId, List<ModelAction>
actions)
{
int actionNo = 1;
StringBuilder text = new StringBuilder();
foreach (var action in actions)
{
if (action.LocationType == location)
{
if (inTable)
text.Append("<td>");
else
if (actionNo > 1)
text.Append(" | ");
text.Append(htmlHelper.ActionLink(action.Label, action.ActionTypeLookup.CodeName, new { actionId = action.ModelActionId,
parentRecordId = parentRecordId }));
if (inTable)
text.Append("</td>");
actionNo++;
}
}
return text.ToString();
}
This really worked well in MVC.
What would the equivent be in MVVM?
I would expect I could do something much more eligent, more along the lines of creating my actions in my viewmodel, and somehow binding to those actions in my view...
For something like that you would probably need to create a custom control. Then you could put it in your view and bind it to the collection of Actions which would exist in your ViewModel.

Resources