I'm trying to make a simple Windows Phone 7 Silverlight app. There's the MainPage.xaml with some UI elements, and a separate C# class MyClass.cs with some code.
My problem is: MyClass can't access anything contained in MainPage (i.e. it doesn't know the UI elements or C# methods exist).
If I try to inherit MainPage, the app compiles, but refuses to run:
public class MyClass : MainPage
{
// No good
}
If I try this solution, then I get an InvalidCastException error:
public class MyClass
{
// Also no good
MainPage m = (MainPage)Application.Current.RootVisual;
}
My question is: how can I access MainPage from a separate class?
In MainPage.xaml.cs, I can simply use myElement.Property etc. However, this isn't possible in MyClass, but I'm not sure why.
I guess there's a simple answer that I'm missing, but I'm really not sure what it is (... C# newbie trying to run before he can walk!?).
Thanks in advance for any advice you can offer.
The answer is that you shouldn't be doing this as this level of coupling is likley to cause issues in the future in terms of reuse and particularly testing.
You could look at having a separate global view model which has the information (properties) you need and is bound to the view model of the main view.
If you need your class to call methods on the view then you could look at a messaging system or similar.
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've started a project using MVVM Light and have run into an issue where once a window is created a ViewModel is bound to it, however, if I close this window and reopen the same window another viewmodel is made.
Through the debugger I can see the code looping through properties and methods after interacting with forms. I can see many instances of the same collections/properties/methods being fired. This then creates errors of 'Out of Bounds" after deleting items, etc.
*Note: Using ViewModelLocator, bound within XAML and completely removed from the XAML.cs files. ViewModels not referenced anywhere else.
I've attempted the following. No Help.
(WPF/MVVM) Single Instance In MainViewModel
How should I handle this to eliminate multiple ViewModels and looping properties/methods. Methods/properties should only be looped once.
EDIT
I've solved my issue. By referencing a static class within windows resources I was creating a new instance per ListView. Thus forcing the ViewModel to loop to conditions to meet those instances each form that consumed an instance.
By eliminating the resource and moving all data to MVVM Light DataService and using Task from System.Threading.Tasks, I am able to bind to a collection within the ViewModel rather than a independent instance. No more looping. Thanks for the answers.
It's common to use viewmodel first and a single window application rather than multiple windows with their own viewmodels.
Partly since it's quite easy for users to "lose" multiple windows. It also closes off a number of sharing issue edge cases where you have window X open and when you open window Y the processing clashes.
With what you have now, one simple way round this is to use SimpleIOC to provide your viewmodels.
SimpleIOC gives you singletons for anything you ask for.
You may have seen code does:
SimpleIoc.Default.GetInstance<vmType>();
Which of course has a definite type inside those angle brackets.
An alternative is:
SimpleIoc.Default.GetInstance(vmType);
Where vmType can be a variable. A Type variable which matches the tupe of the viewmodel you want.
You could make a markup extension which takes a type as parameter and makes that call, returning the viewmodel.
I've not tried it, but I don't think you even need to register the type using that syntax.
You can always use Singleton Design Pattern
public sealed class Vm
{
//Private Constructor.
private Vm()
{
}
private static Vm instance = null;
public static Vm Instance
{
get
{
if (instance == null)
{
instance = new Vm();
}
return instance;
}
}
}
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.
Is there an shorter way to access public methods from other windows phone pages (e.g. from the MainPage.xaml) then this?
((MainPage)(((System.Windows.Controls.ContentControl)(App.RootFrame)).Content)).getMyPublicMethod()
If you are implementing it the way you are, you have a very bad separation of concerns. Normally, the view (aka the Page in this case) should not carry any functional components in terms of triggering actions that might be needed in another view.
A much better solution is to use the 'light' MVVM approach, where you have a core view model and then simply call actions from it, passing the necessary data from view to view. I've described a basic implementation here.
As far as I can see that's the only way. But you can create a wrapper to shorten syntax to access that page. For example, method wrapper :
public MainPage getMainPage()
{
return (MainPage)(((System.Windows.Controls.ContentControl)(App.RootFrame)).Content);
}
//usage :
getMainPage().getMyPublicMethod();
or property wrapper :
public MainPage OtherWindow
{
return (MainPage)(((System.Windows.Controls.ContentControl)(App.RootFrame)).Content);
}
//usage :
OtherWindow.getMyPublicMethod();
In my WPF Application, I specify an Application.Resource, as shown here:
<Application x:Class="MyApp" [...]>
<Application.Resources>
<local:MyData x:Key="MyData">
</Application.Resources>
</Application>
I have a class MyData, which currently does all of its initialization (including loading data from a file) in its constructor. I consider this bad form, as I'm coming from a C++ background, where constructors should be kept minimal.
I suspect I want the MyData class to listen for an event, something like "AfterResourcesLoaded", but I haven't been able to find any description of this in searching MSDN.
Am I right that doing too much work in the constructor is bad form in C#, as it is in C++? Or is it acceptable to do significant work in the constructor?
How do I register an event-handler in MyData in the XAML, so that I can do the necessary work when the event is triggered, after the object is constructed?
(or perhaps I'm on the wrong track entirely? Alternative ideas gratefully considered.)
Doing too much work in the constructor of a class is also bad idea in C# as well as in C++ (mainly because it is not obvious for the users of the class).
What you can do is to have the data load triggered when it is accessed for the first time (e.g. in a property getter of the MyData class) or by providing an explicit method (e.g. LoadData) that can be called in the Application.Startup event handler. Something like this:
public class MyApp : Application
{
public override OnStartup(EventArgs e)
{
base.OnStartup(e);
MyData data = FindResource("MyData");
data.LoadData();
}
}
Assuming MyData is basically a simple class that just holds data, I'd move the responsibility of loading MyData out of itself and into another class. The other class could hydrate MyData in your application or Window's Loaded event.