MVVM Light Messenger Action Executing Multiple Times - wpf

Thanks for the first response. I tried and it worked. I did not use attached behavior. I used EventTrigger.
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<interactivity:Interaction.Triggers>
<!-- In order to Call Cleanup in ViewModel to unregister Messenger. -->
<interactivity:EventTrigger EventName="Unloaded">
<interactivity:InvokeCommandAction Command="{Binding ViewUnloadCommand}" />
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
Then my view will call the command in ViewModel to unregister the Messenger when this view is unloaded.
Thanks again.
Thanks Laurent for your fantastic work on MVVM light.
I've been working on a WPF project using this framework. Then I encountered this issue. I tried to search it on Google, MSDN and StackOverFlow. I found this solution when Messgener is used between ViewModel and View. I would do something like this in CodeBehind file, to call Unregister in Unloaded event handler.
public FinishedTodoItemTreeViewUserControl()
{
InitializeComponent();
Messenger.Default.Register<DialogMessage>(this, FinishedTodoItemTreeViewModel.DeleteAllDoneItemsConfirmMessageToken, dialog =>
{
var confirmResult = MessageBox.Show(dialog.Content, dialog.Caption, dialog.Button, dialog.Icon);
dialog.ProcessCallback(confirmResult);
});
}
private void currentControl_Unloaded(object sender, RoutedEventArgs e)
{
Messenger.Default.Unregister(this);
}
But when I am doing this in ViewModel, when I should call Unregister or Cleanup? Because I still need to receive this message again when it happens. But I don't want to receive this message multiple times with just one shot.
Thanks in advance.
/// <summary>
/// Register to be observer of TodoItems change notification receiver.
/// </summary>
private void RegisterTodoItemsChangedNotification()
{
Messenger.Default.Register<UnfinishedTodoItemTreeViewModel>(this, UnfinishedTodoItemTreeViewModel.RelatedTodoItemsChangedMessageToken, itemTreeViewModel =>
{
if (itemTreeViewModel.ActionCategory == UnfinishedTodoItemTreeViewModel.TodoItemActionCategory.Done)
{
DecreaseRelatedFilterTodoItemCount(itemTreeViewModel.FilterType);
AllTodoItemCount -= 1;
}
else if (itemTreeViewModel.ActionCategory == UnfinishedTodoItemTreeViewModel.TodoItemActionCategory.Undone)
{
IncreaseRelatedFilterTodoItemCount(itemTreeViewModel.FilterType);
AllTodoItemCount += 1;
}
});
}

In the view model, you should unregister whenever it makes sense. I'm guessing you will want to do this when the control it is bound to is unloaded?
You can write an attached behavior for this- just be aware of the other reasons why unloaded may fire. See this answer for one example.

Related

WPF Is there a simple way to update GUI from main thread [duplicate]

I'm a web and backend programmer by nature. Normally I try to avaoid making windows programs. Now I have to make a WPF client.
I have a background task that raises an event every often time. (It is working like a poller and when the criteria are met an event is raised). Noob as I am I wrote this code that was attached to the event to update the UI.
private void IsDisconnectedEvent()
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
}
This gives an exception because I am not on the same thread. After some googling I found that I should change the code with:
private void IsDisconnectedEvent()
{
Dispatcher.Invoke(() =>
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
});
}
This works, but this is not the only event and thus makes my code horrible ugly. Are there better ways to do this?
Regarding this:
This works, but this is not the only event and thus makes my code
horrible ugly
Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.
Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.
This means that there should be nothing like this:
UserWindow.Visibility = Visibility.Hidden;
anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.
Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>
Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.
Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.
When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.
Therefore our first step is to have all our ViewModels inherit from a class like this:
(taken from this answer):
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
//Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:
public class UserViewModel: PropertyChangedBase
{
private bool _showUserWindow;
public bool ShowUserWindow
{
get {return _showUserWindow; }
set
{
_showUserWindow = value;
OnPropertyChanged("ShowUserWindow"); //This is important!!!
}
}
}
Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:
public UserWindow() //Window's Constructor
{
InitializeComponent(); //this is required.
DataContext = new UserViewModel(); //here we set the DataContext
}
As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.
This same concept applies to EVERYTHING in WPF.
One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.
You can read more about that in the link and also the MSDN DataBinding page linked above.
Let me know if you need further details.

MVVM pattern violation: MediaElement.Play()

I understand that ViewModel shouldn't have any knowledge of View, but how can I call MediaElement.Play() method from ViewModel, other than having a reference to View (or directly to MediaElement) in ViewModel?
Other (linked) question: how can I manage View's controls visibility from ViewModel without violating MVVM pattern?
1) Do not call Play() from the view model. Raise an event in the view model instead (for instance PlayRequested) and listen to this event in the view:
view model:
public event EventHandler PlayRequested;
...
if (this.PlayRequested != null)
{
this.PlayRequested(this, EventArgs.Empty);
}
view:
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.PlayRequested += (sender, e) =>
{
this.myMediaElement.Play();
};
2) You can expose in the view model a public boolean property, and bind the Visibility property of your controls to this property. As Visibility is of type Visibility and not bool, you'll have to use a converter.
You can find a basic implementation of such a converter here.
This related question might help you too.
For all the late-comers,
There are many ways to achieve the same result and it really depends on how you would like to implement yours, as long as your code is not difficult to maintain, I do believe it's ok to break the MVVM pattern under certain cases.
But having said that, I also believe there is always way to do this within the pattern, and the following is one of them just in case if anyone would like to know what other alternatives are available.
The Tasks:
we don't want to have direct reference from the ViewModel to any UI elements, i.e. the the MediaElement and the View itself.
we want to use Command to do the magic here
The Solution:
In short, we are going to introduce an interface between the View and the ViewModel to break the dependecy, and the View will be implementing the interface and be responsible for the direct controlling of the MediaElement while leaving the ViewModel talking only to the interface, which can be swapped with other implementation for testing purposes if needed, and here comes the long version:
Introduce an interface called IMediaService as below:
public interface IMediaService
{
void Play();
void Pause();
void Stop();
void Rewind();
void FastForward();
}
Implement the IMediaService in the View:
public partial class DemoView : UserControl, IMediaService
{
public DemoView()
{
InitializeComponent();
}
void IMediaService.FastForward()
{
this.MediaPlayer.Position += TimeSpan.FromSeconds(10);
}
void IMediaService.Pause()
{
this.MediaPlayer.Pause();
}
void IMediaService.Play()
{
this.MediaPlayer.Play();
}
void IMediaService.Rewind()
{
this.MediaPlayer.Position -= TimeSpan.FromSeconds(10);
}
void IMediaService.Stop()
{
this.MediaPlayer.Stop();
}
}
we then do few things in the DemoView.XAML:
Give the MediaElement a name so the code behind can access it like above:
<MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/>
Give the view a name so we can pass it as a parameter, and
import the interactivity namespace for later use (some default namespaces are omitted for simplicity reason):
<UserControl x:Class="Test.DemoView"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
x:Name="MediaService">
Hookup the Loaded event through Trigger to pass the view itself to the view model through a Command
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction>
</ia:EventTrigger>
</ia:Interaction.Triggers>
last but not least, we need to hookup the media controls through Commands:
<Button Command="{Binding PlayCommand}" Content="Play"></Button>
<Button Command="{Binding PauseCommand}" Content="Pause"></Button>
<Button Command="{Binding StopCommand}" Content="Stop"></Button>
<Button Command="{Binding RewindCommand}" Content="Rewind"></Button>
<Button Command="{Binding FastForwardCommand}" Content="FastForward"></Button>
We now can catch everything in the ViewModel (I'm using prism's DelegateCommand here):
public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest
{
public IMediaService {get; private set;}
private DelegateCommand<IMediaService> loadedCommand;
public DelegateCommand<IMediaService> LoadedCommand
{
get
{
if (this.loadedCommand == null)
{
this.loadedCommand = new DelegateCommand<IMediaService>((mediaService) =>
{
this.MediaService = mediaService;
});
}
return loadedCommand;
}
}
private DelegateCommand playCommand;
public DelegateCommand PlayCommand
{
get
{
if (this.playCommand == null)
{
this.playCommand = new DelegateCommand(() =>
{
this.MediaService.Play();
});
}
return playCommand;
}
}
.
. // other commands are not listed, but you get the idea
.
}
Side note: I use Prism's Auto Wiring feature to link up the View and ViewModel. So at the View's code behind file there is no DataContext assignment code, and I prefer to keep it that way, and hence I chose to use purely Commands to achieve this result.
I use media element to play sounds in UI whenever an event occurs in the application. The view model handling this, was created with a Source property of type Uri (with notify property changed, but you already know you need that to notify UI).
All you have to do whenever source changes (and this is up to you), is to set the source property to null (this is why Source property should be Uri and not string, MediaElement will naturally throw exception, NotSupportedException I think), then set it to whatever URI you want.
Probably, the most important aspect of this tip is that you have to set MediaElement's property LoadedBehaviour to Play in XAML of your view. Hopefully no code behind is needed for what you want to achieve.
The trick is extremely simple so I won't post a complete example. The view model's play function should look like this:
private void PlaySomething(string fileUri)
{
if (string.IsNullOrWhiteSpace(fileUri))
return;
// HACK for MediaElement: to force it to play a new source, set source to null then put the real source URI.
this.Source = null;
this.Source = new Uri(fileUri);
}
Here is the Source property, nothing special about it:
#region Source property
/// <summary>
/// Stores Source value.
/// </summary>
private Uri _Source = null;
/// <summary>
/// Gets or sets file URI to play.
/// </summary>
public Uri Source
{
get { return this._Source; }
private set
{
if (this._Source != value)
{
this._Source = value;
this.RaisePropertyChanged("Source");
}
}
}
#endregion Source property
As for Visibility, and stuff like this, you can use converters (e.g. from bool to visibility, which you can find on CodePlex for WPF, SL, WP7,8) and bind your control's property to that of the view model's (e.g. IsVisible). This way, you control parts of you view's aspect. Or you can just have Visibility property typed System.Windows.Visibility on your view model (I don't see any pattern breach here). Really, it's not that uncommon.
Good luck,
Andrei
P.S. I have to mention that .NET 4.5 is the version where I tested this, but I think it should work on other versions as well.

Bound Button Not Enabling After Background Worker Process Completes

I have a background worker process that starts provisioning a new client for our system. Here is what the DoWork method looks like:
ProvisioningManager manager = new ProvisioningManager(false)
{
};
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.MaxSteps = manager.MaxProgress;
}));
manager.StatusUpdated += new ProvisioningManager.StatusUpdatedHandler(manager_StatusUpdated);
manager.TaskCompleted += new ProvisioningManager.TaskCompleteHandler(manager_TaskCompleted);
manager.ProvisionClient();
while (!manager.Completed)
{
System.Threading.Thread.Sleep(100 * 60);
}
Basically it creates the manager that handles talking to the different sub-systems which provision the client.
Now I have a status update event and completed event for the provisioning manager. When the TaskCompleted event fires I want to be able to set a property on my display object so that the finish button in the wizard is enabled:
void manager_TaskCompleted(object sender, ProvisioningManager.Task taskType)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
The XAML for the button looks like this:
<wizard:WizardPage Header="Provisioning Client..."
ShowBack="False"
AllowBack="False"
AllowFinish="{Binding Source={StaticResource ResourceKey=dataObject}, Path=ProvisioningComplete}"
Loaded="Provisioning_Loaded">
</wizard:WizardPage>
This isn't working. Even though I make sure to hit the dispatcher thread to set the property of the display object it doesn't actually change the button to enabled until I click on the window. Is this a bug in AvalonWizard or am I not on the correct thread to set an INotifyPropertyChanged? Is there a way to hack this; basically can I programmatically focus the window without the mouse click?
I tired placing that while loop in the DoWork method so that I could use the BackgroundWorker's completed method:
void provisioningWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
That doesn't work either. What gives?!
Update
Here is the requested static resource instantiation for the display object:
<Window.Resources>
<ObjectDataProvider x:Key="dataObject" ObjectType="{x:Type winDO:NewClientWizardDO}" />
</Window.Resources>
Update II
Here is the property and property change firer:
public bool ProvisioningComplete
{
get { return this._ProvisioningComplete; }
set
{
this._ProvisioningComplete = value;
this.NotifyPropertyChanged("ProvisioningComplete");
}
}
protected void NotifyPropertyChanged(params string[] propertyNames)
{
if (this.PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
sorry if I don't understand something, but is the ProvisioningComplete property marked as "volatile"? If not then this might be the problem.
So I couldn't find out exactly why I was having this issue. I tried setting focus to the window, the button, etc. I tried multiple ways of letting the view know the viewmodel had updated. Basically every suggestion I could find on the web didn't work. It almost seems like a bug.
A smarty on my team suggested faking a mouse click on the window. His idea was that since all it took to activate the button was a simple mouse click on the screen then faking one should have the same effect. I thought (and think) that this hack was ridiculous. I did try it out just to see if I could call it a "solution".
Well, it worked. We had this same problem in another one of our wizards (not AvalonWizard but a homegrown one). I think there has to be some underlying issue with the way the window redraws after a background thread updates objects that are bound to the UI.
Anyhow, the way I found to solve this issue is with the following hack-tastic code.
//import user32.dll and setup the use of the mouse_event method
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
/// <summary>
/// Watches for properties to change on the data object, mainly the ProvisioningComplete method
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DataObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ProvisioningComplete":
//if the provisioning is completed then we need to make the finish button selectable.
if (this.DataObject.ProvisioningComplete)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
//give the window focus
this.Focus();
//update the layout
WizardPageProvisioningClient.UpdateLayout();
//fake mouse click 50 pixels into the window
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)(this.Left + 50), (uint)(this.Top + 50), 0, 0);
}));
}
break;
}
}
I've tested this when the window is not the active window and when the user leaves the window as selected. The focus method seems to take care of this issue when the window isn't active. Our QA team hasn't run a complete test against the UI so I can't say if there is any situations where it doesn't work, but it seems to be the best solution that I've come up with to date.
I'm open to any other suggestions if anyone out there has a better idea of what could be causing the button to not update.

how to bind the click event of a button and the selecteditemchanged of a listbox to a viewmodel in mvvm in Silverlight

i'm just starting with the mvvm model in Silverlight.
In step 1 i got a listbox bound to my viewmodel, but now i want to propagate a click in a button and a selecteditemchanged of the listbox back to the viewmodel.
I guess i have to bind the click event of the button and the selecteditemchanged of the listbox to 2 methods in my viewmodel somehow?
For the selecteditemchanged of the listbox i think there must also be a 'return call' possible when the viewmodel tries to set the selecteditem to another value?
i come from a asp.net (mvc) background, but can't figure out how to do it in silverlight.
Roboblob provides excellent step-by-step solution for Silverlight 4. It strictly follows MVVM paradigm.
I would not bind or tie the VM in any way directly to the events of controls within the View. Instead, have a separate event that is raised by the View in response to the button click.
[disclaimer: this code is all done straight from my head, not copy & pasted from VS - treat it as an example!!]
So in pseudo code, the View will look like this:
private void MyView_Loaded(...)
{
MyButton.Click += new EventHandler(MyButton_Click);
}
private void MyButton_Click(...)
{
//Raise my event:
OnUserPressedGo();
}
private void OnUserPressedGo()
{
if (UserPressedTheGoButton != null)
this.UserPressedTheGoButton(this, EventArgs.Empty);
}
public EventHandler UserPressedTheGoButton;
and the VM would have a line like this:
MyView.UserPressedTheGoButton += new EventHandler(myHandler);
this may seem a little long-winded, why not do it a bit more directly? The main reason for this is you do not want to tie your VM too tightly (if at all) to the contents of the View, otherwise it becomes difficult to change the View. Having one UI agnostic event like this means the button can change at any time without affecting the VM - you could change it from a button to a hyperlink or that kool kat designer you hire may change it to something totally weird and funky, it doesn't matter.
Now, let's talk about the SelectedItemChanged event of the listbox. Chances are you want to intercept an event for this so that you can modify the data bound to another control in the View. If this is a correct assumption, then read on - if i'm wrong then stop reading and reuse the example from above :)
The odds are that you may be able to get away with not needing a handler for that event. If you bind the SelectedItem of the listbox to a property in the VM:
<ListBox ItemSource={Binding SomeList} SelectedItem={Binding MyListSelectedItem} />
and then in the MyListSelectedItem property of the VM:
public object MyListSelectedItem
{
get { return _myListSelectedItem; }
set
{
bool changed = _myListSelectedItem != value;
if (changed)
{
_myListSelectedItem = value;
OnPropertyChanged("MyListSelectedItem");
}
}
}
private void OnPropertyChanged(string propertyName)
{
if (this.NotifyPropertyChanged != null)
this.NotifyPropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
To get that NotifyPropertyChanged event, just implement the INotifyPropertyChanged interface on your VM (which you should have done already). That is the basic stuff out of the way... what you then follow this up with is a NotifyPropertyChanged event handler on the VM itself:
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "MyListSelectedItem":
//at this point i know the value of MyListSelectedItem has changed, so
//i can now retrieve its value and use it to modify a secondary
//piece of data:
MySecondaryList = AllAvailableItemsForSecondaryList.Select(p => p.Id == MyListSelectedItem.Id);
break;
}
}
All you need now is for MySecondaryList to also notify that its value has changed:
public List<someObject> MySecondaryList
{
get { return _mySecondaryList; }
set
{
bool changed = .......;
if (changed)
{
... etc ...
OnNotifyPropertyChanged("MySecondaryList");
}
}
}
and anything bound to it will automatically be updated. Once again, it may seem that this is the long way to do things, but it means you have avoided having any handlers for UI events from the View, you have kept the abstraction between the View and the ViewModel.
I hope this has made some sense to you. With my code, i try to have the ViewModel knowing absolutely zero about the View, and the View only knowing the bare minimum about the ViewModel (the View recieves the ViewModel as an interface, so it can only know what the interface has specified).
Regarding binding the button click event I can recommend Laurent Bugnion's MVVM Light Toolkit (http://www.galasoft.ch/mvvm/getstarted/) as a way of dealing with this, I'll provide a little example, but Laurent's documentation is most likely a better way of understanding his framework.
Reference a couple of assemblies in your xaml page
xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
add a blend behaviour to the button
<Button Content="Press Me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<command:EventToCommand Command="{Binding ViewModelEventName}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
and create the event within your viewmodel which will be called when the button is clicked
public RelayCommand ViewModelEventName { get; protected set; }
...
public PageViewModel()
{
ViewModelEventName = new RelayCommand(
() => DoWork()
);
}
This supports passing parameters, checking whether execution is allowed etc also.
Although I haven't used it myself, I think the Prism framework also allows you to do something similar.

Silverlight 3 Beta, NavigationService in the ViewModel

im developing a silverlight 3 beta navigation application, so i've gone with a slight variation of the MVVM pattern :) (all-in-one viewmodel), using prism, and stuff.
Question: How do i navigate to a different "NavigationPage" in the viewmodel
Now to cut a long story short, the viewmodel is declared as a page resource.
<navigation:Page.Resources>
<mvvm:LoginModel x:Key="DataSource" d:IsDataSource="True"></mvvm:LoginModel>
</navigation:Page.Resources>
And then a command is used to wireup everything with the viewmodel
<Button x:Name="LoginButton" Width="100" Margin="8" Content="Login"
prism:Click.Command="{Binding LoginCommand}"/>
Now if i try to navigate anywhere in the viewmodel like so
this.NavigationService.Navigate(new Uri("/Views/About.xaml", UriKind.Relative));
the Navigationservice is null, i've looked around and found this article, which describes using helix 0.3 for navigation, this was built back in the sl2 days, when the navigation controls never existed, now helix's model works well, and by implementing INavigationAware in the viewmodel, you are able to gain access to the NavigationContext, and then do whatever it is you require, i have tried helix, and it works.
SL3 comes with the builtin Navigation support, so to speak, which does exactly what helix does. So i dont wanna use a 3rd party framework, instead i prefer to use the built in sl3 features.
Is there anything in SL3 that emulates the helix's INavigationAware interface?
I personally think NavigationService is a UI-concept associated with the UI Frame or Page.
Another way to accomplish this without having to pass in a NavigationService into the view model is to have the ViewModel raise an event when navigation is supposed to occur... have the view handle the view model event and call Navigate in response.
A dodgy fix, but the only thing i've been able to use to get this working.
In the OnNavigatedTo event in the view, access the ViewModel and set the NavigationService to a property in the viewmodel so that it can be used later in the viewmodel
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModels.LoginViewModel viewmodel = (ViewModels.LoginViewModel)this.Resources["DataSource"];
//DataSource being the x:Name given to the viewmodel that is loaded as a page resource
viewmodel .service = NavigationService;
}
You may want to consider using the Messaging system if you are using MVVM light. Have a listener on your page hosting the frame that does the navigation and send the nav messages from your view models.
okay to help my question along, cos there still hasn't been any answer, i'm gonna throw more information at it.
This is the code in the viewmodel
public LoginModel()
{
LoginCommand = new DelegateCommand<object>(LoginCommandExecuted, a => { return _CanLoginCommandExecute; });
}
public ICommand LoginCommand { get; private set; }
private bool _CanLoginCommandExecute = true;
private void LoginCommandExecuted(object parameter)
{
_CanLoginCommandExecute = false;
AdminClient client = new AdminClient();
client.AuthorizeAsync();
client.AuthorizeCompleted +=
new EventHandler<AsyncCompletedEventArgs>(
(s, e) =>
{
if (e.Error != null)
{
MessageBox.Show("Login Failed");
}
else
{
this.NavigationService.Navigate(new Uri("/Views/About.xaml", UriKind.Relative));
}
_CanLoginCommandExecute = true;
}
);
}
NavigationService is null, therefore i cant move to the next view, help!!!
NavigationService.Navigate(new Uri("/About", UriKind.Relative));
Above should work.

Resources