How do you access the MainViewModel in ViewModelLocator from code behind? - windows-phone-7

Building a WP7 app using MVVM light for my view models. I'm using the ViewModelLocator that gets added when you add the library through NuGet. Works great but now I need to get access to a ViewModel from code.
In my code the user clicks a button and I need to search the MainViewModel (which contains several view models) and find one based on the criteria the user entered.
Normally I would just response to the Click event of the button but I don't have an instance variable of the ViewModelLocator class to get a hold of the MainViewModel to perform the search. With the default template (non-MVVMLight) for Windows Phone 7, the App class has a static variable to the main view model so you can access it anytime with App.ViewModel.
There's some talk from twitter about using commands which would be good, but at some point I have to perform a code search across multiple vms to get the results I need. Probably need to inject a ISearchViewModel service into the View or something to make this work.
Here's the implementation of ViewModelLocator that is provided:
public class ViewModelLocator
{
private static MainViewModel _main;
public ViewModelLocator()
{
_main = new MainViewModel();
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return _main;
}
}
}
So from the code behind of another view, how do you get access to Main (MainViewModel contains all the lists of data and has a search method I call)? Or Should you?
Just wondering how people are solving this type of problem?
Thanks.

In MVVM-Light the ViewModelLocator is provided as an application resource. Therefore you can still directly access it, but the syntax is different. If you look at your App.xaml you should see this piece of code somewhere.
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>
From anywhere in your application you can access the App's resources and therefore also the MainViewModel with this piece of code:
(App.Current.Resources["Locator"] as ViewModelLocator).Main
This works for any application resource.

If you created the ViewModelLocator as in the template you have static references to the ViewModels. The mvvmlocatorproperty-snippet creates ViewModel-properties like this.
This means that you could just instantiate a new ViewModelLocator to locate the ViewModels in your code behind button click. It will always be the same viewmodels independent of the different instances of the ViewModelLocator

To access the MainViewModel from your code you can add this property to your class:
public ViewModel.MainViewModel myContext { get { return (DataContext as ViewModel.MainViewModel); } }
Then you can just use myContext.[whatever]

You can just use ViewModelLocator.MainViewModelStatic. Default template for MVVMLight have a static property for each your viewmodel.

Related

How can I add to my page layout with code in the ViewModel?

My page layout looks like this:
<StackLayout x:Name="detailsLayout" VerticalOptions="FillAndExpand">
</StackLayout>
When I create my ViewModel I pass in a pointer to the page. What I would like to do is this:
public class CardsViewModel : BaseViewModel
{
private readonly Cards cardsPage;
public CardsViewModel(Cards cardsPage)
{
this.cardsPage = cardsPage;
}
async public Task CardBtn()
{
cardsPage.detailsLayout.Children.Clear();
But I don't have access to the detailsLayout due to its protection level.
Can someone tell me how I can get access to do this?
The example above is an example of coupling and breaking the separation between the ViewModel and the View(Page). If you want to benefit from MVVM pattern - don't keep a reference to your UI layer and definitely don't try to manipulate the UI layer directly in the ViewModel.
What you should do instead is depends on your needs, however I can give you few ideas:
Encapsulate the logic within the UI control it self, exposing a delegate, command or property you could bind to from your ViewModel to trigger the specific event.
Keep the logic on UI layer, the Page itself and use a MessagingCenter to communicate to it from the ViewModel.
Good luck.
P.S.: You can find more information and examples about MessagingCenter in the official Xamarin documentation. Just don't forget to unsubscribe, otherwise you will find yourself in a bad place.

SubreportProcessing event handler cannot access my viewmodel due to being on a different thread

I have a WPF application that is utilizing the reporting tools included with Visual Studio 2010. I've had some other problems that I've solved by creating a graph of objects that are all marked as serializable, etc., as mentioned on various other web pages.
The ReportViewer control is contained in a WindowsFormsHost. I'm handling the SubreportProcessing event of the ReportViewer.LocalReport object to provide the data for the sub report.
The object graph that I'm reporting on is generated in my viewmodel, and that viewmodel holds a reference to it. The SubreportProcessing handler is in my code behind of my window (may not be the best place - but I simply want to get the ReportViewer working at this point).
Here's the problem: In my event handler, I'm attempting to get a reference to my viewmodel using the following code:
var vm = DataContext as FailedAssemblyReportViewModel;
When the handler is called, this line throws an InvalidOperationException with the message The calling thread cannot access this object because a different thread owns it.
I didn't realize the handler might be called on a different thread. How can I resolve this?
I attempted some searches, but all I've come up with is in regards to updating the UI from another thread using the Dispatcher, but that won't work in this case...
I solved this problem using something I believe is a hack, by adding the following function:
public object GetDataContext() {
return DataContext;
}
And then replacing the line of code from my question with:
object dc = Dispatcher.Invoke(new Func<object>(GetDataContext), null);
var vm = dc as FailedAssemblyReportViewModel;
However, this seems like a hack, and I might be circumventing some sort of safety check the CLR is doing. Please let me know if this is an incorrect way to accomplish this.
That's a nasty problem you have there.
Why don't you use in the view a content presenter which you bind to a windows form host?
And in the view model you would have a property of type of type WindowsFormsHost. Also,in the view model's constructor you could set the windows form's host Child property with the report viewer.
After that is smooth sailing, you could use your report viewer anywhere in your code. Something like this:
View:
<ContentPresenter Content="{Binding Path=FormHost}"/>
ViewModel:
private ReportViewer report = new ReportViewer();
private WindowsFormsHost host = new WindowsFormsHost();
public WindowsFormsHost FormHost
{
get {return this.host;}
set
{
if(this.host!=value)
{
this.host = value;
OnPropertyChanged("FormHost");
}
}
}
public ViewModel() //constructor
{
this.host.Child = this.report;
}
After that happy coding. Hope it helps.

How to alter a DataTemplate based on a property on the page ViewModel in Windows Phone 7?

I have a ViewModel which contains a Boolean property which tells you if the user has authenticated or not.
I have a WrapPanel which is bound to a collection of profiles. The DataTemplate for these profiles has an icon - a closed padlock for when the user is not authenticated and an open one for when the user is authenticated. Ideally these would be bound to the Boolean on the ViewModel but the DataContext for the templates is the individual profile objects.
I have tried,
Setting the Source selector in the binding as specified here although it appear Windows Phone 7 does not support x:Reference
I tried also the Inversion of Control(?) method detailed here (but containerLocator was not found on my object)
I tried applying a Style.Trigger but these are not supported in Windows Phone 7
I also tried accessing the XAML elements in the code behind and updating programmatically on event triggers, however I could not get a handle on the Image element inside the DataTemplate
Edit after comment: WP7 does not support style triggers. But if anyone is looking for this answer on following versions I let the reply below:
I would use a Style Trigger as seen here to update the icon Source property on the fly - as part of the style of your DataTemplate so you would get a hold of your Image.
One way I found that works based on an answer by Damian Antonowicz but does not implement the full inversion of control method that he uses, is as follows,
Create a partial class which resolves to your view-model instance under your view-model namespace, e.g.
public partial class ViewModelInstanceLocator
{
public AppViewModel AppViewModel // Or whatever the type of your view-model is ...
{
get
{
return App.VM; // Or wherever your view model instance is ...
}
}
}
Define the other half of the class in your XAML page as a resource so that it can be referred to as a static resource, I did this in my App.xaml so that it could be referred to everywhere,
<ResourceDictionary>
<viewmodel:ViewModelInstanceLocator x:Key="ViewModelInstanceLocator" />
...
</ResourceDictionary>
You may need to include the relevant namespace if there is not already a reference to your view-model namespace e.g. at the top,
xmlns:viewmodel="clr-namespace:MyAppNamespace.ViewModel"
Finally to bind to the view-model as follows,
{Binding AppViewModel.SomeProperty, Source={StaticResource ViewModelInstanceLocator}}
The binding updates as usual just as if the view-model instance had been referred to through the DataContext. However, it does not work with design-time data.

WP7 Application is being deactivated with no reason 10 seconds after navigating to a page

A WP 7.1 project starts with a Page that host a Panorama control. At some point user click on a ListBox, and this navigates the application to a details page.
In case the debugger is attached, everything stays on the screen as it should. But If I test an application either in emulator, or on the phone without a debugger, approximately in 5-10 seconds after the details page navigation, an application gets deactivated.
No unhanded exception, not closing, but deactivated even is raised. I have placed a message boxes in each of "exit handlers" to know exactly what happens and found out that it is deactivation.
No user input takes place after navigation and before the deactivation.
What may be the reason for such "no interaction" deactivation?
I don't call no "deactivate" requests from code.
Additional info:
Details page is bound to a sample view model that is obtained via MVVM Light ViewModel locator. View model locator gets it from ninject kernel that is a static public property of an App object(Yes, I have made IOC container publicly available via App property. I know it probably is a horrible practice, but I doubt the problem is linked to that). The page initializes just fine and displays all the data from a sample view model class. It almost seems like an app is deactivated due to inactivity, but there is no such thing in WP7 as far as I know.
UPDATE
A deactivation takes place exactly 10 seconds afer I call this line:
((PhoneApplicationFrame)(Application.Current.RootVisual)).Navigate(new Uri("/Views/BookDetailsView.xaml", UriKind.Relative));
from a view model of a main application view. The problem view is a details view, not the main one.
The constructor for BookDetailsView is empty (default):
public partial class BookDetailsView : UserControl
{
public BookDetailsView()
{
InitializeComponent();
}
}
The XAML for the view binds it's datacontext to a property of a mvvm light view model locator:
DataContext="{Binding Source={StaticResource Locator}, Path=BookDetails}"
The Locator resource is decleared in App.xaml and points to ViewModelLocator.cs.
The property that provides datacontext for a problem view is:
public static IBookDetailsViewModel BookDetailsStatic
{
get;
set;
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public IBookDetailsViewModel BookDetails
{
get
{
return BookDetailsStatic;
}
}
The BookDetailsStatic is initialized from the IoC container call in the ViewModelLocator ctor:
BookDetailsStatic = App.Kernel.Get<IBookDetailsViewModel>();
In a any scenario the call to get an IBookDetailsViewModel returns an instance of a
public class SampleBookDetailsViewModel: IBookDetailsViewModel
which has an empty constructor and a bunch of properties.
SOLVED
My view, that I have been navigating to, was declared as a UserControl, and should have been as PhoneApplicationPage.
Hard to say without knowing what's on the page, but you could be hitting the memory limit.
In general, you can consider the memory limit to be 90mb, but you're better off checking DeviceStatus.ApplicationMemoryUsageLimit and DeviceStatus.ApplicationCurrentMemoryUsage and possibly displaying it on screen every half second or so to debug.
You can also try the profiler, assuming it doesn't affect the repro.
BookDetailsView was decleared as a UserControl.
Navigating to a UserControl deactivates an application in 10 seconds.
Changing the type of a view to PhoneApplicationPage solves the problem.

Prism / MEF new view not getting a new viewmodel from MEF import

I have a tabbed application where I want the user to be able to search for a person and then, in a new view, show the person's details. The user should be able to have multiple person detail views open for different people.
I was a little unsure if I am following the correct procedure for creating my new view. Using Unity (which I am not) it seems you would call Container.Resolve(view) however I am doing the following, the satisfyImports was necessary in order for my imports in the view / viewmodel to be created.
PersonDetailView view = new PersonDetailView();
_container.SatisfyImportsOnce(view);
_regionManager.Regions["MainRegion"].Add(view, this.SelectedPerson.Name);
_regionManager.RequestNavigate("MainRegion", new Uri("PersonDetailView", UriKind.Relative));
In the code for my PersonDetailView I have the following property to set the data context.
[Import]
public PersonDetailsViewModel ViewModel
{
set
{
this.DataContext = value;
}
}
This seems to work but the trouble I am having is that when I create a second person view, the new view is getting the same instance of the datacontext as the view that is already created.
Is this because I am creating my views incorrectly or is there a way that I tell MEF to create a new oject when it fulfills the imports for my new view?
When you export a part, by default it used a CreationPolicy of Shared. This essentially makes the exported instance a singleton in the container. With your export, add another attribute:
[Export, PartCreationPolicy(CreationPolicy.NonShared)]
public class Foo { }
This will ensure a new instance is created each time you call to compose the consumer instance.

Resources