I'm trying the MVVM pattern and I've run into a problem.
Here's how I instantiate my model:
<common:LayoutAwarePage
...
...(omitted boiler plate generated lines here)
...
...
mc:Ignorable="d">
<common:LayoutAwarePage.DataContext>
<local:TextGameClientModel x:Name="textGameClientModel"/>
</common:LayoutAwarePage.DataContext>
But when I try to use it, I get a NullReferenceException because this.textGameClientModel is NULL:
public MainPage()
{
this.InitializeComponent();
this.textGameClientModel.runsPublished += textGameClientModel_runsPublished;
}
I've also tried the same line in the Page's OnNavigateTo handler, and also in the OnLoaded handler, but with the same result.
Where is the right place to hook up my event handler?
(Please don't let my code-behind in an MVVM project distract you from the question. My use of a RichTextBox has forced me to color outside the lines a little.)
I actually wrote an answer about the WPF Creation Steps fairly recently, however that's not the problem in this case.
In this case, you are setting the DataContext in your XAML, however that's not the same as setting the textGameClientModel property
You need to do something like this to set the property equal to your DataContext first
this.textGameClientModel = this.DataContext as GameClientModel;
or simply cast your DataContext as your class to setup the event
((GameClientModel)this.DataContext).runsPublished += textGameClientModel_runsPublished;
As a side note, I never recommend hardcoding the DataContext into a UserControl like you have. By doing so, you are preventing any other DataContext from getting passed to the UserControl, which kind of defeats one of the biggest advantages of WPF/MVVM, which is having separate UI and data layers.
Related
I'm using the MVVM pattern with MVVM Light. I have a ViewModelLocator defined in App.xaml which holds all the ViewModels which are bound to from the Views using Blend.
I have an AppViewModel which basically contains all the important global info for my app, like user preferences. AppViewModel is instantiated by ViewModelLocator, which is in turn, instantiated by app.xaml.
The trouble is, I need to access AppViewModel from inside OnLaunched, however, at this stage of the application lifecycle, it appears app.xaml has not yet instantiated its declared resources. It appears to only do this after a Frame.Navigate().
I need to access AppViewModel because I have to inspect the properties on there in order to work out which Page to load. Hence it's a chicken-and-egg situation.
If I separately instantiate AppViewModel inside OnLaunched, then I can access AppViewModel, but then ViewModelLocator gets instantiated twice which causes problems with my IoC.
Is there a way of manually instantiating items declared inside app.xaml? Or is there a way to get app.xaml to load its resources before presenting a UI?
Or am I doing this wrong? Generally speaking, I regard the application to be the view model layer and the views essentially observe that... so hence I need the ViewModels available before I present any UI.
XAML:
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>
Code:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
var vml = App.Current.Resources["Locator"] as ViewModelLocator; // exception raised
Exception:
WinRT information: Cannot find a resource with the given key.
thanks
How about you instantiate the ViewModelLocator when you first need it and then add it to Application.Resources once Application.Resources is otherwise initialized or whenever you need? You don't need to add key value pairs to the ResourceDictionaries in XAML always.
I don't know C# events very well so I have difficulties in understanding some code I found in internet
Inside the code behind of a view file there is the following method:
public void SavingMesBoxClosedHandler(object sender, object args)
The DisplayMessageBox class is derived form FrameworkElement and defines the following event:
public delegate void MessageBoxClosedHandler(object sender, object args);
public event MessageBoxClosedHandler DialogClosed;
Inside the xaml of the view:
<DisplayMessageBoxDemo:DisplayMessageBox
// some dependency properties here
DialogClosed="SavingMesBoxClosedHandler"/>
I thought I could use only dependency properties, while DialogClosed is an event.
Which is the magic to map a method of the view to an event in DisplayMessageBox class using only its name ?
Why don't I have to use a binding ?
Is there an easy way to assign a viewmodel method as the event handler of DialogClose ?
May be I didn't use the correct terms in my last question. To put in other words I want to call a method inside my viewmodel, not in the view as in the example I reported above.
You don't have to use only dependency properties in XAML. You can use normal properties just as well. Dependency properties are a necessity when you use DataBinding, as you point out. In the case you mention there seems to be no need to use DataBinding because you will not use different handlers depending on your DataContext. If you want to stick to the MVVM pattern and keep the event handling logic in your view model, you can use EventTrigger: http://www.kunal-chowdhury.com/2010/11/using-eventtrigger-in-xaml-for-mvvm-no.html
I'm new to Silverlight/MVVM. I tried some example of MVVM Light, it looks great.
For my scenario, I want to create a Panorama Page, for each Panorama Item, showing my usercontrol, a item list for a customer.
I've built usercontrol(view), viewmodel and WCF service model and works well in a single Panorama Item(Only use first customer).
Also, I use Locator of MVVM Light shown in MIX10 demo, it enables me to make design time data for Expression Blend.
My viewmodel will receive a parameter of customer ID then exchange data with WCF based on this ID.
And the customer list also comes from WCF. So I can't actually makes viewmodels in Locator's static constructor.
If viewmodels are built in runtime by calling Locator, how to make data binding?
The only way I think about is to make viewmodel object in usercontrol's constructor and make it datacontext.
Is there a better solution?
If you want to keep the same declarative model in the XAML, you can put a CurrentCustomerViewModel property on the locator and then set property to the right viewmodel before you navigate to the page.
Personally though for pages like that I typically put a viewmodel factory method on the locator (so it can cache them, etc) and call it from the OnNavigatedTo method, something like this.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
string id = NavigationContext.QueryString["customerID"];
vm = ViewModelLocator.GetCustomerViewModel(id);
DataContext = vm;
base.OnNavigatedTo(e);
}
Then I just use Blend's sample data capabilities for design time data. This way also helps support pinning the page to the start screen since that will be the entry point to the app and I won't necessarily get a good chance to set the "CurrentCustomerVM" property anyway.
I have a functional MVVM patterned SL app with a RadWindow (essentially a ChildWindow), that I would like to remove the code-behind in my view that shows the window using its ShowDialog. Both the main view and the window is bound to the same ViewModel if that helps.
The button has both a ViewModel command using a MVVMLight RelayCommand to handle setting state as well as the event handler in the View.
The ultimate solution/pattern will be reused 20+ times so something better than code-behind would be great.
Thoughts?
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
var window = new RadWindowTest.Controls.ChildWindow1();
window.Closed += new System.EventHandler<Telerik.Windows.Controls.WindowClosedEventArgs>(window_Closed);
window.ShowDialog();
}
In this case I would suggest you implement DialogService. This service you can inject into your view model, for testing you will have to inject an instance that does not depend on the view, thus maintaining testability. You can use the ViewModelLocator to make this service available to your application.
Another approach would be to implement a ViewBase class that implements an interface that allows you to display the dialog. This interface is now used by the ViewModel to display a dialog. Again to test you need to provide a different implementation of the interface. A sample can be found here.
Both ideas revolve around the same concept of factoring out the function that shows the dialog. In order to strictly de-couple the View and the ViewModel you will have to think about how the ViewModel specifies which dialog has to be shoen, and how the dialog resut or dialog model is returned to the calling ViewModel.
Which approach is better is open to your judgement. Personally I prefer the first approach as I do not have to inherit the views from a common base class.
PS: Use a command to get to the point in your ViewModel where you want to show the dialog. Using a dialog service should need no code behind.
If I have a usercontrol (in Silverlight) that I've written, that uses XAML to define it's appearance, how can I make a customised version of it?
i.e. I have MyControl.xaml & MyControl.xaml.cs
What do I need to do if I want a "SpecialisedControl" child class? I assume I just make a new code file, then inherit from MyControl. But what if I want to change the appearance of the base class - then what do I do?
I wrote this thinking you were talking about WPF, rather than Silverlight, but there may be enough overlap for this to be helpful, so I'm posting it, anyway.
If by "change the appearance of the base class" you mean "provide a new template", then what you need is probably a CustomControl, not a UserControl.
The best way to accomplish this is to follow the example set by other Microsoft controls, such as Button or ListBox:
Create a class that derives directly from Control (or whatever is closest to your control).
If any properties will need to be exposed to the control (such as text on a button, for example), make sure that you properly define them as DependencyProperties.
As described here, create a ResourceDictionary called Themes/generic.xaml and add a style for your class that includes a template (don't give the style a key).
Use TemplateBindings for any properties of elements on your control that need to get values from your control.
If you'll need to attach any event handlers to elements in your template, give them a unique name. Microsoft uses the convention of prefixing these names with "PART_", and I think it's a good thing to do for the sake of consistency, but it's not strictly required.
Again, if you need to attach event handlers, overload OnApplyTemplate(). In this method, you should detach any old event handlers (we certainly don't want any memory leaks!), and look for elements that have the names your provided in your template--when you find them, attach event handlers, as necessary.
This is certainly much more work than simply deriving from UserControl, but if you want to be able to totally re-template controls, like you can with the built-in controls, this is the way to do it.
On the other hand, if all you want to do is to provide a certain amount of limited customization, such as changing the background, or associating a Command with some user action, then the best thing to do is to expose DependencyProperties, which can then be set in styles for your control, or on instances of your control, itself.
In the case you mentioned of wanting to customize the look in an inherited control, the process is pretty similar: just add a default style for the new control with a new template; if you need to add more event handlers, just be absolutely certain that you call base.OnApplyTemplate().
I dunno, I like doing things with just plain objects. Here's an article that describes an easy way to slip a XAML-designed control outside your inheritance hierarchy so that you can customize appearance and behavior using SimpleThingsLikeInheritance rather than MicrosoftStuffThatAlmostWorks
http://gen5.info/q/2009/02/10/subverting-xaml-how-to-inherit-from-silverlight-user-controls/
As Mihnea's link describes, the easiest solution is to simply add a namespace in your XAML:
C#
public class MyBase : UserControl
{
}
public class FirstUserControl : MyBase
{
...
}
XAML
<local:MyBase
x:Class="FirstUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="YourAssembly" ...>
<!-- Sticking with UserControl instead of local:MyBase makes this clearer -->
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
..
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
..Your XAML
</local:MyBase>
You can solve this by using a wrapper as described in the link above.
But you can also use the strategy pattern to solve this problem.
In this post I explain how you implement these two methods.
http://www.lab101.be/2008/07/silverlight-usercontrol-inheritance/